diff --git a/packages/capabilities/src/types.ts b/packages/capabilities/src/types.ts index 7eb8bf111..e921cff6b 100644 --- a/packages/capabilities/src/types.ts +++ b/packages/capabilities/src/types.ts @@ -633,9 +633,9 @@ export type PlanGetFailure = PlanNotFound // Top export type Top = InferInvokedCapability -export type Abilities = TupleToUnion +export type ServiceAbility = TupleToUnion -export type AbilitiesArray = [ +export type ServiceAbilityArray = [ Top['can'], ProviderAdd['can'], Space['can'], @@ -677,3 +677,13 @@ export type AbilitiesArray = [ Usage['can'], UsageReport['can'] ] + +/** + * @deprecated use ServiceAbility + */ +export type Abilities = ServiceAbility + +/** + * @deprecated use ServiceAbilityArray + */ +export type AbilitiesArray = ServiceAbilityArray diff --git a/packages/w3up-client/src/ability.js b/packages/w3up-client/src/ability.js new file mode 100644 index 000000000..062bd92a4 --- /dev/null +++ b/packages/w3up-client/src/ability.js @@ -0,0 +1,32 @@ +import { abilitiesAsStrings } from '@web3-storage/capabilities' + +const setOfAbilities = new Set(abilitiesAsStrings) + +/** + * Verify and return Abilities. + * + * Given a list of strings representing capability names (Abilities), + * verify that all the strings are valid Abilities and return Abilities[]. + * + * Abilities[] is still just a list of strings, but this helps us play + * nice with Typescript. + * + * @param {string[]} abilities + * @returns {import('@web3-storage/capabilities/types').ServiceAbility[]} + */ +export function asAbilities(abilities) { + for (const ability of abilities) { + if ( + !setOfAbilities.has( + /** @type {import('@web3-storage/capabilities/types').ServiceAbility} */ ( + ability + ) + ) + ) { + throw new Error(`${ability} is not a supported capability`) + } + } + return /** @type {import('@web3-storage/capabilities/types').ServiceAbility[]} */ ( + abilities + ) +} diff --git a/packages/w3up-client/src/index.js b/packages/w3up-client/src/index.js index ebfdecf31..5d38b5363 100644 --- a/packages/w3up-client/src/index.js +++ b/packages/w3up-client/src/index.js @@ -11,6 +11,7 @@ import { generate } from '@ucanto/principal/rsa' import { Client } from './client.js' export * as Result from './result.js' export * as Account from './account.js' +export * from './ability.js' /** * Create a new w3up client. diff --git a/packages/w3up-client/test/ability.test.js b/packages/w3up-client/test/ability.test.js new file mode 100644 index 000000000..87d807f2c --- /dev/null +++ b/packages/w3up-client/test/ability.test.js @@ -0,0 +1,21 @@ +import assert from 'assert' +import { asAbilities } from '../src/ability.js' + +describe('abilities', () => { + it('should return the passed argument if all abilities are valid', async () => { + const abilities = ['store/add', 'upload/add'] + assert.equal(asAbilities(abilities), abilities) + }) + + it('should throw an error if one of the abilities is not supported', async () => { + assert.throws( + () => { + asAbilities(['foo/bar']) + }, + { + name: 'Error', + message: 'foo/bar is not a supported capability', + } + ) + }) +})