Skip to content

Commit

Permalink
feat: alight link API with multiformats (#36)
Browse files Browse the repository at this point in the history
* feat: alight link API with multiformats

* fix: module regressions from merge
  • Loading branch information
Gozala authored Jun 15, 2022
1 parent 5f4fcde commit 0ec460e
Show file tree
Hide file tree
Showing 22 changed files with 163 additions and 184 deletions.
2 changes: 1 addition & 1 deletion packages/authority/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"dependencies": {
"@ucanto/interface": "^0.3.0",
"@noble/ed25519": "^1.6.0",
"@ipld/dag-ucan": "^1.6.0-beta",
"@ipld/dag-ucan": "^1.7.0-beta",
"multiformats": "^9.6.4"
},
"devDependencies": {
Expand Down
12 changes: 10 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@ipld/car": "^4.1.0",
"@ipld/dag-cbor": "^7.0.1",
"multiformats": "^9.6.4",
"@ipld/dag-ucan": "^1.6.0-beta",
"@ipld/dag-ucan": "^1.7.0-beta",
"@ucanto/interface": "^0.3.0"
},
"devDependencies": {
Expand All @@ -54,7 +54,7 @@
"typesVersions": {
"*": {
"*": [
"dist/*"
"dist/src/*"
],
"dist/src/lib.d.ts": [
"dist/src/lib.d.ts"
Expand All @@ -69,6 +69,14 @@
"./src/lib.js": {
"types": "./dist/src/lib.d.ts",
"import": "./src/lib.js"
},
"./link": {
"types": "./dist/src/link.d.ts",
"import": "./src/link.js"
},
"./delegation": {
"types": "./dist/src/delegation.d.ts",
"import": "./src/delegation.js"
}
},
"c8": {
Expand Down
28 changes: 17 additions & 11 deletions packages/core/src/delegation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import * as UCAN from "@ipld/dag-ucan"
import * as API from "@ucanto/interface"
import * as Link from "./link.js"

/**
* @deprecated
* Import `isLink` from module directly
*/
export const isLink =
/** @type {(value:API.Proof) => value is API.LinkedProof} */
(Link.isLink)

/**
*
* @param {API.Proof} proof
* @return {proof is API.Delegation}
*/
export const isDelegation = proof => !Link.isLink(proof)

/**
* Represents UCAN chain view over the set of DAG UCAN nodes. You can think of
Expand Down Expand Up @@ -123,15 +139,6 @@ const decode = ({ bytes }) => {
return data
}

/**
* Type predicate returns true if value is the link.
*
* @param {unknown} value
* @returns {value is UCAN.Proof}
*/
export const isLink = value =>
value != null && /** @type {{asCID: unknown}} */ (value).asCID === value

/**
* Creates a new signed token with a given `options.issuer`. If expiration is
* not set it defaults to 30 seconds from now. Returns UCAN in primary - IPLD
Expand All @@ -151,10 +158,9 @@ export const delegate = async (
const links = []
const blocks = new Map()
for (const proof of proofs) {
if (isLink(proof)) {
if (!isDelegation(proof)) {
links.push(proof)
} else {
proof
links.push(proof.cid)
for (const block of proof.export()) {
blocks.set(block.cid.toString(), block)
Expand Down
10 changes: 9 additions & 1 deletion packages/core/src/lib.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
export * as Delegation from "./delegation.js"
export { delegate, isLink } from "./delegation.js"
export { delegate, isDelegation } from "./delegation.js"
export {
create as createLink,
createV0 as createLegacyLink,
isLink,
asLink,
parse as parseLink,
decode as decodeLink,
} from "./link.js"
export * as UCAN from "@ipld/dag-ucan"
41 changes: 41 additions & 0 deletions packages/core/src/link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CID } from "multiformats"
import * as API from "@ucanto/interface"

/**
* @template {number} Code
* @template {number} Alg
* @param {Code} code
* @param {API.MultihashDigest<Alg>} digest
* @return {API.Link<unknown, Code, Alg, 1> & CID}
*/
export const create = (code, digest) =>
/** @type {any} */ (CID.createV1(code, digest))

/**
* @template {number} Alg
* @param {API.MultihashDigest<Alg>} digest
* @return {API.Link<unknown, 0x70, Alg, 0> & CID}
*/
export const createV0 = digest => /** @type {any} */ (CID.createV0(digest))

/**
* Type predicate returns true if value is the link.
*
* @template {API.Link<unknown, number, number, 0|1>} L
* @param {unknown} value
* @returns {value is (L & CID)}
*/
export const isLink = value =>
value != null && /** @type {{asCID: unknown}} */ (value).asCID === value

export const asLink =
/** @type {<L extends API.Link<unknown, number, number, 0|1>>(value:L|unknown) => (L & CID)|null} */
(CID.asCID)

export const parse =
/** @type {<P extends string>(source:string, base?:API.MultibaseDecoder<P>) => API.Link<unknown, number, number, 0|1> & CID} */
(CID.parse)

export const decode =
/** @type {(bytes:Uint8Array) => API.Link<unknown, number, number, 0|1> & CID} */
CID.decode
8 changes: 4 additions & 4 deletions packages/core/test/lib.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert, test } from "./test.js"
import { Delegation, UCAN } from "../src/lib.js"
import { Delegation, UCAN, isDelegation } from "../src/lib.js"
import { alice, bob, mallory, service } from "./fixtures.js"

test("create delegation", async () => {
Expand Down Expand Up @@ -271,7 +271,7 @@ test("create delegation chain", async () => {
"references proof via link"
)

if (Delegation.isLink(actual)) {
if (!isDelegation(actual)) {
return assert.fail("expect not to be a link")
}

Expand Down Expand Up @@ -310,7 +310,7 @@ test("create delegation chain", async () => {
bytes: delegation.bytes,
})

if (Delegation.isLink(actual)) {
if (!isDelegation(actual)) {
return assert.fail("expect not to be a link")
}

Expand Down Expand Up @@ -400,7 +400,7 @@ test("issue chained delegation", async () => {

const [delegation] = invocation.proofs || []

if (Delegation.isLink(delegation)) {
if (!isDelegation(delegation)) {
return assert.fail("must be a delegation")
}

Expand Down
32 changes: 32 additions & 0 deletions packages/core/test/link.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { test, assert } from "./test.js"
import { CID } from "multiformats"
import * as Link from "../src/link.js"

{
const dataset = [
[
"QmdpiaQ9q7n4E224syBJz4peZpAFLArwJgSXHZWH5F6DxB",
"bafybeihgb7yguzxak7m4yu6pmxn3fdarzqbkshw6ovq7bxzbpilu4wl63a",
"QmdpiaQ9q7n4E224syBJz4peZpAFLArwJgSXHZWH5F6DxB",
],
[
"bafybeiepa5hmd3vg2i2unyzrhnxnthwi2aksunykhmcaykbl2jx2u77cny",
"bafybeiepa5hmd3vg2i2unyzrhnxnthwi2aksunykhmcaykbl2jx2u77cny",
"QmXxyUQDxCpSuF8QrWHxsqmeCzMnHCukaBU3hnkgnYLYHj",
],
]

for (const [input, expect, expect2] of dataset) {
test(`Link.create - ${input}`, () => {
const cid = CID.parse(input)
const link = Link.create(cid.code, cid.multihash)
assert.deepEqual(link, CID.parse(expect))
})

test(`Link.createV0 - ${input}`, () => {
const cid = CID.parse(input)
const link = Link.createV0(cid.multihash)
assert.deepEqual(link.toString(), CID.parse(expect2).toString())
})
}
}
2 changes: 1 addition & 1 deletion packages/interface/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"typecheck": "tsc --build"
},
"dependencies": {
"@ipld/dag-ucan": "^1.6.0-beta",
"@ipld/dag-ucan": "^1.7.0-beta",
"multiformats": "^9.6.4"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions packages/interface/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
Identity,
Audience,
MultihashHasher,
MultihashDigest,
ByteView,
Ability,
Resource,
Expand All @@ -31,6 +32,8 @@ export type {
} from "multiformats/bases/interface"

export type {
MultihashDigest,
MultihashHasher,
Transport,
Encoded,
Link,
Expand Down
4 changes: 4 additions & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
".": {
"types": "./dist/src/lib.d.ts",
"import": "./src/lib.js"
},
"./server": {
"types": "./dist/src/server.d.ts",
"import": "./src/server.js"
}
},
"type": "module",
Expand Down
65 changes: 0 additions & 65 deletions packages/server/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,68 +28,3 @@ export interface ProviderContext<

context: API.InvocationContext
}

// export interface Provider<R extends Route<any, any, any, any> = never> {
// execute: R["execute"]

// or<K extends Route<any, any, any, any>>(other: Provider<K>): Provider<R & K>
// }

// interface Route<
// A extends API.Ability,
// C extends API.Caveats,
// O extends unknown,
// X extends { error: true }
// > {
// readonly can: A
// readonly capability: API.CapabilityParser<
// API.Match<API.ParsedCapability<A, API.InferCaveats<C>>>
// >
// readonly execute: (
// capability: API.ParsedCapability<A, API.InferCaveats<C>>,
// options: API.ValidationOptions<API.ParsedCapability<A, API.InferCaveats<C>>>
// ) => API.Await<API.Result<O, X | API.Unauthorized>>
// }

// export type InferParsedCapability<R extends Route> = R extends Route<
// infer A,
// infer C
// >
// ? API.ParsedCapability<A, InferCaveats<C>>
// : never

// export type InferInvocation<R extends Route> = R extends Route<infer A>
// ? API.Invocation<API.Capability<A>>
// : never

// export type InferReturn<R extends Route> = R extends Route<
// API.Ability,
// API.Caveats,
// infer O,
// infer X
// >
// ? API.Await<API.Result<O, X | InvocationError>>
// : never

// export interface Route<
// A extends API.Ability = API.Ability,
// C extends API.Caveats = API.Caveats,
// O extends unknown = unknown,
// X extends { error: true } = { error: true }
// > {
// can: A
// execute: (
// invocation: API.Invocation<API.Capability<A>>,
// options: API.ValidationOptions<API.ParsedCapability<A, InferCaveats<C>>>
// ) => API.Await<API.Result<O, X | InvocationError>>
// }

// // export type Provider<
// // I extends API.Capability = API.Capability,
// // O extends unknown = unknown,
// // X extends { error: true } = API.Failure
// // > = {
// // [K in I["can"]]: (
// // invocation: API.Invocation<I>
// // ) => API.Await<API.Result<O, X | API.Unauthorized>>
// // }
5 changes: 5 additions & 0 deletions packages/server/src/lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./server.js"
export * from "./handler.js"
export * from "./api.js"

export { InvocationError } from "./server.js"
1 change: 0 additions & 1 deletion packages/server/test/server.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import * as CBOR from "@ucanto/transport/cbor"
import { alice, bob, mallory, service as w3 } from "./fixtures.js"
import * as Service from "../../client/test/service.js"
import { test, assert } from "./test.js"
import { CID } from "multiformats"

const storeAdd = Server.capability({
can: "store/add",
Expand Down
21 changes: 7 additions & 14 deletions packages/transport/src/car/codec.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import * as API from "@ucanto/interface"
import * as CARWriter from "@ipld/car/buffer-writer"
import { CarReader } from "@ipld/car/reader"
import { CID } from "multiformats/cid"
import { base32 } from "multiformats/bases/base32"
import { UCAN } from "@ucanto/core"
import { UCAN, createLink } from "@ucanto/core"
import { sha256 } from "multiformats/hashes/sha2"

export { CID }

export const code = 0x0202

/**
* @typedef {{
* cid: API.Link
* bytes: Uint8Array
* }} Block
* @typedef {API.UCAN.Block<unknown, number, number, 0|1>} Block
* @typedef {{
* roots: Block[]
* blocks: Map<string, Block>
Expand Down Expand Up @@ -80,10 +74,6 @@ class Writer {

export const createWriter = () => new Writer()

/**
*/

/**
* @param {Partial<Model>} input
*/
Expand Down Expand Up @@ -120,11 +110,14 @@ export const decode = async bytes => {

/**
* @param {Partial<Model>} data
* @param {{hasher?: import('multiformats/hashes/interface').MultihashHasher }} [options]
* @param {{hasher?: API.MultihashHasher }} [options]
*/
export const write = async (data, { hasher = sha256 } = {}) => {
const bytes = encode(data)
const digest = await hasher.digest(bytes)
const cid = CID.createV1(code, digest)

const cid =
/** @type {UCAN.Link<Model, typeof code, number> & import('multiformats').CID}*/
(createLink(code, digest))
return { bytes, cid }
}
Loading

0 comments on commit 0ec460e

Please sign in to comment.