Skip to content

Commit

Permalink
feat!: doc capabilities & make requierd nb non-optionals (#159)
Browse files Browse the repository at this point in the history
- add jsdoc comments to all the capabilities and their fields
- make required nb fields non-optional
- update to latest ucanto
  • Loading branch information
Gozala committed Nov 15, 2022
1 parent 4e0700f commit f6b6d06
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 281 deletions.
14 changes: 7 additions & 7 deletions packages/access-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
"license": "(Apache-2.0 OR MIT)",
"dependencies": {
"@ipld/dag-ucan": "^2.0.1",
"@ucanto/client": "^3.0.1",
"@ucanto/core": "^3.0.1",
"@ucanto/interface": "^3.0.0",
"@ucanto/principal": "^3.0.0",
"@ucanto/server": "^3.0.1",
"@ucanto/transport": "^3.0.1",
"@ucanto/validator": "^3.0.1",
"@ucanto/client": "^3.0.2",
"@ucanto/core": "^3.0.2",
"@ucanto/interface": "^3.0.1",
"@ucanto/principal": "^3.0.1",
"@ucanto/server": "^3.0.4",
"@ucanto/transport": "^3.0.2",
"@ucanto/validator": "^3.0.4",
"@web3-storage/access": "workspace:^",
"@web3-storage/worker-utils": "0.4.3-dev",
"multiformats": "^10.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/access-api/test/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export async function send(ucan, mf) {

/**
* @param {Types.Signer} issuer
* @param {Types.Principal} service
* @param {Types.Principal<"key">} service
* @param {Types.ConnectionView<import('@web3-storage/access/types').Service>} conn
* @param {string} email
*/
Expand Down
14 changes: 7 additions & 7 deletions packages/access/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@
"@ipld/dag-ucan": "^2.0.1",
"@noble/ed25519": "^1.7.1",
"@types/ws": "^8.5.3",
"@ucanto/client": "^3.0.1",
"@ucanto/core": "^3.0.1",
"@ucanto/interface": "^3.0.0",
"@ucanto/principal": "^3.0.0",
"@ucanto/server": "^3.0.1",
"@ucanto/transport": "^3.0.1",
"@ucanto/validator": "^3.0.1",
"@ucanto/client": "^3.0.2",
"@ucanto/core": "^3.0.2",
"@ucanto/interface": "^3.0.1",
"@ucanto/principal": "^3.0.1",
"@ucanto/server": "^3.0.4",
"@ucanto/transport": "^3.0.2",
"@ucanto/validator": "^3.0.4",
"@web-std/fetch": "^4.1.0",
"bigint-mod-arith": "^3.1.2",
"conf": "^10.1.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/access/src/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export async function connection(principal, _fetch, url) {
* Agent
*/
export class Agent {
/** @type {Ucanto.Principal|undefined} */
/** @type {Ucanto.Principal<"key">|undefined} */
#service

/** @type {typeof fetch} */
Expand Down
80 changes: 73 additions & 7 deletions packages/access/src/capabilities/store.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
/* eslint-disable jsdoc/require-param */
import { capability, Failure, Link, URI, Schema } from '@ucanto/validator'
import { equalLink, equalWith } from './utils.js'
import { any } from './any.js'

/**
* all the `store/*` capabilities which can also be derived
* from any capability.
* Capability can only be delegated (but not invoked) allowing audience to
* derived any `store/` prefixed capability for the (memory) space identified
* by did:key in the `with` field.
*/
export const store = any.derive({
to: capability({
can: 'store/*',
/**
* did:key identifier of the (memory) space where CAR is intended to
* be stored.
*/
with: URI.match({ protocol: 'did:' }),
derives: equalWith,
}),
/**
* `store/*` can be derived from the `*` capability as long as `with` field
* is the same.
*/
derives: equalWith,
})

Expand All @@ -23,17 +33,42 @@ export const store = any.derive({
const base = any.or(store)

/**
* `store/add` can be derived from the `store/*` capability
* as long as with fields match.
* `store/add` capability allows agent to store a CAR file into a (memory) space
* identified by did:key in the `with` field. Agent must precompute CAR locally
* and provide it's CID and size using `nb.link` and `nb.size` fields, allowing
* a service to provision a write location for the agent to PUT or POST desired
* CAR into.
*/
export const add = base.derive({
to: capability({
can: 'store/add',
/**
* did:key identifier of the (memory) space where CAR is intended to
* be stored.
*/
with: URI.match({ protocol: 'did:' }),
nb: {
link: Link.optional(),
/**
* CID of the CAR file to be stored. Service will provision write target
* for this exact CAR file for agent to PUT or POST it. Attempt to write
* any other content will fail.
*/
link: Link,
/**
* Size of the CAR file to be stored. Service will provision write target
* for this exact size. Attempt to write a larger CAR file will fail.
*/
size: Schema.integer(),
/**
* Agent may optionally provide a link to a related CAR file using `origin`
* field. This is useful when storing large DAGs, agent could shard it
* across multiple CAR files and then link each shard with a previous one.
*
* Providing this relation tells service that given CAR is shard of the
* larger DAG as opposed to it being intentionally partial DAG. When DAG is
* not sharded, there will be only one `store/add` with `origin` left out.
*/
origin: Link.optional(),
size: Schema.integer().optional(),
},
derives: (claim, from) => {
const result = equalLink(claim, from)
Expand All @@ -50,24 +85,51 @@ export const add = base.derive({
}
},
}),
/**
* `store/add` can be derived from the `store/*` & `*` capability
* as long as the `with` fields match.
*/
derives: equalWith,
})

/**
* Capability can be used to remove the stored CAR file from the (memory)
* space identified by `with` field.
*/
export const remove = base.derive({
to: capability({
can: 'store/remove',
/**
* did:key identifier of the (memory) space where CAR is intended to
* be stored.
*/
with: URI.match({ protocol: 'did:' }),
nb: {
link: Link.optional(),
/**
* CID of the CAR file to be removed from the store.
*/
link: Link,
},
derives: equalLink,
}),
/**
* `store/remove` can be derived from the `store/*` & `*` capability
* as long as the `with` fields match.
*/
derives: equalWith,
})

/**
* Capability can be invoked to request a list of stored CAR files in the
* (memory) space identified by `with` field.
*/
export const list = base.derive({
to: capability({
can: 'store/list',
/**
* did:key identifier of the (memory) space where CAR is intended to
* be stored.
*/
with: URI.match({ protocol: 'did:' }),
derives: (claimed, delegated) => {
if (claimed.with !== delegated.with) {
Expand All @@ -78,6 +140,10 @@ export const list = base.derive({
return true
},
}),
/**
* `store/list` can be derived from the `store/*` & `*` capability
* as long as the `with` fields match.
*/
derives: equalWith,
})

Expand Down
79 changes: 71 additions & 8 deletions packages/access/src/capabilities/upload.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { capability, Link, URI } from '@ucanto/validator'
import { codec } from '@ucanto/transport/car'
import { codec as CAR } from '@ucanto/transport/car'
import { equalWith, fail, equal } from './utils.js'
import { any } from './any.js'

/**
* All the `upload/*` capabilities which can also be derived
* from `any` (a.k.a `*`) capability.
* Capability can only be delegated (but not invoked) allowing audience to
* derived any `upload/` prefixed capability for the (memory) space identified
* by did:key in the `with` field.
*/
export const upload = any.derive({
to: capability({
can: 'upload/*',
/**
* did:key identifier of the (memory) space where upload is add to the
* upload list.
*/
with: URI.match({ protocol: 'did:' }),
derives: equalWith,
}),
/**
* `upload/*` can be derived from the `*` capability as long as `with` field
* is the same.
*/
derives: equalWith,
})

Expand All @@ -23,18 +32,45 @@ export const upload = any.derive({
// `upload/*`.
const base = any.or(upload)

const CARLink = Link.match({ code: codec.code, version: 1 })
/**
* Schema representing a link (a.k.a CID) to a CAR file. Enforces CAR codec code and CID v1.
*/
const CARLink = Link.match({ code: CAR.code, version: 1 })

/**
* `upload/add` can be derived from the `upload/*` capability
* as long as with fields match.
* Capability allows an agent to add an arbitrary DAG (root) to the upload list
* of the specified (memory) space (identified by did:key in the `with` field).
* It is recommended to provide an optional list of shard links that contain
* fragments of this DAG, as it allows system to optimize block discovery, it is
* also a way to communicate DAG partiality - this upload contains partial DAG
* identified by the given `root`.
*
* Usually when agent wants to upload a DAG it will encode it as a one or more
* CAR files (shards) and invoke `store/add` capability for each one. Once all
* shards are stored it will invoke `upload/add` capability (providing link to
* a DAG root and all the shards) to add it the upload list.
*
* That said `upload/add` could be invoked without invoking `store/add`s e.g.
* because another (memory) space may already have those CARs.
*
* Note: If DAG with the given root is already in the upload list, invocation
* will simply update `shards` to be a union of existing and new shards.
*/
export const add = base.derive({
to: capability({
can: 'upload/add',
/**
* did:key identifier of the (memory) space where uploaded is added.
*/
with: URI.match({ protocol: 'did:' }),
nb: {
root: Link.optional(),
/**
* Root CID of the DAG to be added to the upload list.
*/
root: Link,
/**
* CIDs to the CAR files that contain blocks of the DAG.
*/
shards: CARLink.array().optional(),
},
derives: (self, from) => {
Expand All @@ -46,15 +82,30 @@ export const add = base.derive({
)
},
}),
/**
* `upload/add` can be derived from the `upload/*` & `*` capability
* as long as `with` fields match.
*/
derives: equalWith,
})

/**
* Capability removes an upload (identified by it's root CID) from the upload
* list. Please note that removing an upload does not delete corresponding shards
* from the store, however that could be done via `store/remove` invocations.
*/
export const remove = base.derive({
to: capability({
can: 'upload/remove',
/**
* did:key identifier of the (memory) space where uploaded is removed from.
*/
with: URI.match({ protocol: 'did:' }),
nb: {
root: Link.optional(),
/**
* Root CID of the DAG to be removed from the upload list.
*/
root: Link,
},
derives: (self, from) => {
return (
Expand All @@ -64,13 +115,25 @@ export const remove = base.derive({
)
},
}),
/**
* `upload/remove` can be derived from the `upload/*` & `*` capability
* as long as `with` fields match.
*/
derives: equalWith,
})

/**
* Capability can be invoked to request a list of uploads in the (memory) space
* identified by the `with` field.
*/
export const list = base.derive({
to: capability({
can: 'upload/list',
with: URI.match({ protocol: 'did:' }),
}),
/**
* `upload/list` can be derived from the `upload/*` & `*` capability
* as long as with fields match.
*/
derives: equalWith,
})
Loading

0 comments on commit f6b6d06

Please sign in to comment.