From ced0e0d2a5f546c82834672a89b4ef783a8df569 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Thu, 9 Feb 2023 16:20:07 +0000 Subject: [PATCH 01/54] feat: wip implement access/authorize in agent --- packages/access-client/src/agent-data.js | 14 +++++ packages/access-client/src/agent.js | 68 +++++++++++++++++++++--- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index c6ce8cb72..d5033d71b 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -1,7 +1,10 @@ import { Signer } from '@ucanto/principal' import { Signer as EdSigner } from '@ucanto/principal/ed25519' +import { DID } from '@ucanto/core' import { importDAG } from '@ucanto/core/delegation' import { CID } from 'multiformats' +import { Access } from '@web3-storage/capabilities' +import { isExpired } from './delegations.js' /** @typedef {import('./types').AgentDataModel} AgentDataModel */ @@ -143,4 +146,15 @@ export class AgentData { this.delegations.delete(cid.toString()) await this.#save(this.export()) } + + /** + * The current session proof. + */ + sessionProof() { + for (const { delegation } of this.delegations.values()) { + // @ts-expect-error "key" does not exist in object, unless it's a session capability + const cap = delegation.capabilities.find(c => c.can === Access.session.can && c.nb?.key === this.principal.did()) + if (cap && !isExpired(delegation)) return delegation + } + } } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index e6e939ca9..d8403da08 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -10,6 +10,7 @@ import { URI } from '@ucanto/validator' import { Peer } from './awake/peer.js' import * as Space from '@web3-storage/capabilities/space' import * as Voucher from '@web3-storage/capabilities/voucher' +import * as Access from '@web3-storage/capabilities/access' import { stringToDelegation } from './encoding.js' import { Websocket, AbortError } from './utils/ws.js' import { Signer } from '@ucanto/principal/ed25519' @@ -186,21 +187,35 @@ export class Agent { } /** - * Get all the proofs matching the capabilities + * Get all the proofs matching the capabilities. * - * Proofs are delegations with an audience matching agent DID. + * Proofs are delegations with an audience matching agent DID, or with an + * audience matching the session DID. + * + * Proof of session will also be included in the returned proofs if any + * proofs matching the passed capabilities require it. * * @param {import('@ucanto/interface').Capability[]} [caps] - Capabilities to filter by. Empty or undefined caps with return all the proofs. */ proofs(caps) { const arr = [] - - for (const value of this.#delegations(caps)) { - if (value.delegation.audience.did() === this.issuer.did()) { - arr.push(value.delegation) + const session = this.#data.sessionProof() + let hasSessionDelegations = false + + for (const { delegation } of this.#delegations(caps)) { + const aud = delegation.audience + if (aud.did() === this.issuer.did()) { + arr.push(delegation) + } else if (aud.did() === session?.audience.did()) { + arr.push(delegation) + hasSessionDelegations = true } } + if (session && hasSessionDelegations) { + arr.push(session) + } + return arr } @@ -228,7 +243,9 @@ export class Agent { const arr = [] for (const value of this.#delegations(caps)) { - if (value.delegation.audience.did() !== this.issuer.did()) { + const { delegation } = value + const isSession = delegation.capabilities.some(c => c.can === Access.session.can) + if (!isSession && delegation.audience.did() !== this.issuer.did()) { arr.push(value) } } @@ -404,6 +421,43 @@ export class Agent { } } + /** + * Request authorization of a session allowing this agent to issue UCANs + * signed by the passed email address. + * + * @param {string} email + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + */ + async authorize(email, opts) { + const parts = email.split('@').map(s => encodeURIComponent(s)) + const sessionPrincipal = DID.parse(`did:mailto:${parts[1]}:${parts[0]}`) + + const res = await Access.authorize.invoke({ + issuer: this.issuer, + audience: this.connection.id, + with: this.issuer.did(), + nb: { as: sessionPrincipal.did() } + }).execute(this.connection) + + if (res?.error) { + throw new Error('failed to authorize session', { cause: res }) + } + + const sessionDelegation = + /** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */ + (await this.#waitForDelegation(opts)) + + const cap = sessionDelegation.capabilities.find(c => c.can === Access.session.can && c.nb.key === this.issuer.did()) + if (!cap && isExpired(sessionDelegation)) { + throw new Error('received invalid delegation') + } + + await this.addProof(sessionDelegation) + + // TODO: claim delegations + } + /** * Invokes voucher/redeem for the free tier, wait on the websocket for the voucher/claim and invokes it * From 6b1d820214e4fc0120397d7381574c6c8932c4dd Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Wed, 8 Mar 2023 15:38:08 -0800 Subject: [PATCH 02/54] fix: updates for recent ucanto changes --- packages/access-client/src/agent-data.js | 6 ++++-- packages/access-client/src/agent.js | 23 +++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index d5033d71b..6aec44711 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -1,6 +1,5 @@ import { Signer } from '@ucanto/principal' import { Signer as EdSigner } from '@ucanto/principal/ed25519' -import { DID } from '@ucanto/core' import { importDAG } from '@ucanto/core/delegation' import { CID } from 'multiformats' import { Access } from '@web3-storage/capabilities' @@ -153,7 +152,10 @@ export class AgentData { sessionProof() { for (const { delegation } of this.delegations.values()) { // @ts-expect-error "key" does not exist in object, unless it's a session capability - const cap = delegation.capabilities.find(c => c.can === Access.session.can && c.nb?.key === this.principal.did()) + const cap = delegation.capabilities.find( + (c) => + c.can === Access.session.can && c.nb?.key === this.principal.did() + ) if (cap && !isExpired(delegation)) return delegation } } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index fafd5bcfd..b68a5a149 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -196,7 +196,7 @@ export class Agent { * * Proofs are delegations with an audience matching agent DID, or with an * audience matching the session DID. - * + * * Proof of session will also be included in the returned proofs if any * proofs matching the passed capabilities require it. * @@ -249,7 +249,9 @@ export class Agent { for (const value of this.#delegations(caps)) { const { delegation } = value - const isSession = delegation.capabilities.some(c => c.can === Access.session.can) + const isSession = delegation.capabilities.some( + (c) => c.can === Access.session.can + ) if (!isSession && delegation.audience.did() !== this.issuer.did()) { arr.push(value) } @@ -435,15 +437,17 @@ export class Agent { * @param {AbortSignal} [opts.signal] */ async authorize(email, opts) { - const parts = email.split('@').map(s => encodeURIComponent(s)) + const parts = email.split('@').map((s) => encodeURIComponent(s)) const sessionPrincipal = DID.parse(`did:mailto:${parts[1]}:${parts[0]}`) - const res = await Access.authorize.invoke({ - issuer: this.issuer, + const res = await this.invokeAndExecute(Access.authorize, { audience: this.connection.id, with: this.issuer.did(), - nb: { as: sessionPrincipal.did() } - }).execute(this.connection) + nb: { + iss: sessionPrincipal.did(), + att: [{ can: 'store/*' }, { can: 'provider/add' }], + }, + }) if (res?.error) { throw new Error('failed to authorize session', { cause: res }) @@ -453,7 +457,10 @@ export class Agent { /** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */ (await this.#waitForDelegation(opts)) - const cap = sessionDelegation.capabilities.find(c => c.can === Access.session.can && c.nb.key === this.issuer.did()) + // @ts-expect-error "key" does not exist in object, unless it's a session capability + const cap = sessionDelegation.capabilities.find( + (c) => c.can === Access.session.can && c.nb.key === this.issuer.did() + ) if (!cap && isExpired(sessionDelegation)) { throw new Error('received invalid delegation') } From daace2367866117b94ea64340e9ab195b19baf5c Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Thu, 9 Mar 2023 17:05:57 -0800 Subject: [PATCH 03/54] wip: break various auth steps out into separate functions I think we'll want to make higher level combinations of these at some point, but this translates the various operations from @bengo's observable (https://observablehq.com/d/95bfec64fbb3c2d1) into functions that clients can use --- packages/access-client/src/agent-data.js | 1 + packages/access-client/src/agent.js | 129 ++++++++++++++++++++++- 2 files changed, 125 insertions(+), 5 deletions(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 6aec44711..2a794a8d9 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -154,6 +154,7 @@ export class AgentData { // @ts-expect-error "key" does not exist in object, unless it's a session capability const cap = delegation.capabilities.find( (c) => + // @ts-expect-error "key" does not exist in object, unless it's a session capability c.can === Access.session.can && c.nb?.key === this.principal.did() ) if (cap && !isExpired(delegation)) return delegation diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index b68a5a149..2495ec381 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -6,12 +6,15 @@ import * as Ucanto from '@ucanto/interface' import * as CAR from '@ucanto/transport/car' import * as CBOR from '@ucanto/transport/cbor' import * as HTTP from '@ucanto/transport/http' +import * as ucanto from '@ucanto/core' import { URI } from '@ucanto/validator' import { Peer } from './awake/peer.js' import * as Space from '@web3-storage/capabilities/space' import * as Voucher from '@web3-storage/capabilities/voucher' import * as Access from '@web3-storage/capabilities/access' -import { stringToDelegation } from './encoding.js' +import * as Provider from '@web3-storage/capabilities/provider' + +import { stringToDelegation, bytesToDelegations } from './encoding.js' import { Websocket, AbortError } from './utils/ws.js' import { Signer } from '@ucanto/principal/ed25519' import { Verifier } from '@ucanto/principal' @@ -29,6 +32,55 @@ export { AgentData } const HOST = 'https://access.web3.storage' const PRINCIPAL = DID.parse('did:web:web3.storage') +/** + * + * @param {string} email + */ +function emailToSessionPrincipal(email) { + const parts = email.split('@').map((s) => encodeURIComponent(s)) + return DID.parse(`did:mailto:${parts[1]}:${parts[0]}`) +} +/** + * + * @param {Signer} space + * @param {Signer} account + * @returns + */ +async function createSpaceSaysAccountCanStoreAddWithSpace(space, account) { + return ucanto.delegate({ + issuer: space, + audience: account, + capabilities: [ + { + can: 'store/add', + with: space.did(), + }, + { + can: 'store/list', + with: space.did(), + }, + ], + }) +} + +/** + * + * @param {Signer.EdSigner} space + * @param {Signer} device + */ +async function createSpaceSaysDeviceCanAccessDelegateWithSpace(space, device) { + return ucanto.delegate({ + issuer: space, + audience: device, + capabilities: [ + { + can: 'access/delegate', + with: space.did(), + }, + ], + }) +} + /** * @typedef {import('./types').Service} Service */ @@ -437,8 +489,7 @@ export class Agent { * @param {AbortSignal} [opts.signal] */ async authorize(email, opts) { - const parts = email.split('@').map((s) => encodeURIComponent(s)) - const sessionPrincipal = DID.parse(`did:mailto:${parts[1]}:${parts[0]}`) + const sessionPrincipal = emailToSessionPrincipal(email) const res = await this.invokeAndExecute(Access.authorize, { audience: this.connection.id, @@ -457,8 +508,8 @@ export class Agent { /** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */ (await this.#waitForDelegation(opts)) - // @ts-expect-error "key" does not exist in object, unless it's a session capability const cap = sessionDelegation.capabilities.find( + // @ts-expect-error "key" does not exist in object, unless it's a session capability (c) => c.can === Access.session.can && c.nb.key === this.issuer.did() ) if (!cap && isExpired(sessionDelegation)) { @@ -466,8 +517,76 @@ export class Agent { } await this.addProof(sessionDelegation) + } + + async claimDelegations() { + const res = await this.invokeAndExecute(Access.claim, { + audience: this.connection.id, + with: this.issuer.did(), + }) - // TODO: claim delegations + if (res.error) { + throw new Error('error claiming delegations') + } + const delegations = Object.values(res.delegations).flatMap((bytes) => + bytesToDelegations(bytes) + ) + for (const delegation of delegations) { + this.addProof(delegation) + } + + // TODO: should we be inferring which spaces we have access to here and updating local space state? + + return delegations + } + + /** + * + * @param {string} email + * @param {Signer.EdSigner} space - TODO is this type correct? + */ + async addProvider(email, space) { + const sessionPrincipal = emailToSessionPrincipal(email) + await this.invokeAndExecute(Provider.add, { + audience: this.connection.id, + with: sessionPrincipal.did(), + // TODO: do we really want to send ALL delegations here or should we only select some subset? + proofs: this.#data.delegations, + nb: { + // TODO probably need to make it possible to pass other providers in + provider: 'did:web:web3.storage:providers:w3up-alpha', + consumer: space.did(), + }, + }) + } + + /** + * + * @param {string} email + * @param {Signer.EdSigner} space - TODO is this type correct? + */ + async delegateSpaceAccessToAccount(email, space) { + const sessionPrincipal = emailToSessionPrincipal(email) + const spaceSaysAccountCanStoreAddWithSpace = + await createSpaceSaysAccountCanStoreAddWithSpace(space, sessionPrincipal) + await this.invokeAndExecute(Access.delegate, { + audience: this.connection.id, + with: space.did(), + nb: { + delegations: { + [spaceSaysAccountCanStoreAddWithSpace.cid.toString()]: + spaceSaysAccountCanStoreAddWithSpace.cid, + }, + }, + proofs: [ + await createSpaceSaysDeviceCanAccessDelegateWithSpace( + space, + this.issuer + ), + // must be embedded here because it's referenced by cid in .nb.delegations + spaceSaysAccountCanStoreAddWithSpace, + ], + }) } /** From 941bb4d7d5e48e48fe2549987b5b69ae16a016f0 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:33:53 -0700 Subject: [PATCH 04/54] adjust access-client until lint passes --- packages/access-client/src/agent-data.js | 3 +- packages/access-client/src/agent.js | 37 ++++++++++++++++-------- packages/access-client/src/types.ts | 4 +-- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 2a794a8d9..9ba38155c 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -151,11 +151,10 @@ export class AgentData { */ sessionProof() { for (const { delegation } of this.delegations.values()) { - // @ts-expect-error "key" does not exist in object, unless it's a session capability const cap = delegation.capabilities.find( (c) => // @ts-expect-error "key" does not exist in object, unless it's a session capability - c.can === Access.session.can && c.nb?.key === this.principal.did() + c.can === Access.session.can && c?.nb?.key === this.principal.did() ) if (cap && !isExpired(delegation)) return delegation } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 2495ec381..568151b03 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -35,15 +35,16 @@ const PRINCIPAL = DID.parse('did:web:web3.storage') /** * * @param {string} email + * @returns {Ucanto.Principal>} */ function emailToSessionPrincipal(email) { const parts = email.split('@').map((s) => encodeURIComponent(s)) return DID.parse(`did:mailto:${parts[1]}:${parts[0]}`) } + /** - * - * @param {Signer} space - * @param {Signer} account + * @param {Ucanto.Signer>} space + * @param {Ucanto.Principal>} account * @returns */ async function createSpaceSaysAccountCanStoreAddWithSpace(space, account) { @@ -64,9 +65,8 @@ async function createSpaceSaysAccountCanStoreAddWithSpace(space, account) { } /** - * - * @param {Signer.EdSigner} space - * @param {Signer} device + * @param {Ucanto.Signer>} space + * @param {Ucanto.Principal>} device */ async function createSpaceSaysDeviceCanAccessDelegateWithSpace(space, device) { return ucanto.delegate({ @@ -520,11 +520,19 @@ export class Agent { } async claimDelegations() { - const res = await this.invokeAndExecute(Access.claim, { - audience: this.connection.id, - with: this.issuer.did(), - }) - + const [res] = await this.connection.execute( + Access.claim.invoke({ + issuer: this.issuer, + audience: this.connection.id, + with: this.issuer.did(), + proofs: this.proofs([ + { + can: 'access/claim', + with: this.issuer.did(), + }, + ]), + }) + ) if (res.error) { throw new Error('error claiming delegations') } @@ -551,7 +559,12 @@ export class Agent { audience: this.connection.id, with: sessionPrincipal.did(), // TODO: do we really want to send ALL delegations here or should we only select some subset? - proofs: this.#data.delegations, + proofs: this.proofs([ + { + can: 'provider/add', + with: sessionPrincipal.did(), + }, + ]), nb: { // TODO probably need to make it possible to pass other providers in provider: 'did:web:web3.storage:providers:w3up-alpha', diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index f02d85a4b..f27a81117 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -157,7 +157,7 @@ export type CIDString = string */ export interface AgentDataModel { meta: AgentMeta - principal: Signer + principal: Signer> currentSpace?: DID spaces: Map delegations: Map @@ -331,4 +331,4 @@ export type EncodedDelegation = string & Phantom export type BytesDelegation = - Uint8Array & Phantom + Uint8Array & Phantom> From 92f7caa17f9f9dce033a7d26f762fa369d3bed13 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:45:36 -0700 Subject: [PATCH 05/54] remove unnecessary '?' from agent data sessionProof check --- packages/access-client/src/agent-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 9ba38155c..3f6f2a350 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -154,7 +154,7 @@ export class AgentData { const cap = delegation.capabilities.find( (c) => // @ts-expect-error "key" does not exist in object, unless it's a session capability - c.can === Access.session.can && c?.nb?.key === this.principal.did() + c.can === Access.session.can && c.nb?.key === this.principal.did() ) if (cap && !isExpired(delegation)) return delegation } From 75e243fbd49e26671b3606ac148f09cb96ebc410 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Mon, 13 Mar 2023 13:19:27 -0700 Subject: [PATCH 06/54] claimDelegations uses invokeAndExecute --- packages/access-client/src/agent.js | 27 +++++++++------------------ packages/access-client/src/types.ts | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 568151b03..610c0dbe2 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -520,19 +520,10 @@ export class Agent { } async claimDelegations() { - const [res] = await this.connection.execute( - Access.claim.invoke({ - issuer: this.issuer, - audience: this.connection.id, - with: this.issuer.did(), - proofs: this.proofs([ - { - can: 'access/claim', - with: this.issuer.did(), - }, - ]), - }) - ) + const res = await this.invokeAndExecute(Access.claim, { + audience: this.connection.id, + with: this.issuer.did(), + }) if (res.error) { throw new Error('error claiming delegations') } @@ -768,12 +759,12 @@ export class Agent { * await recoverInvocation.execute(agent.connection) * ``` * + * @type {import('./types').InvokeAndExecute} * @template {Ucanto.Ability} A * @template {Ucanto.URI} R - * @template {Ucanto.TheCapabilityParser>} CAP - * @template {Ucanto.Caveats} [C={}] - * @param {CAP} cap - * @param {import('./types').InvokeOptions} options + * @template {Ucanto.Caveats} C + * @param {Ucanto.TheCapabilityParser>} cap + * @param {import('./types').InvokeOptions>>} options */ async invokeAndExecute(cap, options) { const inv = await this.invoke(cap, options) @@ -781,7 +772,7 @@ export class Agent { // @ts-ignore const out = inv.execute(this.connection) - return /** @type {Promise, import('./types').Service>>} */ ( + return /** @type {Promise>>, import('./types').Service>>} */ ( out ) } diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index f27a81117..648707603 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -22,7 +22,10 @@ import type { SignerArchive, SigAlg, Caveats, + TheCapabilityParser, + CapabilityMatch, } from '@ucanto/interface' +import * as Ucanto from '@ucanto/interface' import type { Abilities, @@ -332,3 +335,19 @@ export type EncodedDelegation = string & export type BytesDelegation = Uint8Array & Phantom> + +export type InvokeAndExecute = < + A extends Ability, + R extends URI, + C extends Ucanto.Caveats +>( + cap: TheCapabilityParser>, + options: InvokeOptions>> +) => Promise< + Ucanto.InferServiceInvocationReturn< + Ucanto.InferInvokedCapability< + Ucanto.TheCapabilityParser> + >, + import('./types').Service + > +> From 4ceb10b440d5401bf33e3ed014ad96dcb1f79291 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Mon, 13 Mar 2023 18:35:59 -0700 Subject: [PATCH 07/54] feat: add access-api + access-client agent test and improve types (#533) Motivation: * there weren't any tests that test access-api + access-client agent, so this adds a stub for one * it includes a lot of type adjustments that should make it easier to write more tests in followup * specifically it removed some old types I had tried to make when testing access delegate. I added a new `ServiceInvoke` type that is better than the ones I had made before. This is useful for making an 'invoke' function that will handle a subset of the caps available on `Service` but expect the same success/fail types as Service --- packages/access-api/src/types/ucanto.ts | 8 ++ .../test/access-client-agent.test.js | 31 ++++++ .../access-api/test/access-delegate.test.js | 101 +----------------- packages/access-api/test/helpers/types.ts | 4 +- .../test/helpers/ucanto-test-utils.js | 12 ++- packages/access-api/test/provider-add.test.js | 9 +- 6 files changed, 61 insertions(+), 104 deletions(-) create mode 100644 packages/access-api/src/types/ucanto.ts create mode 100644 packages/access-api/test/access-client-agent.test.js diff --git a/packages/access-api/src/types/ucanto.ts b/packages/access-api/src/types/ucanto.ts new file mode 100644 index 000000000..5bc5ea5d3 --- /dev/null +++ b/packages/access-api/src/types/ucanto.ts @@ -0,0 +1,8 @@ +import * as Ucanto from '@ucanto/interface' + +export type ServiceInvoke< + Service extends Record, + InvocationCapabilities extends Ucanto.Capability = Ucanto.Capability +> = ( + invocation: Ucanto.ServiceInvocation +) => Promise> diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js new file mode 100644 index 000000000..075fb7fd6 --- /dev/null +++ b/packages/access-api/test/access-client-agent.test.js @@ -0,0 +1,31 @@ +import { context } from './helpers/context.js' +import { createTesterFromContext } from './helpers/ucanto-test-utils.js' +import * as principal from '@ucanto/principal' +import { Agent as AccessAgent } from '@web3-storage/access/agent' +import * as assert from 'assert' + +for (const accessApiVariant of /** @type {const} */ ([ + { + name: 'using access-api in miniflare', + ...(() => { + const spaceWithStorageProvider = principal.ed25519.generate() + return { + spaceWithStorageProvider, + ...createTesterFromContext(context, { + registerSpaces: [spaceWithStorageProvider], + }), + } + })(), + }, +])) { + describe(`access-client-agent ${accessApiVariant.name}`, () => { + it('can be used with access-client agent', async () => { + const accessAgent = await AccessAgent.create(undefined, { + connection: await accessApiVariant.connection, + }) + const space = await accessAgent.createSpace('test-add') + const delegations = accessAgent.proofs() + assert.equal(space.proof.cid, delegations[0].cid) + }) + }) +} diff --git a/packages/access-api/test/access-delegate.test.js b/packages/access-api/test/access-delegate.test.js index 2dc1d66d9..27fab5e9c 100644 --- a/packages/access-api/test/access-delegate.test.js +++ b/packages/access-api/test/access-delegate.test.js @@ -6,13 +6,10 @@ import * as Ucanto from '@ucanto/interface' import * as ucanto from '@ucanto/core' import * as principal from '@ucanto/principal' import { createAccessDelegateHandler } from '../src/service/access-delegate.js' -import { createAccessClaimHandler } from '../src/service/access-claim.js' import { createDelegationsStorage, toDelegationsDict, } from '../src/service/delegations.js' -import { createD1Database } from '../src/utils/d1.js' -import { DbDelegationsStorage } from '../src/models/delegations.js' import * as delegationsResponse from '../src/utils/delegations-response.js' import { assertNotError, @@ -122,70 +119,6 @@ async function testCanAccessDelegateWithRegisteredSpace(options) { * Run the same tests against several variants of ( access/delegate | access/claim ) handlers. */ for (const variant of /** @type {const} */ ([ - { - name: 'handled by createAccessHandler using array createDelegationsStorage', - ...(() => { - const spaceWithStorageProvider = principal.ed25519.generate() - return { - spaceWithStorageProvider, - ...createTesterFromHandler( - (() => { - const delegations = createDelegationsStorage() - return () => { - return createAccessHandler( - createAccessDelegateHandler({ - delegations, - hasStorageProvider: async (uri) => { - return ( - uri === - (await spaceWithStorageProvider.then((s) => s.did())) - ) - }, - }), - createAccessClaimHandler({ delegations }) - ) - } - })() - ), - } - })(), - }, - { - name: 'handled by createAccessHandler using DbDelegationsStorage', - ...(() => { - const spaceWithStorageProvider = principal.ed25519.generate() - const d1 = context().then((ctx) => ctx.d1) - const database = d1.then((d1) => createD1Database(d1)) - const delegations = database.then((db) => new DbDelegationsStorage(db)) - return { - spaceWithStorageProvider, - ...createTesterFromHandler( - (() => { - return () => { - /** - * @type {InvocationHandler} - */ - return async (invocation) => { - const handle = createAccessHandler( - createAccessDelegateHandler({ - delegations: await delegations, - hasStorageProvider: async (uri) => { - return ( - uri === - (await spaceWithStorageProvider.then((s) => s.did())) - ) - }, - }), - createAccessClaimHandler({ delegations: await delegations }) - ) - return handle(invocation) - } - } - })() - ), - } - })(), - }, { name: 'handled by access-api in miniflare', ...(() => { @@ -443,7 +376,11 @@ async function testInsufficientStorageIfNoStorageProvider(options) { */ /** - * @param {InvocationHandler} invoke + * @typedef {import('@web3-storage/access/types').Service} AccessService + */ + +/** + * @param {import('../src/types/ucanto.js').ServiceInvoke} invoke * @param {Ucanto.Signer>} issuer * @param {Ucanto.Verifier} audience */ @@ -520,31 +457,3 @@ async function setupDelegateThenClaim(invoker, audience) { * @typedef {Ucanto.InferInvokedCapability} AccessClaim * @typedef {Ucanto.InferInvokedCapability} AccessDelegate */ - -/** - * @param {import('../src/service/access-delegate.js').AccessDelegateHandler} handleDelegate - * @param {InvocationHandler} handleClaim - * @returns {InvocationHandler} - */ -function createAccessHandler(handleDelegate, handleClaim) { - return async (invocation) => { - const can = invocation.capabilities[0].can - switch (can) { - case 'access/claim': { - return handleClaim( - /** @type {Ucanto.Invocation} */ (invocation) - ) - } - case 'access/delegate': { - return handleDelegate( - /** @type {Ucanto.Invocation} */ (invocation) - ) - } - default: { - // eslint-disable-next-line no-void - void (/** @type {never} */ (can)) - } - } - throw new Error(`unexpected can=${can}`) - } -} diff --git a/packages/access-api/test/helpers/types.ts b/packages/access-api/test/helpers/types.ts index 5c2ba1e02..c939aba7e 100644 --- a/packages/access-api/test/helpers/types.ts +++ b/packages/access-api/test/helpers/types.ts @@ -1,9 +1,9 @@ import type * as Ucanto from '@ucanto/interface' import type { Miniflare } from 'miniflare' -export interface HelperTestContext { +export interface HelperTestContext> { issuer: Ucanto.Signer> service: Ucanto.Signer> - conn: Ucanto.ConnectionView> + conn: Ucanto.ConnectionView mf: Miniflare } diff --git a/packages/access-api/test/helpers/ucanto-test-utils.js b/packages/access-api/test/helpers/ucanto-test-utils.js index e6357d32b..9cfad490d 100644 --- a/packages/access-api/test/helpers/ucanto-test-utils.js +++ b/packages/access-api/test/helpers/ucanto-test-utils.js @@ -4,13 +4,14 @@ import * as assert from 'assert' import * as principal from '@ucanto/principal' /** - * @typedef {import('./types').HelperTestContext} HelperTestContext + * @typedef {import('@web3-storage/access/types').Service} AccessService */ /** * Tests using context from "./helpers/context.js", which sets up a testable access-api inside miniflare. * - * @param {() => Promise} createContext + * @template {Record} Service + * @param {() => Promise>} createContext * @param {object} [options] * @param {Iterable>} options.registerSpaces - spaces to register in access-api. Some access-api functionality on a space requires it to be registered. */ @@ -19,19 +20,20 @@ export function createTesterFromContext(createContext, options) { await registerSpaces(options?.registerSpaces ?? [], ctx) return ctx }) + /** @type {Promise>} */ + const connection = context.then((ctx) => ctx.conn) const issuer = context.then(({ issuer }) => issuer) const audience = context.then(({ service }) => service) const miniflare = context.then(({ mf }) => mf) /** - * @template {Ucanto.Capability} Capability - * @param {Ucanto.Invocation} invocation + * @type {import('../../src/types/ucanto').ServiceInvoke} */ const invoke = async (invocation) => { const { conn } = await context const [result] = await conn.execute(invocation) return result } - return { issuer, audience, invoke, miniflare } + return { issuer, audience, invoke, miniflare, context, connection } } /** diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 81c20ae21..22ade34c9 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -289,6 +289,12 @@ export function createEmail(storage) { return email } +/** + * @typedef {import('@web3-storage/capabilities/types').AccessClaim} AccessClaim + * @typedef {import('@web3-storage/capabilities/types').AccessAuthorize} AccessAuthorize + * @typedef {import('@web3-storage/capabilities/types').ProviderAdd} ProviderAdd + */ + /** * @param {object} options * @param {Ucanto.Signer>} options.deviceA @@ -296,7 +302,7 @@ export function createEmail(storage) { * @param {Ucanto.Principal>} options.accountA * @param {Ucanto.Principal>} options.service - web3.storage service * @param {import('miniflare').Miniflare} options.miniflare - * @param {(invocation: Ucanto.Invocation) => Promise} options.invoke + * @param {import('../src/types/ucanto.js').ServiceInvoke} options.invoke * @param {ValidationEmailSend[]} options.emails */ async function testAuthorizeClaimProviderAdd(options) { @@ -393,6 +399,7 @@ async function testAuthorizeClaimProviderAdd(options) { assertNotError(providerAddAsAccountResult) const spaceStorageResult = await options.invoke( + // @ts-ignore - not in service type because only enabled while testing await ucanto .invoke({ issuer: space, From 7df213315879ab70b0d597e02a33e58eb996525a Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Mon, 13 Mar 2023 18:42:55 -0700 Subject: [PATCH 08/54] rename access-client-agent test --- packages/access-api/test/access-client-agent.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 075fb7fd6..e69866b2d 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -19,7 +19,7 @@ for (const accessApiVariant of /** @type {const} */ ([ }, ])) { describe(`access-client-agent ${accessApiVariant.name}`, () => { - it('can be used with access-client agent', async () => { + it('can createSpace', async () => { const accessAgent = await AccessAgent.create(undefined, { connection: await accessApiVariant.connection, }) From 86d77a13c292ee952ddb17a7faf025ee74826298 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Mon, 13 Mar 2023 18:37:33 -0700 Subject: [PATCH 09/54] feat: get space creation working-ish 1) save session principal in agent data 2) delegate more permissions from space to account (I think this is working?) 3) add newCreateSpace that can be renamed to get this working with the cli I haven't tested actual space uploads yet, but moving on to that next! --- packages/access-client/src/agent-data.js | 20 +++- packages/access-client/src/agent.js | 128 +++++++++++++++++----- packages/access-client/src/delegations.js | 3 +- packages/access-client/src/types.ts | 2 + packages/access-client/test/agent.test.js | 2 +- 5 files changed, 124 insertions(+), 31 deletions(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 3f6f2a350..b87a83184 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -1,6 +1,7 @@ import { Signer } from '@ucanto/principal' import { Signer as EdSigner } from '@ucanto/principal/ed25519' import { importDAG } from '@ucanto/core/delegation' +import { DID } from '@ucanto/core' import { CID } from 'multiformats' import { Access } from '@web3-storage/capabilities' import { isExpired } from './delegations.js' @@ -19,6 +20,7 @@ export class AgentData { constructor(data, options = {}) { this.meta = data.meta this.principal = data.principal + this.sessionPrincipal = data.sessionPrincipal this.spaces = data.spaces this.delegations = data.delegations this.currentSpace = data.currentSpace @@ -37,6 +39,7 @@ export class AgentData { { meta: { name: 'agent', type: 'device', ...init.meta }, principal: init.principal ?? (await EdSigner.generate()), + sessionPrincipal: init.sessionPrincipal, spaces: init.spaces ?? new Map(), delegations: init.delegations ?? new Map(), currentSpace: init.currentSpace, @@ -76,6 +79,10 @@ export class AgentData { meta: raw.meta, // @ts-expect-error for some reason TS thinks this is a EdSigner principal: Signer.from(raw.principal), + // @ts-expect-error TODO figure out the types for this too + sessionPrincipal: raw.sessionPrincipal + ? DID.parse(raw.sessionPrincipal) + : undefined, currentSpace: raw.currentSpace, spaces: raw.spaces, delegations: dels, @@ -92,6 +99,7 @@ export class AgentData { const raw = { meta: this.meta, principal: this.principal.toArchive(), + sessionPrincipal: this.sessionPrincipal?.did(), currentSpace: this.currentSpace, spaces: this.spaces, delegations: new Map(), @@ -126,6 +134,14 @@ export class AgentData { await this.#save(this.export()) } + /** + * @param {import('@ucanto/interface').Principal>} principal + */ + async setSessionPrincipal(principal) { + this.sessionPrincipal = principal + await this.#save(this.export()) + } + /** * @param {import('@ucanto/interface').Delegation} delegation * @param {import('./types').DelegationMeta} [meta] @@ -152,9 +168,7 @@ export class AgentData { sessionProof() { for (const { delegation } of this.delegations.values()) { const cap = delegation.capabilities.find( - (c) => - // @ts-expect-error "key" does not exist in object, unless it's a session capability - c.can === Access.session.can && c.nb?.key === this.principal.did() + (c) => c.can === Access.session.can // TODO we should make sure this is the current session proof - we were checking nb.key but that doesn't seem to exist in the staging ucan/attest at the moment ) if (cap && !isExpired(delegation)) return delegation } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 610c0dbe2..1a281233f 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -47,17 +47,21 @@ function emailToSessionPrincipal(email) { * @param {Ucanto.Principal>} account * @returns */ -async function createSpaceSaysAccountCanStoreAddWithSpace(space, account) { +async function createSpaceSaysAccountCanAdminSpace(space, account) { return ucanto.delegate({ issuer: space, audience: account, capabilities: [ { - can: 'store/add', + can: 'space/*', with: space.did(), }, { - can: 'store/list', + can: 'store/*', + with: space.did(), + }, + { + can: 'upload/*', with: space.did(), }, ], @@ -261,10 +265,13 @@ export class Agent { for (const { delegation } of this.#delegations(caps)) { const aud = delegation.audience - if (aud.did() === this.issuer.did()) { - arr.push(delegation) - } else if (aud.did() === session?.audience.did()) { + if ( + aud.did() === this.issuer.did() || + aud.did() === session?.audience.did() + ) { arr.push(delegation) + } + if (aud.did() === session?.audience.did()) { hasSessionDelegations = true } } @@ -312,6 +319,42 @@ export class Agent { return arr } + /** + * Creates a space signer and a delegation to the agent + * + * @param {string} [name] + */ + async newCreateSpace(name) { + const signer = await Signer.generate() + const proof = await Space.top.delegate({ + issuer: signer, + audience: this.issuer, + with: signer.did(), + expiration: Infinity, + }) + + await this.addProvider(signer) + await this.delegateSpaceAccessToAccount(signer) + + /** @type {import('./types').SpaceMeta} */ + const meta = { isRegistered: true } + // eslint-disable-next-line eqeqeq + if (name != undefined) { + if (typeof name !== 'string') { + throw new TypeError('invalid name') + } + meta.name = name + } + + await this.#data.addSpace(signer.did(), meta, proof) + + return { + did: signer.did(), + meta, + proof, + } + } + /** * Creates a space signer and a delegation to the agent * @@ -496,7 +539,7 @@ export class Agent { with: this.issuer.did(), nb: { iss: sessionPrincipal.did(), - att: [{ can: 'store/*' }, { can: 'provider/add' }], + att: [{ can: 'store/*' }, { can: 'provider/add' }, { can: 'upload/*' }], }, }) @@ -517,6 +560,7 @@ export class Agent { } await this.addProof(sessionDelegation) + this.#data.setSessionPrincipal(sessionPrincipal) } async claimDelegations() { @@ -532,6 +576,25 @@ export class Agent { ) for (const delegation of delegations) { this.addProof(delegation) + + // if we can find a store/* capability in this delegation, look in the proofs + // for the concrete capabilities where space DIDs will be specified + // TODO: this was my first attempt at inferring spaces from claimed delegations, but I think it needs work - tv + // if (delegation.capabilities.some((cap) => cap.can === 'store/*')) { + // const spaceListingProof = delegation.proofs.find((del) => + // del.capabilities.some((cap) => cap.can === 'store/list') + // ) + // const spaceListingCap = spaceListingProof.capabilities.find( + // (cap) => cap.can === 'store/list' + // ) + // if (spaceListingCap) { + // this.#data.addSpace( + // spaceListingCap.with, + // { isRegistered: true }, + // delegation + // ) + // } + // } } // TODO: should we be inferring which spaces we have access to here and updating local space state? @@ -541,15 +604,18 @@ export class Agent { /** * - * @param {string} email * @param {Signer.EdSigner} space - TODO is this type correct? */ - async addProvider(email, space) { - const sessionPrincipal = emailToSessionPrincipal(email) - await this.invokeAndExecute(Provider.add, { + async addProvider(space) { + const sessionPrincipal = this.#data.sessionPrincipal + + if (!sessionPrincipal) { + throw new Error('cannot add provider, please authorize first') + } + + return await this.invokeAndExecute(Provider.add, { audience: this.connection.id, with: sessionPrincipal.did(), - // TODO: do we really want to send ALL delegations here or should we only select some subset? proofs: this.proofs([ { can: 'provider/add', @@ -558,7 +624,7 @@ export class Agent { ]), nb: { // TODO probably need to make it possible to pass other providers in - provider: 'did:web:web3.storage:providers:w3up-alpha', + provider: 'did:web:staging.web3.storage', consumer: space.did(), }, }) @@ -566,20 +632,27 @@ export class Agent { /** * - * @param {string} email * @param {Signer.EdSigner} space - TODO is this type correct? */ - async delegateSpaceAccessToAccount(email, space) { - const sessionPrincipal = emailToSessionPrincipal(email) - const spaceSaysAccountCanStoreAddWithSpace = - await createSpaceSaysAccountCanStoreAddWithSpace(space, sessionPrincipal) - await this.invokeAndExecute(Access.delegate, { + async delegateSpaceAccessToAccount(space) { + const sessionPrincipal = this.#data.sessionPrincipal + + if (!sessionPrincipal) { + throw new Error( + 'cannot add delegate space access to account, please authorize first' + ) + } + + const spaceSaysAccountCanAdminSpace = + await createSpaceSaysAccountCanAdminSpace(space, sessionPrincipal) + return await this.invokeAndExecute(Access.delegate, { audience: this.connection.id, with: space.did(), + expiration: Infinity, nb: { delegations: { - [spaceSaysAccountCanStoreAddWithSpace.cid.toString()]: - spaceSaysAccountCanStoreAddWithSpace.cid, + [spaceSaysAccountCanAdminSpace.cid.toString()]: + spaceSaysAccountCanAdminSpace.cid, }, }, proofs: [ @@ -588,7 +661,7 @@ export class Agent { this.issuer ), // must be embedded here because it's referenced by cid in .nb.delegations - spaceSaysAccountCanStoreAddWithSpace, + spaceSaysAccountCanAdminSpace, ], }) } @@ -829,6 +902,7 @@ export class Agent { throw new Error('No space selected, you need pass a resource.') } + const extraProofs = options.proofs || [] const proofs = this.proofs([ { with: space, @@ -836,13 +910,15 @@ export class Agent { }, ]) - if (proofs.length === 0 && options.with !== this.did()) { + if ( + proofs.length === 0 && + options.with !== this.did() && + extraProofs.length === 0 + ) { throw new Error( - `no proofs available for resource ${space} and ability ${cap.can}` + `no proofs available for resource ${space} and ability ${cap.can} and no extra proofs were provided` ) } - - const extraProofs = options.proofs || [] const inv = invoke({ ...options, audience: options.audience || this.connection.id, diff --git a/packages/access-client/src/delegations.js b/packages/access-client/src/delegations.js index 5844701f3..3401aad5f 100644 --- a/packages/access-client/src/delegations.js +++ b/packages/access-client/src/delegations.js @@ -63,8 +63,9 @@ export function validate(delegation, opts) { */ export function canDelegateCapability(delegation, child) { for (const parent of delegation.capabilities) { + // TODO is this right? if ( - parent.with === child.with && + (parent.with === child.with || parent.with === 'ucan:*') && canDelegateAbility(parent.can, child.can) ) { return true diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index 648707603..3a07ab1a4 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -161,6 +161,7 @@ export type CIDString = string export interface AgentDataModel { meta: AgentMeta principal: Signer> + sessionPrincipal?: Principal> currentSpace?: DID spaces: Map delegations: Map @@ -174,6 +175,7 @@ export type AgentDataExport = Pick< 'meta' | 'currentSpace' | 'spaces' > & { principal: SignerArchive + sessionPrincipal?: string delegations: Map< CIDString, { diff --git a/packages/access-client/test/agent.test.js b/packages/access-client/test/agent.test.js index e88d7c225..b58d8cb62 100644 --- a/packages/access-client/test/agent.test.js +++ b/packages/access-client/test/agent.test.js @@ -129,7 +129,7 @@ describe('Agent', function () { name: 'Error', message: `no proofs available for resource ${URI.from( fixtures.alice.did() - )} and ability space/info`, + )} and ability space/info and no extra proofs were provided`, } ) }) From 0471e8979fc0a2235a6e3414604191d0832806b7 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Tue, 14 Mar 2023 11:31:09 -0700 Subject: [PATCH 10/54] feat: define `access/confirm` handler and use it in ucanto-test-utils registerSpaces + validate-email handler (#530) Previously a lot of logic to handle `access/confirm` was in the `validate-email` flow, since that is the most common place we'd receive that invocation (after clicking email sent by `access/authorize` handler). However, this logic can be expressed as a `ServiceMethod` on invocation of `access/confirm`, and validate-email can call that. This allows us to also self-issue `access/confirm` in some tests, e.g. in ucanto-test-utils `registerSpaces` and send it to our service to handle, which is only enabled when node env is TEST for now. Benefits: * wherever we use ucanto-test-utils `registerSpaces`, we'll be accurately testing the `access/confirm` + `provider/add` flow (not old/deprecated `voucher/redeem`) --------- Co-authored-by: Travis Vachon --- .../access-api/src/routes/validate-email.js | 104 +++++----------- .../access-api/src/service/access-confirm.js | 91 ++++++++++++++ packages/access-api/src/service/index.js | 17 +++ .../access-api/test/access-authorize.test.js | 19 ++- packages/access-api/test/access-claim.test.js | 1 + .../access-api/test/access-delegate.test.js | 4 + .../test/helpers/ucanto-test-utils.js | 113 +++++++++++++++--- packages/access-api/test/provider-add.test.js | 1 + packages/access-client/src/types.ts | 9 ++ packages/capabilities/src/provider.js | 11 +- packages/capabilities/src/types.ts | 5 + .../test/capabilities/provider.test.js | 36 +++--- 12 files changed, 295 insertions(+), 116 deletions(-) create mode 100644 packages/access-api/src/service/access-confirm.js diff --git a/packages/access-api/src/routes/validate-email.js b/packages/access-api/src/routes/validate-email.js index ba4f003b4..ea3d8b199 100644 --- a/packages/access-api/src/routes/validate-email.js +++ b/packages/access-api/src/routes/validate-email.js @@ -1,6 +1,6 @@ import { - stringToDelegation, delegationsToString, + stringToDelegation, } from '@web3-storage/access/encoding' import * as Access from '@web3-storage/capabilities/access' import QRCode from 'qrcode' @@ -11,10 +11,12 @@ import { ValidateEmailError, PendingValidateEmail, } from '../utils/html.js' -import * as ucanto from '@ucanto/core' import * as validator from '@ucanto/validator' -import { Verifier, Absentee } from '@ucanto/principal' -import { collect } from 'streaming-iterables' +import { Verifier } from '@ucanto/principal' +import * as delegationsResponse from '../utils/delegations-response.js' +import * as accessConfirm from '../service/access-confirm.js' +import { provide } from '@ucanto/server' +import * as Ucanto from '@ucanto/interface' /** * @param {import('@web3-storage/worker-utils/router').ParsedRequest} req @@ -149,62 +151,35 @@ async function authorize(req, env) { }) if (confirmation.error) { - throw new Error(`unable to validate access session: ${confirmation}`) - } - if (confirmation.capability.with !== env.signer.did()) { - throw new Error(`Not a valid access/confirm delegation`) + throw new Error(`unable to validate access session: ${confirmation}`, { + cause: confirmation.error, + }) } - // Create a absentee signer for the account that authorized the delegation - const account = Absentee.from({ id: confirmation.capability.nb.iss }) - const agent = Verifier.parse(confirmation.capability.nb.aud) - - // It the future we should instead render a page and allow a user to select - // which delegations they wish to re-delegate. Right now we just re-delegate - // everything that was requested for all of the resources. - const capabilities = - /** @type {ucanto.UCAN.Capabilities} */ - ( - confirmation.capability.nb.att.map(({ can }) => ({ - can, - with: /** @type {ucanto.UCAN.Resource} */ ('ucan:*'), - })) - ) - - // create an delegation on behalf of the account with an absent signature. - const delegation = await ucanto.delegate({ - issuer: account, - audience: agent, - capabilities, - expiration: Infinity, - // We include all the delegations to the account so that the agent will - // have delegation chains to all the delegated resources. - // We should actually filter out only delegations that support delegated - // capabilities, but for now we just include all of them since we only - // implement sudo access anyway. - proofs: await collect( - env.models.delegations.find({ - audience: account.did(), - }) - ), - }) - - const attestation = await Access.session.delegate({ - issuer: env.signer, - audience: agent, - with: env.signer.did(), - nb: { proof: delegation.cid }, - expiration: Infinity, + const confirm = provide( + Access.confirm, + async ({ capability, invocation }) => { + return accessConfirm.handleAccessConfirm( + /** @type {Ucanto.Invocation} */ ( + invocation + ), + env + ) + } + ) + const confirmResult = await confirm(request, { + id: env.signer.verifier, + principal: Verifier, }) - - // Store the delegations so that they can be pulled with access/claim - // The fact that we're storing proofs chains that we pulled from the - // database is not great, but it's a tradeoff we're making for now. - await env.models.delegations.putMany(delegation, attestation) - - const authorization = delegationsToString([delegation, attestation]) - // Send delegations to the client through a websocket - await env.models.validations.putSession(authorization, agent.did()) + if (confirmResult.error) { + throw new Error('error confirming', { + cause: confirmResult.error, + }) + } + const { account, agent } = accessConfirm.parse(request) + const confirmDelegations = [ + ...delegationsResponse.decode(confirmResult.delegations), + ] // We render HTML page explaining to the user what has happened and providing // a QR code in the details if they want to drill down. @@ -213,20 +188,7 @@ async function authorize(req, env) { { - if (/too big to be stored in a qr/i.test(error.message)) { - env.log.error(error) - // It's not important to have the QR code - // eslint-disable-next-line unicorn/no-useless-undefined - return undefined - } - throw error - })} + ucan={delegationsToString(confirmDelegations)} /> ) ) diff --git a/packages/access-api/src/service/access-confirm.js b/packages/access-api/src/service/access-confirm.js new file mode 100644 index 000000000..3dd04b92a --- /dev/null +++ b/packages/access-api/src/service/access-confirm.js @@ -0,0 +1,91 @@ +import * as Ucanto from '@ucanto/interface' +import * as ucanto from '@ucanto/core' +import { Verifier, Absentee } from '@ucanto/principal' +import { collect } from 'streaming-iterables' +import * as Access from '@web3-storage/capabilities/access' +import { delegationsToString } from '@web3-storage/access/encoding' +import * as delegationsResponse from '../utils/delegations-response.js' + +/** + * @typedef {import('@web3-storage/capabilities/types').AccessConfirmSuccess} AccessConfirmSuccess + * @typedef {import('@web3-storage/capabilities/types').AccessConfirmFailure} AccessConfirmFailure + */ + +/** + * @param {Ucanto.Invocation} invocation + */ +export function parse(invocation) { + const capability = invocation.capabilities[0] + // Create a absentee signer for the account that authorized the delegation + const account = Absentee.from({ id: capability.nb.iss }) + const agent = Verifier.parse(capability.nb.aud) + return { + account, + agent, + } +} + +/** + * @param {Ucanto.Invocation} invocation + * @param {import('../bindings').RouteContext} ctx + * @returns {Promise>} + */ +export async function handleAccessConfirm(invocation, ctx) { + const capability = invocation.capabilities[0] + if (capability.with !== ctx.signer.did()) { + throw new Error(`Not a valid access/confirm delegation`) + } + + const { account, agent } = parse(invocation) + + // It the future we should instead render a page and allow a user to select + // which delegations they wish to re-delegate. Right now we just re-delegate + // everything that was requested for all of the resources. + const capabilities = + /** @type {ucanto.UCAN.Capabilities} */ + ( + capability.nb.att.map(({ can }) => ({ + can, + with: /** @type {ucanto.UCAN.Resource} */ ('ucan:*'), + })) + ) + + // create an delegation on behalf of the account with an absent signature. + const delegation = await ucanto.delegate({ + issuer: account, + audience: agent, + capabilities, + expiration: Infinity, + // We include all the delegations to the account so that the agent will + // have delegation chains to all the delegated resources. + // We should actually filter out only delegations that support delegated + // capabilities, but for now we just include all of them since we only + // implement sudo access anyway. + proofs: await collect( + ctx.models.delegations.find({ + audience: account.did(), + }) + ), + }) + + const attestation = await Access.session.delegate({ + issuer: ctx.signer, + audience: agent, + with: ctx.signer.did(), + nb: { proof: delegation.cid }, + expiration: Infinity, + }) + + // Store the delegations so that they can be pulled with access/claim + // The fact that we're storing proofs chains that we pulled from the + // database is not great, but it's a tradeoff we're making for now. + await ctx.models.delegations.putMany(delegation, attestation) + + const authorization = delegationsToString([delegation, attestation]) + // Save delegations for the validation process + await ctx.models.validations.putSession(authorization, agent.did()) + + return { + delegations: delegationsResponse.encode([delegation, attestation]), + } +} diff --git a/packages/access-api/src/service/index.js b/packages/access-api/src/service/index.js index cef74bbb0..4ecb848cc 100644 --- a/packages/access-api/src/service/index.js +++ b/packages/access-api/src/service/index.js @@ -4,6 +4,7 @@ import * as Server from '@ucanto/server' import * as validator from '@ucanto/validator' import { Failure } from '@ucanto/server' import * as Space from '@web3-storage/capabilities/space' +import * as Access from '@web3-storage/capabilities/access' import { top } from '@web3-storage/capabilities/top' import { delegationToString, @@ -17,6 +18,7 @@ import { accessDelegateProvider } from './access-delegate.js' import { accessClaimProvider } from './access-claim.js' import { providerAddProvider } from './provider-add.js' import { Spaces } from '../models/spaces.js' +import { handleAccessConfirm } from './access-confirm.js' /** * @param {import('../bindings').RouteContext} ctx @@ -45,6 +47,21 @@ export function service(ctx) { config: ctx.config, })(...args) }, + confirm: Server.provide( + Access.confirm, + async ({ capability, invocation }) => { + // only needed in tests + if (ctx.config.ENV !== 'test') { + throw new Error(`access/confirm is disabled`) + } + return handleAccessConfirm( + /** @type {Ucanto.Invocation} */ ( + invocation + ), + ctx + ) + } + ), delegate: (...args) => { // disable until hardened in test/staging if (ctx.config.ENV === 'production') { diff --git a/packages/access-api/test/access-authorize.test.js b/packages/access-api/test/access-authorize.test.js index 9a5230ba8..50d3820cc 100644 --- a/packages/access-api/test/access-authorize.test.js +++ b/packages/access-api/test/access-authorize.test.js @@ -17,6 +17,7 @@ import { } from './helpers/ucanto-test-utils.js' import { ed25519, Absentee } from '@ucanto/principal' import { delegate } from '@ucanto/core' +import { Space } from '@web3-storage/capabilities' /** @type {typeof assert} */ const t = assert @@ -312,8 +313,12 @@ describe('access/authorize', function () { const space = await ed25519.generate() const w3 = ctx.service - await registerSpaces([space], ctx) const account = Absentee.from({ id: 'did:mailto:dag.house:test' }) + await registerSpaces([space], { + ...ctx, + agent: ctx.issuer, + account, + }) // delegate all space capabilities to the account const delegation = await delegate({ @@ -342,6 +347,7 @@ describe('access/authorize', function () { }) .execute(ctx.conn) + warnOnErrorResult(delegateResult) assert.equal(delegateResult.error, undefined, 'delegation succeeded') // Now generate an agent and try to authorize with the account @@ -424,5 +430,16 @@ describe('access/authorize', function () { delegation.cid, 'delegation to an account is included' ) + + // use these delegations to do something on the space + const info = await Space.info + .invoke({ + issuer: agent, + audience: w3, + with: space.did(), + proofs: [authorization, attestation], + }) + .execute(ctx.conn) + assert.notDeepEqual(info.error, true, 'space/info did not error') }) }) diff --git a/packages/access-api/test/access-claim.test.js b/packages/access-api/test/access-claim.test.js index 6d5122234..97bc84362 100644 --- a/packages/access-api/test/access-claim.test.js +++ b/packages/access-api/test/access-claim.test.js @@ -16,6 +16,7 @@ for (const handlerVariant of /** @type {const} */ ([ spaceWithStorageProvider, ...createTesterFromContext(() => context(), { registerSpaces: [spaceWithStorageProvider], + account: { did: () => /** @type {const} */ ('did:mailto:foo') }, }), } })(), diff --git a/packages/access-api/test/access-delegate.test.js b/packages/access-api/test/access-delegate.test.js index 27fab5e9c..a9bcb2048 100644 --- a/packages/access-api/test/access-delegate.test.js +++ b/packages/access-api/test/access-delegate.test.js @@ -26,10 +26,12 @@ for (const handlerVariant of /** @type {const} */ ([ name: 'handled by access-api in miniflare', ...(() => { const spaceWithStorageProvider = principal.ed25519.generate() + const account = { did: () => /** @type {const} */ ('did:mailto:foo') } return { spaceWithStorageProvider, ...createTesterFromContext(() => context(), { registerSpaces: [spaceWithStorageProvider], + account, }), } })(), @@ -123,10 +125,12 @@ for (const variant of /** @type {const} */ ([ name: 'handled by access-api in miniflare', ...(() => { const spaceWithStorageProvider = principal.ed25519.generate() + const account = { did: () => /** @type {const} */ ('did:mailto:foo') } return { spaceWithStorageProvider, ...createTesterFromContext(() => context(), { registerSpaces: [spaceWithStorageProvider], + account, }), } })(), diff --git a/packages/access-api/test/helpers/ucanto-test-utils.js b/packages/access-api/test/helpers/ucanto-test-utils.js index 9cfad490d..5163e3ae8 100644 --- a/packages/access-api/test/helpers/ucanto-test-utils.js +++ b/packages/access-api/test/helpers/ucanto-test-utils.js @@ -1,8 +1,8 @@ import * as Ucanto from '@ucanto/interface' -import { Voucher } from '@web3-storage/capabilities' +import { Access, Provider, Voucher } from '@web3-storage/capabilities' import * as assert from 'assert' import * as principal from '@ucanto/principal' - +import * as delegationsResponse from '../../src/utils/delegations-response.js' /** * @typedef {import('@web3-storage/access/types').Service} AccessService */ @@ -13,12 +13,23 @@ import * as principal from '@ucanto/principal' * @template {Record} Service * @param {() => Promise>} createContext * @param {object} [options] - * @param {Iterable>} options.registerSpaces - spaces to register in access-api. Some access-api functionality on a space requires it to be registered. + * @param {Ucanto.Principal>} options.account - account to register spaces with + * @param {Iterable>>>} options.registerSpaces - spaces to register in access-api. Some access-api functionality on a space requires it to be registered. */ export function createTesterFromContext(createContext, options) { const context = createContext().then(async (ctx) => { - await registerSpaces(options?.registerSpaces ?? [], ctx) - return ctx + const registeredSpaceAgent = await principal.ed25519.generate() + if (options) { + await registerSpaces(options?.registerSpaces ?? [], { + ...ctx, + account: options.account, + agent: registeredSpaceAgent, + }) + } + return { + ...ctx, + registeredSpaceAgent, + } }) /** @type {Promise>} */ const connection = context.then((ctx) => ctx.conn) @@ -45,23 +56,55 @@ export function createTesterFromContext(createContext, options) { * given an iterable of spaces, register them against an access-api * using a service-issued voucher/redeem invocation * - * @param {Iterable>} spaces + * @param {Iterable>>>} spaces * @param {object} options - * @param {Ucanto.Signer} options.service + * @param {Ucanto.Signer>} options.service + * @param {Ucanto.Signer>} options.agent + * @param {Ucanto.Principal>} options.account * @param {Ucanto.ConnectionView>} options.conn */ -export async function registerSpaces(spaces, { service, conn }) { +export async function registerSpaces( + spaces, + { service, conn, account, agent } +) { + // first register account + const request = await accountRegistrationInvocation( + service, + account.did(), + agent.did(), + service + ) + const results = await conn.execute(request) + assert.deepEqual( + results.length, + 1, + 'registration invocation should have 1 result' + ) + const [result] = results + assertNotError(result) + assert.ok( + 'delegations' in result, + 'registration result should have delegations' + ) + const accountDelegations = [ + ...delegationsResponse.decode(/** @type {any} */ (result.delegations)), + ] for (const spacePromise of spaces) { const space = await spacePromise - const redeem = await spaceRegistrationInvocation(service, space.did()) - const results = await conn.execute(redeem) - assert.deepEqual( - results.length, - 1, - 'registration invocation should have 1 result' - ) - const [result] = results - assertNotError(result) + const addProvider = await Provider.add + .invoke({ + issuer: agent, + audience: service, + with: account.did(), + nb: { + consumer: space.did(), + provider: service.did(), + }, + proofs: [...accountDelegations], + }) + .delegate() + const [addProviderResult] = await conn.execute(addProvider) + assertNotError(addProviderResult) } } @@ -74,7 +117,7 @@ export async function registerSpaces(spaces, { service, conn }) { * @param {Ucanto.DID} space * @param {Ucanto.Principal} audience - audience of the invocation. often is same as issuer */ -export async function spaceRegistrationInvocation( +export async function spaceRegistrationInvocationVoucher( issuer, space, audience = issuer @@ -94,6 +137,40 @@ export async function spaceRegistrationInvocation( return redeem } +/** + * get an access-api invocation that will register an account. + * This is useful e.g. because some functionality (e.g. access/delegate) + * will fail unless the space is registered. + * + * @param {Ucanto.Signer} service - issues voucher/redeem. e.g. could be the same signer as access-api env.PRIVATE_KEY + * @param {Ucanto.DID<'mailto'>} account + * @param {Ucanto.DID<'key'>} agent + * @param {Ucanto.Principal} audience - audience of the invocation. often is same as issuer + * @param {number} lifetimeInSeconds + */ +export async function accountRegistrationInvocation( + service, + account, + agent, + audience = service, + lifetimeInSeconds = 60 * 15 +) { + const register = await Access.confirm + .invoke({ + issuer: service, + audience, + with: service.did(), + lifetimeInSeconds, + nb: { + iss: account, + aud: agent, + att: [{ can: '*' }], + }, + }) + .delegate() + return register +} + /** * @param {{ error?: unknown }|null} result * @param {string} assertionMessage diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 22ade34c9..4411208da 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -86,6 +86,7 @@ for (const accessApiVariant of /** @type {const} */ ([ }), { registerSpaces: [spaceWithStorageProvider], + account: { did: () => /** @type {const} */ ('did:mailto:foo') }, } ), } diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index 3a07ab1a4..3f2fd70d0 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -46,6 +46,9 @@ import type { ProviderAdd, ProviderAddSuccess, ProviderAddFailure, + AccessConfirm, + AccessConfirmSuccess, + AccessConfirmFailure, } from '@web3-storage/capabilities/types' import type { SetRequired } from 'type-fest' import { Driver } from './drivers/types.js' @@ -114,6 +117,12 @@ export interface Service { access: { authorize: ServiceMethod claim: ServiceMethod + // eslint-disable-next-line @typescript-eslint/ban-types + confirm: ServiceMethod< + AccessConfirm, + AccessConfirmSuccess, + AccessConfirmFailure + > delegate: ServiceMethod< AccessDelegate, AccessDelegateSuccess, diff --git a/packages/capabilities/src/provider.js b/packages/capabilities/src/provider.js index a29f9b634..567dadc46 100644 --- a/packages/capabilities/src/provider.js +++ b/packages/capabilities/src/provider.js @@ -8,16 +8,11 @@ * * @module */ -import { capability, DID, literal, struct } from '@ucanto/validator' +import { capability, DID, struct } from '@ucanto/validator' import { equalWith, fail, equal } from './utils.js' -export const Web3StorageId = literal('did:web:web3.storage').or( - literal('did:web:staging.web3.storage') -) - -export const Provider = Web3StorageId.or(DID.match({ method: 'key' })).or( - DID.match({ method: 'web' }) -) +// e.g. did:web:web3.storage or did:web:staging.web3.storage +export const Provider = DID.match({ method: 'web' }) export const AccountDID = DID.match({ method: 'mailto' }) diff --git a/packages/capabilities/src/types.ts b/packages/capabilities/src/types.ts index cae9e031c..25442b907 100644 --- a/packages/capabilities/src/types.ts +++ b/packages/capabilities/src/types.ts @@ -36,6 +36,11 @@ export interface AccessClaimFailure { error: true } +export interface AccessConfirmSuccess { + delegations: Record> +} +export interface AccessConfirmFailure extends Ucanto.Failure {} + export type AccessDelegate = InferInvokedCapability export type AccessDelegateSuccess = unknown export type AccessDelegateFailure = { error: true } | InsufficientStorage diff --git a/packages/capabilities/test/capabilities/provider.test.js b/packages/capabilities/test/capabilities/provider.test.js index b6918b31a..e8af4546e 100644 --- a/packages/capabilities/test/capabilities/provider.test.js +++ b/packages/capabilities/test/capabilities/provider.test.js @@ -18,7 +18,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, proofs: await createAuthorization({ agent, service, account }), @@ -35,7 +35,7 @@ describe('provider/add', function () { assert.deepEqual(result.audience.did(), service.did()) assert.equal(result.capability.can, 'provider/add') assert.deepEqual(result.capability.nb, { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }) } @@ -50,7 +50,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, }) @@ -78,7 +78,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, proofs: [delegation], @@ -107,7 +107,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, proofs: [attestation], @@ -129,9 +129,9 @@ describe('provider/add', function () { issuer: bob, audience: service, with: bobAccount.did(), - // @ts-expect-error + // @ts-ignore nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', }, }) }, /Error: Invalid 'nb' - Object contains invalid field "consumer"/) @@ -145,7 +145,7 @@ describe('provider/add', function () { audience: service, with: bobAccount.did(), nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', // @ts-expect-error consumer: 'did:mailto:web3.storage:user', }, @@ -162,7 +162,7 @@ describe('provider/add', function () { with: bobAccount.did(), // @ts-expect-error - missing provider nb: { - // provider: service.did(), + // provider: 'did:web:test.web3.storage', consumer: bob.did(), }, }) @@ -193,7 +193,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, proofs: [ @@ -202,7 +202,7 @@ describe('provider/add', function () { audience: bob, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, proofs: await createAuthorization({ agent, service, account }), @@ -228,7 +228,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, proofs: [ @@ -237,7 +237,7 @@ describe('provider/add', function () { audience: bob, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', }, proofs: await createAuthorization({ agent, service, account }), }), @@ -262,7 +262,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: space.did(), }, proofs: [ @@ -296,7 +296,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: bob.did(), }, proofs: [ @@ -331,7 +331,7 @@ describe('provider/add', function () { audience: service, with: account, nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: bob.did(), }, proofs: [ @@ -371,7 +371,7 @@ describe('provider/add', function () { audience: service, with: 'did:mailto:mallory.com:bob', nb: { - provider: service.did(), + provider: 'did:web:test.web3.storage', consumer: bob.did(), }, proofs: [ @@ -419,7 +419,7 @@ describe('provider/add', function () { with: account.did(), nb: { consumer: space.did(), - provider: service.did(), + provider: 'did:web:test.web3.storage', }, // NOTE: no proofs! }) From 098d488cf28230fbed16c0122c665c0f7f28ac98 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Tue, 14 Mar 2023 11:59:25 -0700 Subject: [PATCH 11/54] fix tsc --- packages/access-api/test/access-client-agent.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index e69866b2d..9b960cfae 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -8,10 +8,14 @@ for (const accessApiVariant of /** @type {const} */ ([ { name: 'using access-api in miniflare', ...(() => { + const account = { + did: () => /** @type {const} */ ('did:mailto:dag.house:foo'), + } const spaceWithStorageProvider = principal.ed25519.generate() return { spaceWithStorageProvider, ...createTesterFromContext(context, { + account, registerSpaces: [spaceWithStorageProvider], }), } From 78cb8bf9073e377060445428d1e5e1077d3eb5f0 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Tue, 14 Mar 2023 15:05:12 -0700 Subject: [PATCH 12/54] feat: fix canDelegateCapability et al 1) use new allows method from a ucanto branch to do so 2) throw errors in new space create flow if we adding a provider or delegating access fails 3) claim delegations after authorizing --- packages/access-client/package.json | 2 +- packages/access-client/src/agent.js | 24 +++++++++++++++++----- packages/access-client/src/delegations.js | 22 ++++++++++++-------- pnpm-lock.yaml | 25 +++++++++++++++++++++-- 4 files changed, 57 insertions(+), 16 deletions(-) diff --git a/packages/access-client/package.json b/packages/access-client/package.json index 883b6ebd7..5561b2efa 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -62,7 +62,7 @@ "@ipld/dag-cbor": "^9.0.0", "@ipld/dag-ucan": "^3.2.0", "@ucanto/client": "^5.1.0", - "@ucanto/core": "^5.1.0", + "@ucanto/core": "file:../../../ucanto/packages/core", "@ucanto/interface": "^6.0.0", "@ucanto/principal": "^5.1.0", "@ucanto/transport": "^5.1.0", diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 1a281233f..ffb1a35c1 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -333,9 +333,19 @@ export class Agent { expiration: Infinity, }) - await this.addProvider(signer) - await this.delegateSpaceAccessToAccount(signer) - + const providerResult = await this.addProvider(signer) + if (providerResult.error) { + throw new Error(providerResult.message, { cause: providerResult }) + } + const delegateSpaceAccessResult = await this.delegateSpaceAccessToAccount( + signer + ) + if (delegateSpaceAccessResult.error) { + // @ts-ignore it's very weird that this is throwing an error but line 338 above does not - ignore for now + throw new Error(delegateSpaceAccessResult.message, { + cause: delegateSpaceAccessResult, + }) + } /** @type {import('./types').SpaceMeta} */ const meta = { isRegistered: true } // eslint-disable-next-line eqeqeq @@ -561,6 +571,10 @@ export class Agent { await this.addProof(sessionDelegation) this.#data.setSessionPrincipal(sessionPrincipal) + + // claim delegations here because we will need an ucan/attest from the service to + // pair with the session delegation we just claimed to make it work + await this.claimDelegations() } async claimDelegations() { @@ -613,7 +627,7 @@ export class Agent { throw new Error('cannot add provider, please authorize first') } - return await this.invokeAndExecute(Provider.add, { + return this.invokeAndExecute(Provider.add, { audience: this.connection.id, with: sessionPrincipal.did(), proofs: this.proofs([ @@ -645,7 +659,7 @@ export class Agent { const spaceSaysAccountCanAdminSpace = await createSpaceSaysAccountCanAdminSpace(space, sessionPrincipal) - return await this.invokeAndExecute(Access.delegate, { + return this.invokeAndExecute(Access.delegate, { audience: this.connection.id, with: space.did(), expiration: Infinity, diff --git a/packages/access-client/src/delegations.js b/packages/access-client/src/delegations.js index 3401aad5f..c6b666ded 100644 --- a/packages/access-client/src/delegations.js +++ b/packages/access-client/src/delegations.js @@ -1,6 +1,7 @@ // @ts-ignore // eslint-disable-next-line no-unused-vars import * as Ucanto from '@ucanto/interface' +import * as ucanto from '@ucanto/core' import { canDelegateAbility } from '@web3-storage/capabilities/utils' /** @@ -44,7 +45,9 @@ export function validate(delegation, opts) { } = opts ?? {} if (checkAudience && delegation.audience.did() !== checkAudience.did()) { - throw new Error(`Delegation audience does not match required DID.`) + throw new Error( + `Delegation audience ${delegation.audience.did()} does not match required DID ${checkAudience.did()}` + ) } if (checkIsExpired && isExpired(delegation)) { @@ -62,13 +65,16 @@ export function validate(delegation, opts) { * @param {import('@ucanto/interface').Capability} child */ export function canDelegateCapability(delegation, child) { - for (const parent of delegation.capabilities) { - // TODO is this right? - if ( - (parent.with === child.with || parent.with === 'ucan:*') && - canDelegateAbility(parent.can, child.can) - ) { - return true + // @ts-ignore ignore this until we bring in ucanto as a normal dependency rather than linking to a local version + const allowsCapabilities = ucanto.Delegation.allows(delegation) + if (allowsCapabilities[child.with]) { + const cans = /** @type {import('@ucanto/interface').Ability[]} */ ( + Object.keys(allowsCapabilities[child.with]) + ) + for (const can of cans) { + if (canDelegateAbility(can, child.can)) { + return true + } } } return false diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 053c62024..66572065a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,7 +134,7 @@ importers: '@types/varint': ^6.0.1 '@types/ws': ^8.5.4 '@ucanto/client': ^5.1.0 - '@ucanto/core': ^5.1.0 + '@ucanto/core': file:../../../ucanto/packages/core '@ucanto/interface': ^6.0.0 '@ucanto/principal': ^5.1.0 '@ucanto/server': ^6.0.0 @@ -171,7 +171,7 @@ importers: '@ipld/dag-cbor': 9.0.0 '@ipld/dag-ucan': 3.2.0 '@ucanto/client': 5.1.0 - '@ucanto/core': 5.1.0 + '@ucanto/core': file:../ucanto/packages/core '@ucanto/interface': 6.0.0 '@ucanto/principal': 5.1.0 '@ucanto/transport': 5.1.0 @@ -13007,3 +13007,24 @@ packages: /zwitch/1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} dev: true + + file:../ucanto/packages/core: + resolution: {directory: ../ucanto/packages/core, type: directory} + name: '@ucanto/core' + version: 5.1.0 + dependencies: + '@ipld/car': 5.1.0 + '@ipld/dag-cbor': 9.0.0 + '@ipld/dag-ucan': 3.2.0 + '@ucanto/interface': file:../ucanto/packages/interface + multiformats: 11.0.1 + dev: false + + file:../ucanto/packages/interface: + resolution: {directory: ../ucanto/packages/interface, type: directory} + name: '@ucanto/interface' + version: 6.1.0 + dependencies: + '@ipld/dag-ucan': 3.2.0 + multiformats: 11.0.1 + dev: false From f4ad371ddc0af83f8b4cd2d815ee1a2b57fb7540 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Tue, 14 Mar 2023 16:12:51 -0700 Subject: [PATCH 13/54] Revert "feat: fix canDelegateCapability et al" This reverts commit 78cb8bf9073e377060445428d1e5e1077d3eb5f0. --- packages/access-client/package.json | 2 +- packages/access-client/src/agent.js | 24 +++++----------------- packages/access-client/src/delegations.js | 22 ++++++++------------ pnpm-lock.yaml | 25 ++--------------------- 4 files changed, 16 insertions(+), 57 deletions(-) diff --git a/packages/access-client/package.json b/packages/access-client/package.json index 5561b2efa..883b6ebd7 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -62,7 +62,7 @@ "@ipld/dag-cbor": "^9.0.0", "@ipld/dag-ucan": "^3.2.0", "@ucanto/client": "^5.1.0", - "@ucanto/core": "file:../../../ucanto/packages/core", + "@ucanto/core": "^5.1.0", "@ucanto/interface": "^6.0.0", "@ucanto/principal": "^5.1.0", "@ucanto/transport": "^5.1.0", diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index ffb1a35c1..1a281233f 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -333,19 +333,9 @@ export class Agent { expiration: Infinity, }) - const providerResult = await this.addProvider(signer) - if (providerResult.error) { - throw new Error(providerResult.message, { cause: providerResult }) - } - const delegateSpaceAccessResult = await this.delegateSpaceAccessToAccount( - signer - ) - if (delegateSpaceAccessResult.error) { - // @ts-ignore it's very weird that this is throwing an error but line 338 above does not - ignore for now - throw new Error(delegateSpaceAccessResult.message, { - cause: delegateSpaceAccessResult, - }) - } + await this.addProvider(signer) + await this.delegateSpaceAccessToAccount(signer) + /** @type {import('./types').SpaceMeta} */ const meta = { isRegistered: true } // eslint-disable-next-line eqeqeq @@ -571,10 +561,6 @@ export class Agent { await this.addProof(sessionDelegation) this.#data.setSessionPrincipal(sessionPrincipal) - - // claim delegations here because we will need an ucan/attest from the service to - // pair with the session delegation we just claimed to make it work - await this.claimDelegations() } async claimDelegations() { @@ -627,7 +613,7 @@ export class Agent { throw new Error('cannot add provider, please authorize first') } - return this.invokeAndExecute(Provider.add, { + return await this.invokeAndExecute(Provider.add, { audience: this.connection.id, with: sessionPrincipal.did(), proofs: this.proofs([ @@ -659,7 +645,7 @@ export class Agent { const spaceSaysAccountCanAdminSpace = await createSpaceSaysAccountCanAdminSpace(space, sessionPrincipal) - return this.invokeAndExecute(Access.delegate, { + return await this.invokeAndExecute(Access.delegate, { audience: this.connection.id, with: space.did(), expiration: Infinity, diff --git a/packages/access-client/src/delegations.js b/packages/access-client/src/delegations.js index c6b666ded..3401aad5f 100644 --- a/packages/access-client/src/delegations.js +++ b/packages/access-client/src/delegations.js @@ -1,7 +1,6 @@ // @ts-ignore // eslint-disable-next-line no-unused-vars import * as Ucanto from '@ucanto/interface' -import * as ucanto from '@ucanto/core' import { canDelegateAbility } from '@web3-storage/capabilities/utils' /** @@ -45,9 +44,7 @@ export function validate(delegation, opts) { } = opts ?? {} if (checkAudience && delegation.audience.did() !== checkAudience.did()) { - throw new Error( - `Delegation audience ${delegation.audience.did()} does not match required DID ${checkAudience.did()}` - ) + throw new Error(`Delegation audience does not match required DID.`) } if (checkIsExpired && isExpired(delegation)) { @@ -65,16 +62,13 @@ export function validate(delegation, opts) { * @param {import('@ucanto/interface').Capability} child */ export function canDelegateCapability(delegation, child) { - // @ts-ignore ignore this until we bring in ucanto as a normal dependency rather than linking to a local version - const allowsCapabilities = ucanto.Delegation.allows(delegation) - if (allowsCapabilities[child.with]) { - const cans = /** @type {import('@ucanto/interface').Ability[]} */ ( - Object.keys(allowsCapabilities[child.with]) - ) - for (const can of cans) { - if (canDelegateAbility(can, child.can)) { - return true - } + for (const parent of delegation.capabilities) { + // TODO is this right? + if ( + (parent.with === child.with || parent.with === 'ucan:*') && + canDelegateAbility(parent.can, child.can) + ) { + return true } } return false diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 66572065a..053c62024 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,7 +134,7 @@ importers: '@types/varint': ^6.0.1 '@types/ws': ^8.5.4 '@ucanto/client': ^5.1.0 - '@ucanto/core': file:../../../ucanto/packages/core + '@ucanto/core': ^5.1.0 '@ucanto/interface': ^6.0.0 '@ucanto/principal': ^5.1.0 '@ucanto/server': ^6.0.0 @@ -171,7 +171,7 @@ importers: '@ipld/dag-cbor': 9.0.0 '@ipld/dag-ucan': 3.2.0 '@ucanto/client': 5.1.0 - '@ucanto/core': file:../ucanto/packages/core + '@ucanto/core': 5.1.0 '@ucanto/interface': 6.0.0 '@ucanto/principal': 5.1.0 '@ucanto/transport': 5.1.0 @@ -13007,24 +13007,3 @@ packages: /zwitch/1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} dev: true - - file:../ucanto/packages/core: - resolution: {directory: ../ucanto/packages/core, type: directory} - name: '@ucanto/core' - version: 5.1.0 - dependencies: - '@ipld/car': 5.1.0 - '@ipld/dag-cbor': 9.0.0 - '@ipld/dag-ucan': 3.2.0 - '@ucanto/interface': file:../ucanto/packages/interface - multiformats: 11.0.1 - dev: false - - file:../ucanto/packages/interface: - resolution: {directory: ../ucanto/packages/interface, type: directory} - name: '@ucanto/interface' - version: 6.1.0 - dependencies: - '@ipld/dag-ucan': 3.2.0 - multiformats: 11.0.1 - dev: false From 95a1c4ab175979e7e9885abc518517e47fb411ce Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Tue, 14 Mar 2023 16:32:10 -0700 Subject: [PATCH 14/54] feat: test access-client-agent authorize (#535) --- packages/access-api/package.json | 1 + .../access-api/src/service/access-confirm.js | 72 ++++++++--- .../test/access-client-agent.test.js | 122 +++++++++++++++++- .../test/helpers/ucanto-test-utils.js | 3 +- packages/access-api/test/helpers/utils.js | 23 ++++ packages/access-api/test/provider-add.test.js | 19 +-- packages/access-client/src/agent.js | 4 +- 7 files changed, 199 insertions(+), 45 deletions(-) diff --git a/packages/access-api/package.json b/packages/access-api/package.json index 18f23566c..34053dc01 100644 --- a/packages/access-api/package.json +++ b/packages/access-api/package.json @@ -94,6 +94,7 @@ "error", { "definedTypes": [ + "AsyncIterable", "AsyncIterableIterator", "Awaited", "D1Database", diff --git a/packages/access-api/src/service/access-confirm.js b/packages/access-api/src/service/access-confirm.js index 3dd04b92a..9b04088b1 100644 --- a/packages/access-api/src/service/access-confirm.js +++ b/packages/access-api/src/service/access-confirm.js @@ -1,10 +1,11 @@ import * as Ucanto from '@ucanto/interface' import * as ucanto from '@ucanto/core' -import { Verifier, Absentee } from '@ucanto/principal' +import { Absentee } from '@ucanto/principal' import { collect } from 'streaming-iterables' import * as Access from '@web3-storage/capabilities/access' import { delegationsToString } from '@web3-storage/access/encoding' import * as delegationsResponse from '../utils/delegations-response.js' +import * as validator from '@ucanto/validator' /** * @typedef {import('@web3-storage/capabilities/types').AccessConfirmSuccess} AccessConfirmSuccess @@ -18,7 +19,9 @@ export function parse(invocation) { const capability = invocation.capabilities[0] // Create a absentee signer for the account that authorized the delegation const account = Absentee.from({ id: capability.nb.iss }) - const agent = Verifier.parse(capability.nb.aud) + const agent = { + did: () => validator.DID.match({ method: 'key' }).from(capability.nb.aud), + } return { account, agent, @@ -50,31 +53,21 @@ export async function handleAccessConfirm(invocation, ctx) { })) ) - // create an delegation on behalf of the account with an absent signature. - const delegation = await ucanto.delegate({ - issuer: account, - audience: agent, + const [delegation, attestation] = await createSessionProofs( + ctx.signer, + account, + agent, capabilities, - expiration: Infinity, // We include all the delegations to the account so that the agent will // have delegation chains to all the delegated resources. // We should actually filter out only delegations that support delegated // capabilities, but for now we just include all of them since we only // implement sudo access anyway. - proofs: await collect( - ctx.models.delegations.find({ - audience: account.did(), - }) - ), - }) - - const attestation = await Access.session.delegate({ - issuer: ctx.signer, - audience: agent, - with: ctx.signer.did(), - nb: { proof: delegation.cid }, - expiration: Infinity, - }) + ctx.models.delegations.find({ + audience: account.did(), + }), + Infinity + ) // Store the delegations so that they can be pulled with access/claim // The fact that we're storing proofs chains that we pulled from the @@ -89,3 +82,40 @@ export async function handleAccessConfirm(invocation, ctx) { delegations: delegationsResponse.encode([delegation, attestation]), } } + +/** + * @param {Ucanto.Signer} service + * @param {Ucanto.Principal>} account + * @param {Ucanto.Principal>} agent + * @param {Ucanto.Capabilities} capabilities + * @param {AsyncIterable} delegationProofs + * @param {number} expiration + * @returns {Promise<[delegation: Ucanto.Delegation, attestation: Ucanto.Delegation]>} + */ +export async function createSessionProofs( + service, + account, + agent, + capabilities, + delegationProofs, + expiration +) { + // create an delegation on behalf of the account with an absent signature. + const delegation = await ucanto.delegate({ + issuer: Absentee.from({ id: account.did() }), + audience: agent, + capabilities, + expiration, + proofs: [...(await collect(delegationProofs))], + }) + + const attestation = await Access.session.delegate({ + issuer: service, + audience: agent, + with: service.did(), + nb: { proof: delegation.cid }, + expiration, + }) + + return [delegation, attestation] +} diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 9b960cfae..0b0da17ab 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -1,8 +1,14 @@ +/* eslint-disable no-console */ import { context } from './helpers/context.js' import { createTesterFromContext } from './helpers/ucanto-test-utils.js' import * as principal from '@ucanto/principal' import { Agent as AccessAgent } from '@web3-storage/access/agent' +import * as w3caps from '@web3-storage/capabilities' import * as assert from 'assert' +import * as Ucanto from '@ucanto/interface' +import { createEmail } from './helpers/utils.js' +import { stringToDelegations } from '@web3-storage/access/encoding' +import * as delegationsResponse from '../src/utils/delegations-response.js' for (const accessApiVariant of /** @type {const} */ ([ { @@ -12,12 +18,24 @@ for (const accessApiVariant of /** @type {const} */ ([ did: () => /** @type {const} */ ('did:mailto:dag.house:foo'), } const spaceWithStorageProvider = principal.ed25519.generate() + /** @type {{to:string, url:string}[]} */ + const emails = [] + const email = createEmail(emails) return { spaceWithStorageProvider, - ...createTesterFromContext(context, { - account, - registerSpaces: [spaceWithStorageProvider], - }), + emails, + ...createTesterFromContext( + () => + context({ + globals: { + email, + }, + }), + { + account, + registerSpaces: [spaceWithStorageProvider], + } + ), } })(), }, @@ -31,5 +49,101 @@ for (const accessApiVariant of /** @type {const} */ ([ const delegations = accessAgent.proofs() assert.equal(space.proof.cid, delegations[0].cid) }) + it.skip('can authorize', async () => { + const accessAgent = await AccessAgent.create(undefined, { + connection: await accessApiVariant.connection, + }) + await accessAgent.authorize('example@dag.house') + }) + + it('can be used to do session authorization', async () => { + const { emails, connection, service } = accessApiVariant + const accessAgent = await AccessAgent.create(undefined, { + connection: await connection, + }) + /** @type {Ucanto.Principal>} */ + const account = { did: () => 'did:mailto:dag.house:example' } + await testSessionAuthorization( + await service, + accessAgent, + account, + emails + ) + }) + }) +} + +/** + * @typedef {import('./provider-add.test.js').AccessAuthorize} AccessAuthorize + * @typedef {import('@web3-storage/capabilities/src/types.js').AccessConfirm} AccessConfirm + * @typedef {import('./helpers/ucanto-test-utils.js').AccessService} AccessService + */ + +/** + * @param {principal.ed25519.Signer.Signer<`did:web:${string}`, principal.ed25519.Signer.UCAN.SigAlg>} service + * @param {AccessAgent} access + * @param {Ucanto.Principal>} account + * @param {{to:string, url:string}[]} emails + */ +async function testSessionAuthorization(service, access, account, emails) { + const authorizeResult = await access.invokeAndExecute( + w3caps.Access.authorize, + { + audience: access.connection.id, + with: access.issuer.did(), + nb: { + iss: account.did(), + att: [{ can: '*' }], + }, + } + ) + assert.notDeepStrictEqual( + authorizeResult.error, + true, + 'authorize result is not an error' + ) + + const latestEmail = emails.at(-1) + assert.ok(latestEmail, 'received a confirmation email') + const confirmationInvocations = stringToDelegations( + new URL(latestEmail.url).searchParams.get('ucan') ?? '' + ) + assert.deepEqual(confirmationInvocations.length, 1) + const serviceSaysAccountCanConfirm = + /** @type {Ucanto.Invocation} */ ( + confirmationInvocations[0] + ) + + const confirm = await w3caps.Access.confirm + .invoke({ + nb: { + ...serviceSaysAccountCanConfirm.capabilities[0].nb, + }, + issuer: service, + audience: access.connection.id, + with: access.connection.id.did(), + proofs: [serviceSaysAccountCanConfirm], + }) + .delegate() + + const [confirmationResult] = await access.connection.execute(confirm) + assert.notDeepStrictEqual( + confirmationResult.error, + true, + 'confirm result is not an error' + ) + + const claimResult = await access.invokeAndExecute(w3caps.Access.claim, { + with: access.issuer.did(), }) + assert.notDeepEqual( + claimResult.error, + true, + 'access/claim result is not an error' + ) + assert.ok(!claimResult.error) + const claimedDelegations1 = [ + ...delegationsResponse.decode(claimResult.delegations), + ] + assert.ok(claimedDelegations1.length > 0, 'claimed some delegations') } diff --git a/packages/access-api/test/helpers/ucanto-test-utils.js b/packages/access-api/test/helpers/ucanto-test-utils.js index 5163e3ae8..7d53c3acc 100644 --- a/packages/access-api/test/helpers/ucanto-test-utils.js +++ b/packages/access-api/test/helpers/ucanto-test-utils.js @@ -35,6 +35,7 @@ export function createTesterFromContext(createContext, options) { const connection = context.then((ctx) => ctx.conn) const issuer = context.then(({ issuer }) => issuer) const audience = context.then(({ service }) => service) + const service = context.then(({ service }) => service) const miniflare = context.then(({ mf }) => mf) /** * @type {import('../../src/types/ucanto').ServiceInvoke} @@ -44,7 +45,7 @@ export function createTesterFromContext(createContext, options) { const [result] = await conn.execute(invocation) return result } - return { issuer, audience, invoke, miniflare, context, connection } + return { issuer, audience, invoke, miniflare, context, connection, service } } /** diff --git a/packages/access-api/test/helpers/utils.js b/packages/access-api/test/helpers/utils.js index e8bee3b09..494a9788e 100644 --- a/packages/access-api/test/helpers/utils.js +++ b/packages/access-api/test/helpers/utils.js @@ -110,3 +110,26 @@ export async function createSpace(issuer, service, conn, email) { export function isUploadApiStack(stack) { return stack.includes('file:///var/task/upload-api') } + +/** + * @typedef {import('../../src/utils/email').ValidationEmailSend} ValidationEmailSend + * @typedef {import('../../src/utils/email').Email} Email + */ + +/** + * create an Email that is useful for testing + * + * @param {Pick, 'push'>} storage + * @returns {Pick} + */ +export function createEmail(storage) { + const email = { + /** + * @param {ValidationEmailSend} email + */ + async sendValidation(email) { + storage.push(email) + }, + } + return email +} diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 4411208da..d215e5d9c 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -14,8 +14,8 @@ import * as Ucanto from '@ucanto/interface' import { Access, Provider } from '@web3-storage/capabilities' import * as delegationsResponse from '../src/utils/delegations-response.js' import { createProvisions } from '../src/models/provisions.js' -import { Email } from '../src/utils/email.js' import { NON_STANDARD } from '@ipld/dag-ucan/signature' +import { createEmail } from './helpers/utils.js' for (const providerAddHandlerVariant of /** @type {const} */ ([ { @@ -273,23 +273,6 @@ for (const accessApiVariant of /** @type {const} */ ([ * @typedef {import('../src/utils/email.js').ValidationEmailSend} ValidationEmailSend */ -/** - * - * @param {Pick, 'push'>} storage - * @returns {Pick} - */ -export function createEmail(storage) { - const email = { - /** - * @param {ValidationEmailSend} email - */ - async sendValidation(email) { - storage.push(email) - }, - } - return email -} - /** * @typedef {import('@web3-storage/capabilities/types').AccessClaim} AccessClaim * @typedef {import('@web3-storage/capabilities/types').AccessAuthorize} AccessAuthorize diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 1a281233f..7659d3ff5 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -899,7 +899,9 @@ export class Agent { async invoke(cap, options) { const space = options.with || this.currentSpace() if (!space) { - throw new Error('No space selected, you need pass a resource.') + throw new Error( + 'No space or resource selected, you need pass a resource.' + ) } const extraProofs = options.proofs || [] From 5c2bc7154f0b26415639df613fc7dda8334e40d9 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 09:49:23 -0700 Subject: [PATCH 15/54] feat: @web3-storage/capabilities depends on latest ucanto (#541) Motivation: * do the upgrade so that we can merge https://github.com/web3-storage/w3protocol/pull/539 after (which requires the upgrade) --- packages/access-api/package.json | 8 +- .../access-api/src/routes/validate-email.js | 8 +- .../src/service/access-authorize.js | 3 +- .../access-api/test/access-authorize.test.js | 5 +- packages/access-api/test/store-list.js | 81 -------------- packages/access-api/test/ucan.test.js | 20 ---- .../access-api/test/validate-email.test.js | 2 +- packages/access-client/package.json | 8 +- packages/capabilities/package.json | 6 +- packages/upload-api/package.json | 12 +- packages/upload-client/package.json | 4 +- pnpm-lock.yaml | 104 ++++++++++-------- 12 files changed, 86 insertions(+), 175 deletions(-) diff --git a/packages/access-api/package.json b/packages/access-api/package.json index 34053dc01..f3602a7ad 100644 --- a/packages/access-api/package.json +++ b/packages/access-api/package.json @@ -18,12 +18,12 @@ "license": "(Apache-2.0 OR MIT)", "dependencies": { "@ipld/dag-ucan": "^3.2.0", - "@ucanto/core": "^5.1.0", - "@ucanto/interface": "^6.0.0", + "@ucanto/core": "^5.2.0", + "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", - "@ucanto/server": "^6.0.0", + "@ucanto/server": "^6.1.0", "@ucanto/transport": "^5.1.0", - "@ucanto/validator": "^6.0.0", + "@ucanto/validator": "^6.1.0", "@web3-storage/access": "workspace:^", "@web3-storage/capabilities": "workspace:^", "@web3-storage/worker-utils": "0.4.3-dev", diff --git a/packages/access-api/src/routes/validate-email.js b/packages/access-api/src/routes/validate-email.js index ea3d8b199..44a523858 100644 --- a/packages/access-api/src/routes/validate-email.js +++ b/packages/access-api/src/routes/validate-email.js @@ -152,7 +152,7 @@ async function authorize(req, env) { if (confirmation.error) { throw new Error(`unable to validate access session: ${confirmation}`, { - cause: confirmation.error, + cause: confirmation, }) } @@ -168,12 +168,12 @@ async function authorize(req, env) { } ) const confirmResult = await confirm(request, { - id: env.signer.verifier, + id: env.signer, principal: Verifier, }) if (confirmResult.error) { throw new Error('error confirming', { - cause: confirmResult.error, + cause: confirmResult, }) } const { account, agent } = accessConfirm.parse(request) @@ -193,6 +193,8 @@ async function authorize(req, env) { ) ) } catch (error) { + // eslint-disable-next-line no-console + console.warn('error in validate-email', error) const err = /** @type {Error} */ (error) env.log.error(err) return new HtmlResponse( diff --git a/packages/access-api/src/service/access-authorize.js b/packages/access-api/src/service/access-authorize.js index b88265852..8ce7807c0 100644 --- a/packages/access-api/src/service/access-authorize.js +++ b/packages/access-api/src/service/access-authorize.js @@ -1,7 +1,6 @@ import * as Server from '@ucanto/server' import * as Access from '@web3-storage/capabilities/access' import * as Mailto from '../utils/did-mailto.js' -import * as DID from '@ipld/dag-ucan/did' import { delegationToString } from '@web3-storage/access/encoding' /** @@ -25,7 +24,7 @@ export function accessAuthorizeProvider(ctx) { const confirmation = await Access.confirm .invoke({ issuer: ctx.signer, - audience: DID.parse(capability.nb.iss), + audience: ctx.signer, // Because with is set to our DID no other actor will be able to issue // this delegation without our private key. with: ctx.signer.did(), diff --git a/packages/access-api/test/access-authorize.test.js b/packages/access-api/test/access-authorize.test.js index 50d3820cc..111858ded 100644 --- a/packages/access-api/test/access-authorize.test.js +++ b/packages/access-api/test/access-authorize.test.js @@ -73,7 +73,7 @@ describe('access/authorize', function () { ) const delegation = stringToDelegation(encoded) t.deepEqual(delegation.issuer.did(), service.did()) - t.deepEqual(delegation.audience.did(), accountDID) + t.deepEqual(delegation.audience.did(), service.did()) t.deepEqual(delegation.capabilities, [ { with: conn.id.did(), @@ -122,8 +122,9 @@ describe('access/authorize', function () { const url = new URL(email.url) const rsp = await mf.dispatchFetch(url, { method: 'POST' }) - const html = await rsp.text() + assert.deepEqual(rsp.status, 200) + const html = await rsp.text() assert(html.includes('Email Validated')) assert(html.includes(toEmail(accountDID))) assert(html.includes(issuer.did())) diff --git a/packages/access-api/test/store-list.js b/packages/access-api/test/store-list.js index 83bb01fe8..d133e2fd5 100644 --- a/packages/access-api/test/store-list.js +++ b/packages/access-api/test/store-list.js @@ -69,85 +69,4 @@ describe('proxy store/list invocations to upload-api', function () { assert.ok(!result?.error, 'should not be an error') }) } - - it('errors when a bad delegation is given as proof', async () => { - const mockUpstream = createMockUploadApiServer({ - // eslint-disable-next-line unicorn/no-await-expression-member - id: await ed25519.generate(), - }) - const mockUpstreamHttp = nodeHttp.createServer( - ucantoServerNodeListener(mockUpstream) - ) - await new Promise((resolve, reject) => - // eslint-disable-next-line unicorn/no-useless-undefined - mockUpstreamHttp.listen(0, () => resolve(undefined)) - ) - // now mockUpstreamHttp is listening on a port. If something goes wrong, we will close the server to have it stop litening - after(() => { - mockUpstreamHttp.close() - }) - const mockUpstreamUrl = serverLocalUrl(mockUpstreamHttp.address()) - const [alice, bob, mallory] = await Promise.all( - Array.from({ length: 3 }).map(() => ed25519.Signer.generate()) - ) - const { service: serviceSigner, conn } = await context({ - env: { - UPLOAD_API_URL: mockUpstreamUrl.toString(), - }, - }) - const service = process.env.DID - ? serviceSigner.withDID(ucanto.DID.parse(process.env.DID).did()) - : serviceSigner - const spaceCreation = await createSpace( - alice, - service, - conn, - 'space-info@dag.house' - ) - /** - * @type {Array<{ - * invocation: import('@ucanto/interface').IssuedInvocationView - * resultAssertion: (r: import('@ucanto/interface').Result) => void - * }>} */ - const cases = [ - { - invocation: Store.list.invoke({ - issuer: mallory, - audience: service, - proofs: [ - // this shouldn't work because the audience is bob, - // but its a proof an an invocation issued by mallory - await Store.list.delegate({ - issuer: alice, - audience: bob, - with: spaceCreation.space.did(), - }), - ], - with: spaceCreation.space.did(), - nb: {}, - }), - resultAssertion(result) { - assert.ok(result.error, 'result is an error') - assert.ok('name' in result, 'result has a name') - assert.equal(result.name, 'InvalidAudience') - assert.ok( - 'stack' in result && typeof result.stack === 'string', - 'result has stack string' - ) - }, - }, - ] - for (const { invocation, resultAssertion } of cases) { - const result = await invocation.execute( - /** @type {import('@ucanto/interface').ConnectionView} */ (conn) - ) - try { - resultAssertion(result) - } catch (error) { - // eslint-disable-next-line no-console - console.warn('result failed assertion', result) - throw error - } - } - }) }) diff --git a/packages/access-api/test/ucan.test.js b/packages/access-api/test/ucan.test.js index c8cfc69ee..9a492974c 100644 --- a/packages/access-api/test/ucan.test.js +++ b/packages/access-api/test/ucan.test.js @@ -75,26 +75,6 @@ describe('ucan', function () { ]) }) - test('should fail with bad service audience', async function () { - const { mf, issuer } = ctx - - const audience = await Signer.generate() - const ucan = await UCAN.issue({ - issuer, - audience, - // @ts-ignore - capabilities: [], - }) - const res = await mf.dispatchFetch('http://localhost:8787/raw', { - method: 'POST', - headers: { - Authorization: `Bearer ${UCAN.format(ucan)}`, - }, - }) - const rsp = await res.json() - t.deepEqual(rsp[0].name, 'InvalidAudience') - }) - test('should fail with with more than 1 cap', async function () { const { mf, service, issuer } = ctx diff --git a/packages/access-api/test/validate-email.test.js b/packages/access-api/test/validate-email.test.js index fd1eaeda7..0d98c2a02 100644 --- a/packages/access-api/test/validate-email.test.js +++ b/packages/access-api/test/validate-email.test.js @@ -14,7 +14,7 @@ describe('validate-email', () => { const extraBytes = getRandomValues(new Uint8Array(10 * 1024)) const ucan = await Delegation.delegate({ issuer: service, - audience: agent, + audience: service, capabilities: [ Access.confirm.create({ with: service.did(), diff --git a/packages/access-client/package.json b/packages/access-client/package.json index 883b6ebd7..2af62118e 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -62,11 +62,11 @@ "@ipld/dag-cbor": "^9.0.0", "@ipld/dag-ucan": "^3.2.0", "@ucanto/client": "^5.1.0", - "@ucanto/core": "^5.1.0", - "@ucanto/interface": "^6.0.0", + "@ucanto/core": "^5.2.0", + "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", "@ucanto/transport": "^5.1.0", - "@ucanto/validator": "^6.0.0", + "@ucanto/validator": "^6.1.0", "@web3-storage/capabilities": "workspace:^", "bigint-mod-arith": "^3.1.2", "conf": "10.2.0", @@ -91,7 +91,7 @@ "@types/node": "^18.11.18", "@types/varint": "^6.0.1", "@types/ws": "^8.5.4", - "@ucanto/server": "^6.0.0", + "@ucanto/server": "^6.1.0", "assert": "^2.0.0", "delay": "^5.0.0", "hd-scripts": "^4.0.0", diff --git a/packages/capabilities/package.json b/packages/capabilities/package.json index ce4a5d5e5..7692eec6c 100644 --- a/packages/capabilities/package.json +++ b/packages/capabilities/package.json @@ -64,11 +64,11 @@ "dist/src/**/*.d.ts.map" ], "dependencies": { - "@ucanto/core": "^5.1.0", - "@ucanto/interface": "^6.0.0", + "@ucanto/core": "^5.2.0", + "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", "@ucanto/transport": "^5.1.0", - "@ucanto/validator": "^6.0.0" + "@ucanto/validator": "^6.1.0" }, "devDependencies": { "@types/assert": "^1.5.6", diff --git a/packages/upload-api/package.json b/packages/upload-api/package.json index 1f086bef3..48112fac4 100644 --- a/packages/upload-api/package.json +++ b/packages/upload-api/package.json @@ -59,11 +59,11 @@ }, "dependencies": { "@ucanto/client": "^5.1.0", - "@ucanto/interface": "^6.0.0", + "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", - "@ucanto/server": "^6.0.0", + "@ucanto/server": "^6.1.0", "@ucanto/transport": "^5.1.0", - "@web3-storage/capabilities": "^3.0.0", + "@web3-storage/capabilities": "workspace:^", "multiformats": "^11.0.1", "p-retry": "^5.1.2" }, @@ -71,10 +71,10 @@ "@ipld/car": "^5.0.1", "@types/mocha": "^10.0.1", "@ucanto/core": "^5.1.0", - "@web3-storage/sigv4": "^1.0.2", "@web-std/blob": "^3.0.4", - "mocha": "^10.2.0", - "hd-scripts": "^4.1.0" + "@web3-storage/sigv4": "^1.0.2", + "hd-scripts": "^4.1.0", + "mocha": "^10.2.0" }, "eslintConfig": { "extends": [ diff --git a/packages/upload-client/package.json b/packages/upload-client/package.json index 44a5ee183..9f1c2c4e0 100644 --- a/packages/upload-client/package.json +++ b/packages/upload-client/package.json @@ -67,7 +67,7 @@ "@ipld/dag-ucan": "^3.2.0", "@ipld/unixfs": "^2.0.1", "@ucanto/client": "^5.1.0", - "@ucanto/interface": "^6.0.0", + "@ucanto/interface": "^6.2.0", "@ucanto/transport": "^5.1.0", "@web3-storage/capabilities": "workspace:^", "multiformats": "^11.0.1", @@ -78,7 +78,7 @@ "@types/assert": "^1.5.6", "@types/mocha": "^10.0.1", "@ucanto/principal": "^5.1.0", - "@ucanto/server": "^6.0.0", + "@ucanto/server": "^6.1.0", "assert": "^2.0.0", "blockstore-core": "^3.0.0", "c8": "^7.12.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 053c62024..fb701471f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,12 +42,12 @@ importers: '@types/node': ^18.11.18 '@types/qrcode': ^1.5.0 '@ucanto/client': ^5.1.0 - '@ucanto/core': ^5.1.0 - '@ucanto/interface': ^6.0.0 + '@ucanto/core': ^5.2.0 + '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 - '@ucanto/server': ^6.0.0 + '@ucanto/server': ^6.1.0 '@ucanto/transport': ^5.1.0 - '@ucanto/validator': ^6.0.0 + '@ucanto/validator': ^6.1.0 '@web3-storage/access': workspace:^ '@web3-storage/capabilities': workspace:^ '@web3-storage/worker-utils': 0.4.3-dev @@ -77,12 +77,12 @@ importers: wrangler: ^2.8.0 dependencies: '@ipld/dag-ucan': 3.2.0 - '@ucanto/core': 5.1.0 - '@ucanto/interface': 6.0.0 + '@ucanto/core': 5.2.0 + '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 - '@ucanto/server': 6.0.0 + '@ucanto/server': 6.1.0 '@ucanto/transport': 5.1.0 - '@ucanto/validator': 6.0.0 + '@ucanto/validator': 6.1.0 '@web3-storage/access': link:../access-client '@web3-storage/capabilities': link:../capabilities '@web3-storage/worker-utils': 0.4.3-dev @@ -134,12 +134,12 @@ importers: '@types/varint': ^6.0.1 '@types/ws': ^8.5.4 '@ucanto/client': ^5.1.0 - '@ucanto/core': ^5.1.0 - '@ucanto/interface': ^6.0.0 + '@ucanto/core': ^5.2.0 + '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 - '@ucanto/server': ^6.0.0 + '@ucanto/server': ^6.1.0 '@ucanto/transport': ^5.1.0 - '@ucanto/validator': ^6.0.0 + '@ucanto/validator': ^6.1.0 '@web3-storage/capabilities': workspace:^ assert: ^2.0.0 bigint-mod-arith: ^3.1.2 @@ -171,11 +171,11 @@ importers: '@ipld/dag-cbor': 9.0.0 '@ipld/dag-ucan': 3.2.0 '@ucanto/client': 5.1.0 - '@ucanto/core': 5.1.0 - '@ucanto/interface': 6.0.0 + '@ucanto/core': 5.2.0 + '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 '@ucanto/transport': 5.1.0 - '@ucanto/validator': 6.0.0 + '@ucanto/validator': 6.1.0 '@web3-storage/capabilities': link:../capabilities bigint-mod-arith: 3.1.2 conf: 10.2.0 @@ -199,7 +199,7 @@ importers: '@types/node': 18.11.18 '@types/varint': 6.0.1 '@types/ws': 8.5.4 - '@ucanto/server': 6.0.0 + '@ucanto/server': 6.1.0 assert: 2.0.0 delay: 5.0.0 hd-scripts: 4.0.0 @@ -259,11 +259,11 @@ importers: '@types/assert': ^1.5.6 '@types/mocha': ^10.0.0 '@types/node': ^18.11.18 - '@ucanto/core': ^5.1.0 - '@ucanto/interface': ^6.0.0 + '@ucanto/core': ^5.2.0 + '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 '@ucanto/transport': ^5.1.0 - '@ucanto/validator': ^6.0.0 + '@ucanto/validator': ^6.1.0 assert: ^2.0.0 hd-scripts: ^4.0.0 mocha: ^10.2.0 @@ -272,11 +272,11 @@ importers: typescript: 4.9.5 watch: ^1.0.2 dependencies: - '@ucanto/core': 5.1.0 - '@ucanto/interface': 6.0.0 + '@ucanto/core': 5.2.0 + '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 '@ucanto/transport': 5.1.0 - '@ucanto/validator': 6.0.0 + '@ucanto/validator': 6.1.0 devDependencies: '@types/assert': 1.5.6 '@types/mocha': 10.0.1 @@ -295,12 +295,12 @@ importers: '@types/mocha': ^10.0.1 '@ucanto/client': ^5.1.0 '@ucanto/core': ^5.1.0 - '@ucanto/interface': ^6.0.0 + '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 - '@ucanto/server': ^6.0.0 + '@ucanto/server': ^6.1.0 '@ucanto/transport': ^5.1.0 '@web-std/blob': ^3.0.4 - '@web3-storage/capabilities': ^3.0.0 + '@web3-storage/capabilities': workspace:^ '@web3-storage/sigv4': ^1.0.2 hd-scripts: ^4.1.0 mocha: ^10.2.0 @@ -308,9 +308,9 @@ importers: p-retry: ^5.1.2 dependencies: '@ucanto/client': 5.1.0 - '@ucanto/interface': 6.0.0 + '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 - '@ucanto/server': 6.0.0 + '@ucanto/server': 6.1.0 '@ucanto/transport': 5.1.0 '@web3-storage/capabilities': link:../capabilities multiformats: 11.0.1 @@ -332,9 +332,9 @@ importers: '@types/assert': ^1.5.6 '@types/mocha': ^10.0.1 '@ucanto/client': ^5.1.0 - '@ucanto/interface': ^6.0.0 + '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 - '@ucanto/server': ^6.0.0 + '@ucanto/server': ^6.1.0 '@ucanto/transport': ^5.1.0 '@web3-storage/capabilities': workspace:^ assert: ^2.0.0 @@ -355,7 +355,7 @@ importers: '@ipld/dag-ucan': 3.2.0 '@ipld/unixfs': 2.0.1 '@ucanto/client': 5.1.0 - '@ucanto/interface': 6.0.0 + '@ucanto/interface': 6.2.0 '@ucanto/transport': 5.1.0 '@web3-storage/capabilities': link:../capabilities multiformats: 11.0.1 @@ -365,7 +365,7 @@ importers: '@types/assert': 1.5.6 '@types/mocha': 10.0.1 '@ucanto/principal': 5.1.0 - '@ucanto/server': 6.0.0 + '@ucanto/server': 6.1.0 assert: 2.0.0 blockstore-core: 3.0.0 c8: 7.12.0 @@ -3560,7 +3560,7 @@ packages: /@ucanto/client/5.1.0: resolution: {integrity: sha512-Rr2q3ARDmiaaVnvNkPNsIhS+1ORyCqQhGRSqf8ugfJVmvbvjLFA6EYxjknUdg5tqt0aVnYTNuZ/GwIxaqMzliA==} dependencies: - '@ucanto/interface': 6.0.0 + '@ucanto/interface': 6.2.0 multiformats: 11.0.1 /@ucanto/core/5.1.0: @@ -3569,11 +3569,21 @@ packages: '@ipld/car': 5.1.0 '@ipld/dag-cbor': 9.0.0 '@ipld/dag-ucan': 3.2.0 - '@ucanto/interface': 6.0.0 + '@ucanto/interface': 6.2.0 multiformats: 11.0.1 + dev: true - /@ucanto/interface/6.0.0: - resolution: {integrity: sha512-OOZQDLKxqhuvmSRAXcHDQrlBBDs52ZiI5GsGJJW8VT/TPOPmfAkwc1HgmU/eMF4bO+a1JfG4i43+4KUoCcQnbA==} + /@ucanto/core/5.2.0: + resolution: {integrity: sha512-Eblo2LfJyojRKmBk5/w25u1hhSCs6K3zUH/zNknwTrJg7CJYxw0hgsGcXrlkQf1TnSRzJVFEduK1ZzYCV55/Uw==} + dependencies: + '@ipld/car': 5.1.0 + '@ipld/dag-cbor': 9.0.0 + '@ipld/dag-ucan': 3.2.0 + '@ucanto/interface': 6.2.0 + multiformats: 11.0.1 + + /@ucanto/interface/6.2.0: + resolution: {integrity: sha512-b37bjTxNWQE+O4f18fvb7/woe41Dvb4AfdbevPLmaJj1fZogssH9fVgWlZdVg8ZsJQhMxRyHDuH40QAvuKRR1w==} dependencies: '@ipld/dag-ucan': 3.2.0 multiformats: 11.0.1 @@ -3583,34 +3593,34 @@ packages: dependencies: '@ipld/dag-ucan': 3.2.0 '@noble/ed25519': 1.7.3 - '@ucanto/interface': 6.0.0 + '@ucanto/interface': 6.2.0 multiformats: 11.0.1 one-webcrypto: 1.0.3 - /@ucanto/server/6.0.0: - resolution: {integrity: sha512-xwZa3S8bclGn3JxuxUIQ+TOZKeAhbSDJEk7B3ZgHUu0bYBBFEyRbAVD177ZpAnTuvj8Et68g1tRzNIStQPMvSA==} + /@ucanto/server/6.1.0: + resolution: {integrity: sha512-IRvYbv1iEAjgm8Rc4hqUPm2NGSU+R+X+wyHp3hUI/YdGji+bajghV4gU9klkDkgm0aqVuI1fDFyODWZb5UROtw==} dependencies: - '@ucanto/core': 5.1.0 - '@ucanto/interface': 6.0.0 - '@ucanto/validator': 6.0.0 + '@ucanto/core': 5.2.0 + '@ucanto/interface': 6.2.0 + '@ucanto/validator': 6.1.0 /@ucanto/transport/5.1.0: resolution: {integrity: sha512-Um/ba0DmTM1icZW08pxg7hyV1BYXGMyWEeVAnrM3W/WnrYd+JTEFoaPrbPce//Dkmrfk8uGjuIIgjpPT1eAfUA==} dependencies: '@ipld/car': 5.1.0 '@ipld/dag-cbor': 9.0.0 - '@ucanto/core': 5.1.0 - '@ucanto/interface': 6.0.0 + '@ucanto/core': 5.2.0 + '@ucanto/interface': 6.2.0 multiformats: 11.0.1 dev: false - /@ucanto/validator/6.0.0: - resolution: {integrity: sha512-3cWy8F7G1DFGeHXHWegcvQ0sz+t8hvmAOD919a57n87qDauoYDB90s3p9c71esfOwarBDr2f8/oEFwtToAjy3A==} + /@ucanto/validator/6.1.0: + resolution: {integrity: sha512-vZ40paByLgosllG+YfuI4eD7m3KyYG1ebEa9jZEkLDYjWh7WWBtYvBn40pziIiLfBCzum2zU1uP1SMOf63EqqQ==} dependencies: '@ipld/car': 5.1.0 '@ipld/dag-cbor': 9.0.0 - '@ucanto/core': 5.1.0 - '@ucanto/interface': 6.0.0 + '@ucanto/core': 5.2.0 + '@ucanto/interface': 6.2.0 multiformats: 11.0.1 /@vue/compiler-core/3.2.45: From 2156f23e7dad67348d3ab9dcb867698f170770aa Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Wed, 15 Mar 2023 10:20:36 -0700 Subject: [PATCH 16/54] feat: use new ucanto allows api for `canDelegateCapability` and implement space registration (#539) * use the new `allows` function in `ucanto` to reimplemeent `canDelegateCapability` * move space registration from `newCreateSpace` to `registerSpace` where it belongs * use agent's current account in registerSpace rather than requiring an email --------- Co-authored-by: Benjamin Goering <171782+gobengo@users.noreply.github.com> --- packages/access-client/src/agent-data.js | 2 +- packages/access-client/src/agent.js | 156 ++++++------------ packages/access-client/src/delegations.js | 21 ++- packages/access-client/src/types.ts | 2 +- .../access-client/test/agent-data.test.js | 2 +- 5 files changed, 64 insertions(+), 119 deletions(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index b87a83184..7be6bf234 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -127,7 +127,7 @@ export class AgentData { } /** - * @param {import('@ucanto/interface').DID} did + * @param {import('@ucanto/interface').DID<'key'>} did */ async setCurrentSpace(did) { this.currentSpace = did diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 7659d3ff5..6992896bd 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -10,7 +10,6 @@ import * as ucanto from '@ucanto/core' import { URI } from '@ucanto/validator' import { Peer } from './awake/peer.js' import * as Space from '@web3-storage/capabilities/space' -import * as Voucher from '@web3-storage/capabilities/voucher' import * as Access from '@web3-storage/capabilities/access' import * as Provider from '@web3-storage/capabilities/provider' @@ -43,43 +42,49 @@ function emailToSessionPrincipal(email) { } /** - * @param {Ucanto.Signer>} space + * @param {Ucanto.Signer>} issuer + * @param {Ucanto.DID} space * @param {Ucanto.Principal>} account * @returns */ -async function createSpaceSaysAccountCanAdminSpace(space, account) { +async function createIssuerSaysAccountCanAdminSpace(issuer, space, account) { return ucanto.delegate({ - issuer: space, + issuer, audience: account, capabilities: [ { can: 'space/*', - with: space.did(), + with: space, }, { can: 'store/*', - with: space.did(), + with: space, }, { can: 'upload/*', - with: space.did(), + with: space, }, ], }) } /** - * @param {Ucanto.Signer>} space + * @param {Ucanto.Signer>} issuer + * @param {Ucanto.DID} space * @param {Ucanto.Principal>} device */ -async function createSpaceSaysDeviceCanAccessDelegateWithSpace(space, device) { +async function createIssuerSaysDeviceCanAccessDelegateWithSpace( + issuer, + space, + device +) { return ucanto.delegate({ - issuer: space, + issuer, audience: device, capabilities: [ { can: 'access/delegate', - with: space.did(), + with: space, }, ], }) @@ -319,42 +324,6 @@ export class Agent { return arr } - /** - * Creates a space signer and a delegation to the agent - * - * @param {string} [name] - */ - async newCreateSpace(name) { - const signer = await Signer.generate() - const proof = await Space.top.delegate({ - issuer: signer, - audience: this.issuer, - with: signer.did(), - expiration: Infinity, - }) - - await this.addProvider(signer) - await this.delegateSpaceAccessToAccount(signer) - - /** @type {import('./types').SpaceMeta} */ - const meta = { isRegistered: true } - // eslint-disable-next-line eqeqeq - if (name != undefined) { - if (typeof name !== 'string') { - throw new TypeError('invalid name') - } - meta.name = name - } - - await this.#data.addSpace(signer.did(), meta, proof) - - return { - did: signer.did(), - meta, - proof, - } - } - /** * Creates a space signer and a delegation to the agent * @@ -466,7 +435,7 @@ export class Agent { * * Other methods will default to use the current space if no resource is defined * - * @param {Ucanto.DID} space + * @param {Ucanto.DID<'key'>} space */ async setCurrentSpace(space) { const proofs = this.proofs([ @@ -561,6 +530,10 @@ export class Agent { await this.addProof(sessionDelegation) this.#data.setSessionPrincipal(sessionPrincipal) + + // claim delegations here because we will need an ucan/attest from the service to + // pair with the session delegation we just claimed to make it work + await this.claimDelegations() } async claimDelegations() { @@ -603,8 +576,7 @@ export class Agent { } /** - * - * @param {Signer.EdSigner} space - TODO is this type correct? + * @param {Ucanto.DID<'key'>} space */ async addProvider(space) { const sessionPrincipal = this.#data.sessionPrincipal @@ -613,7 +585,7 @@ export class Agent { throw new Error('cannot add provider, please authorize first') } - return await this.invokeAndExecute(Provider.add, { + return this.invokeAndExecute(Provider.add, { audience: this.connection.id, with: sessionPrincipal.did(), proofs: this.proofs([ @@ -625,14 +597,14 @@ export class Agent { nb: { // TODO probably need to make it possible to pass other providers in provider: 'did:web:staging.web3.storage', - consumer: space.did(), + consumer: space, }, }) } /** * - * @param {Signer.EdSigner} space - TODO is this type correct? + * @param {Ucanto.DID<'key'>} space */ async delegateSpaceAccessToAccount(space) { const sessionPrincipal = this.#data.sessionPrincipal @@ -644,10 +616,14 @@ export class Agent { } const spaceSaysAccountCanAdminSpace = - await createSpaceSaysAccountCanAdminSpace(space, sessionPrincipal) - return await this.invokeAndExecute(Access.delegate, { + await createIssuerSaysAccountCanAdminSpace( + this.issuer, + space, + sessionPrincipal + ) + return this.invokeAndExecute(Access.delegate, { audience: this.connection.id, - with: space.did(), + with: space, expiration: Infinity, nb: { delegations: { @@ -656,7 +632,8 @@ export class Agent { }, }, proofs: [ - await createSpaceSaysDeviceCanAccessDelegateWithSpace( + await createIssuerSaysDeviceCanAccessDelegateWithSpace( + this.issuer, space, this.issuer ), @@ -671,13 +648,11 @@ export class Agent { * * It also adds a full space delegation to the service in the voucher/claim invocation to allow for recovery * - * @param {string} email * @param {object} [opts] * @param {AbortSignal} [opts.signal] */ - async registerSpace(email, opts) { + async registerSpace(opts) { const space = this.currentSpace() - const service = this.connection.id const spaceMeta = space ? this.#data.spaces.get(space) : undefined if (!space || !spaceMeta) { @@ -687,58 +662,23 @@ export class Agent { if (spaceMeta && spaceMeta.isRegistered) { throw new Error('Space already registered with web3.storage.') } - - const inv = await this.invokeAndExecute(Voucher.claim, { - nb: { - identity: URI.from(`mailto:${email}`), - product: 'product:free', - service: service.did(), - }, - }) - - if (inv && inv.error) { - throw new Error('Voucher claim failed', { cause: inv }) + const providerResult = await this.addProvider( + /** @type {Ucanto.DID<'key'>} */ (space) + ) + if (providerResult.error) { + throw new Error(providerResult.message, { cause: providerResult }) } - - const voucherRedeem = - /** @type {Ucanto.Delegation<[import('./types').VoucherRedeem]>} */ ( - await this.#waitForDelegation(opts) - ) - await this.addProof(voucherRedeem) - const delegationToService = await this.delegate({ - abilities: ['*'], - audience: service, - expiration: Infinity, - audienceMeta: { - name: 'w3access', - type: 'service', - }, - }) - - const accInv = await this.invokeAndExecute(Voucher.redeem, { - with: URI.from(service.did()), - nb: { - space, - identity: voucherRedeem.capabilities[0].nb.identity, - product: voucherRedeem.capabilities[0].nb.product, - }, - proofs: [delegationToService], - facts: [ - { - space: spaceMeta, - agent: this.meta, - }, - ], - }) - - if (accInv && accInv.error) { - throw new Error('Space registration failed', { cause: accInv }) + const delegateSpaceAccessResult = await this.delegateSpaceAccessToAccount( + space + ) + if (delegateSpaceAccessResult.error) { + // @ts-ignore it's very weird that this is throwing an error but line 692 above does not - ignore for now + throw new Error(delegateSpaceAccessResult.message, { + cause: delegateSpaceAccessResult, + }) } - spaceMeta.isRegistered = true - this.#data.addSpace(space, spaceMeta) - this.#data.removeDelegation(voucherRedeem.cid) } /** diff --git a/packages/access-client/src/delegations.js b/packages/access-client/src/delegations.js index 3401aad5f..3b26fa8e3 100644 --- a/packages/access-client/src/delegations.js +++ b/packages/access-client/src/delegations.js @@ -1,6 +1,7 @@ // @ts-ignore // eslint-disable-next-line no-unused-vars import * as Ucanto from '@ucanto/interface' +import * as ucanto from '@ucanto/core' import { canDelegateAbility } from '@web3-storage/capabilities/utils' /** @@ -44,7 +45,9 @@ export function validate(delegation, opts) { } = opts ?? {} if (checkAudience && delegation.audience.did() !== checkAudience.did()) { - throw new Error(`Delegation audience does not match required DID.`) + throw new Error( + `Delegation audience ${delegation.audience.did()} does not match required DID ${checkAudience.did()}` + ) } if (checkIsExpired && isExpired(delegation)) { @@ -62,13 +65,15 @@ export function validate(delegation, opts) { * @param {import('@ucanto/interface').Capability} child */ export function canDelegateCapability(delegation, child) { - for (const parent of delegation.capabilities) { - // TODO is this right? - if ( - (parent.with === child.with || parent.with === 'ucan:*') && - canDelegateAbility(parent.can, child.can) - ) { - return true + const allowsCapabilities = ucanto.Delegation.allows(delegation) + if (allowsCapabilities[child.with]) { + const cans = /** @type {import('@ucanto/interface').Ability[]} */ ( + Object.keys(allowsCapabilities[child.with]) + ) + for (const can of cans) { + if (canDelegateAbility(can, child.can)) { + return true + } } } return false diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index 3f2fd70d0..15be458cd 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -171,7 +171,7 @@ export interface AgentDataModel { meta: AgentMeta principal: Signer> sessionPrincipal?: Principal> - currentSpace?: DID + currentSpace?: DID<'key'> spaces: Map delegations: Map } diff --git a/packages/access-client/test/agent-data.test.js b/packages/access-client/test/agent-data.test.js index cee8eca04..152d29308 100644 --- a/packages/access-client/test/agent-data.test.js +++ b/packages/access-client/test/agent-data.test.js @@ -21,6 +21,6 @@ describe('AgentData', () => { } const store = new Store() const data = await AgentData.create(undefined, { store }) - await assert.doesNotReject(data.setCurrentSpace('did:x:y')) + await assert.doesNotReject(data.setCurrentSpace('did:key:y')) }) }) From 6e89c4261739264cbdb4b48966054ab7a389237a Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:44:55 -0700 Subject: [PATCH 17/54] fix: upgrade to ucanto/transport@5.1.1 (#544) --- packages/access-api/package.json | 2 +- packages/access-client/package.json | 2 +- packages/capabilities/package.json | 2 +- packages/upload-api/package.json | 2 +- packages/upload-client/package.json | 2 +- pnpm-lock.yaml | 54 ++++++++++++++++++----------- 6 files changed, 39 insertions(+), 25 deletions(-) diff --git a/packages/access-api/package.json b/packages/access-api/package.json index f3602a7ad..e6ef5dcaf 100644 --- a/packages/access-api/package.json +++ b/packages/access-api/package.json @@ -22,7 +22,7 @@ "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", "@ucanto/server": "^6.1.0", - "@ucanto/transport": "^5.1.0", + "@ucanto/transport": "^5.1.1", "@ucanto/validator": "^6.1.0", "@web3-storage/access": "workspace:^", "@web3-storage/capabilities": "workspace:^", diff --git a/packages/access-client/package.json b/packages/access-client/package.json index 2af62118e..2f1f398d9 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -65,7 +65,7 @@ "@ucanto/core": "^5.2.0", "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", - "@ucanto/transport": "^5.1.0", + "@ucanto/transport": "^5.1.1", "@ucanto/validator": "^6.1.0", "@web3-storage/capabilities": "workspace:^", "bigint-mod-arith": "^3.1.2", diff --git a/packages/capabilities/package.json b/packages/capabilities/package.json index 7692eec6c..9de3bad5b 100644 --- a/packages/capabilities/package.json +++ b/packages/capabilities/package.json @@ -67,7 +67,7 @@ "@ucanto/core": "^5.2.0", "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", - "@ucanto/transport": "^5.1.0", + "@ucanto/transport": "^5.1.1", "@ucanto/validator": "^6.1.0" }, "devDependencies": { diff --git a/packages/upload-api/package.json b/packages/upload-api/package.json index 48112fac4..935bb668b 100644 --- a/packages/upload-api/package.json +++ b/packages/upload-api/package.json @@ -62,7 +62,7 @@ "@ucanto/interface": "^6.2.0", "@ucanto/principal": "^5.1.0", "@ucanto/server": "^6.1.0", - "@ucanto/transport": "^5.1.0", + "@ucanto/transport": "^5.1.1", "@web3-storage/capabilities": "workspace:^", "multiformats": "^11.0.1", "p-retry": "^5.1.2" diff --git a/packages/upload-client/package.json b/packages/upload-client/package.json index 9f1c2c4e0..26c931af8 100644 --- a/packages/upload-client/package.json +++ b/packages/upload-client/package.json @@ -68,7 +68,7 @@ "@ipld/unixfs": "^2.0.1", "@ucanto/client": "^5.1.0", "@ucanto/interface": "^6.2.0", - "@ucanto/transport": "^5.1.0", + "@ucanto/transport": "^5.1.1", "@web3-storage/capabilities": "workspace:^", "multiformats": "^11.0.1", "p-queue": "^7.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb701471f..885fb9056 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 '@ucanto/server': ^6.1.0 - '@ucanto/transport': ^5.1.0 + '@ucanto/transport': ^5.1.1 '@ucanto/validator': ^6.1.0 '@web3-storage/access': workspace:^ '@web3-storage/capabilities': workspace:^ @@ -81,7 +81,7 @@ importers: '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 '@ucanto/server': 6.1.0 - '@ucanto/transport': 5.1.0 + '@ucanto/transport': 5.1.1 '@ucanto/validator': 6.1.0 '@web3-storage/access': link:../access-client '@web3-storage/capabilities': link:../capabilities @@ -138,7 +138,7 @@ importers: '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 '@ucanto/server': ^6.1.0 - '@ucanto/transport': ^5.1.0 + '@ucanto/transport': ^5.1.1 '@ucanto/validator': ^6.1.0 '@web3-storage/capabilities': workspace:^ assert: ^2.0.0 @@ -174,7 +174,7 @@ importers: '@ucanto/core': 5.2.0 '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 - '@ucanto/transport': 5.1.0 + '@ucanto/transport': 5.1.1 '@ucanto/validator': 6.1.0 '@web3-storage/capabilities': link:../capabilities bigint-mod-arith: 3.1.2 @@ -262,7 +262,7 @@ importers: '@ucanto/core': ^5.2.0 '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 - '@ucanto/transport': ^5.1.0 + '@ucanto/transport': ^5.1.1 '@ucanto/validator': ^6.1.0 assert: ^2.0.0 hd-scripts: ^4.0.0 @@ -275,7 +275,7 @@ importers: '@ucanto/core': 5.2.0 '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 - '@ucanto/transport': 5.1.0 + '@ucanto/transport': 5.1.1 '@ucanto/validator': 6.1.0 devDependencies: '@types/assert': 1.5.6 @@ -298,7 +298,7 @@ importers: '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 '@ucanto/server': ^6.1.0 - '@ucanto/transport': ^5.1.0 + '@ucanto/transport': ^5.1.1 '@web-std/blob': ^3.0.4 '@web3-storage/capabilities': workspace:^ '@web3-storage/sigv4': ^1.0.2 @@ -311,7 +311,7 @@ importers: '@ucanto/interface': 6.2.0 '@ucanto/principal': 5.1.0 '@ucanto/server': 6.1.0 - '@ucanto/transport': 5.1.0 + '@ucanto/transport': 5.1.1 '@web3-storage/capabilities': link:../capabilities multiformats: 11.0.1 p-retry: 5.1.2 @@ -335,7 +335,7 @@ importers: '@ucanto/interface': ^6.2.0 '@ucanto/principal': ^5.1.0 '@ucanto/server': ^6.1.0 - '@ucanto/transport': ^5.1.0 + '@ucanto/transport': ^5.1.1 '@web3-storage/capabilities': workspace:^ assert: ^2.0.0 blockstore-core: ^3.0.0 @@ -356,7 +356,7 @@ importers: '@ipld/unixfs': 2.0.1 '@ucanto/client': 5.1.0 '@ucanto/interface': 6.2.0 - '@ucanto/transport': 5.1.0 + '@ucanto/transport': 5.1.1 '@web3-storage/capabilities': link:../capabilities multiformats: 11.0.1 p-queue: 7.3.0 @@ -1938,7 +1938,7 @@ packages: react: optional: true dependencies: - '@types/react': 18.0.27 + '@types/react': 18.0.28 prop-types: 15.8.1 dev: true @@ -2498,6 +2498,16 @@ packages: multiformats: 11.0.1 varint: 6.0.0 + /@ipld/car/5.1.1: + resolution: {integrity: sha512-HoFTUqUJL9cPGhC9qRmHCvamfIsj1JllQSQ/Xu9/KN/VNJp8To9Ms4qiZPEMOwcrNFclfYqrahjGYbf4KL/d9A==} + engines: {node: '>=16.0.0', npm: '>=7.0.0'} + dependencies: + '@ipld/dag-cbor': 9.0.0 + cborg: 1.10.0 + multiformats: 11.0.2 + varint: 6.0.0 + dev: false + /@ipld/dag-cbor/9.0.0: resolution: {integrity: sha512-zdsiSiYDEOIDW7mmWOYWC9gukjXO+F8wqxz/LfN7iSwTfIyipC8+UQrCbPupFMRb/33XQTZk8yl3My8vUQBRoA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} @@ -2516,7 +2526,7 @@ packages: resolution: {integrity: sha512-8FB/qTlNowCiszL9Sek8xH6xIQxIioXuzZ5B1jVPknQMVkd08nZUHzDjrn1Y6MqJ5PrXWLrBwNghGMWPPpvNVw==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - multiformats: 11.0.1 + multiformats: 11.0.2 /@ipld/dag-ucan/3.2.0: resolution: {integrity: sha512-CTClaGx4F3iEMJgYaYVOVBEvtNXzPc77Mi6p3vBtylSzDWhbf1Gou9ij7PlblOqWKA1H7XI8fp6yweTb6iXNKQ==} @@ -2809,7 +2819,7 @@ packages: resolution: {integrity: sha512-4gCptOviYuu8ts5iUPwAcyIgl1FAyOAtWkQMAdu7FpgWveV5uVmA/919+QhgiZu8lhBGLWvRRTigOEdYNX9y0A==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - multiformats: 11.0.1 + multiformats: 11.0.2 murmurhash3js-revisited: 3.0.0 dev: true @@ -3329,8 +3339,8 @@ packages: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} dev: true - /@types/react/18.0.27: - resolution: {integrity: sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==} + /@types/react/18.0.28: + resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 @@ -3604,14 +3614,14 @@ packages: '@ucanto/interface': 6.2.0 '@ucanto/validator': 6.1.0 - /@ucanto/transport/5.1.0: - resolution: {integrity: sha512-Um/ba0DmTM1icZW08pxg7hyV1BYXGMyWEeVAnrM3W/WnrYd+JTEFoaPrbPce//Dkmrfk8uGjuIIgjpPT1eAfUA==} + /@ucanto/transport/5.1.1: + resolution: {integrity: sha512-5g2Xiofqalvmaw/UPRTdxV2be+KE3TMbFhG4ZNNPkDIBpdBduVENcLQPJ9ksD29Bc2ArIefXALxGzyGLgUPCBg==} dependencies: - '@ipld/car': 5.1.0 + '@ipld/car': 5.1.1 '@ipld/dag-cbor': 9.0.0 '@ucanto/core': 5.2.0 '@ucanto/interface': 6.2.0 - multiformats: 11.0.1 + multiformats: 11.0.2 dev: false /@ucanto/validator/6.1.0: @@ -7798,7 +7808,7 @@ packages: engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: interface-store: 3.0.4 - multiformats: 11.0.1 + multiformats: 11.0.2 dev: true /interface-store/3.0.4: @@ -9089,6 +9099,10 @@ packages: resolution: {integrity: sha512-atWruyH34YiknSdL5yeIir00EDlJRpHzELYQxG7Iy29eCyL+VrZHpPrX5yqlik3jnuqpLpRKVZ0SGVb9UzKaSA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} + /multiformats/11.0.2: + resolution: {integrity: sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==} + engines: {node: '>=16.0.0', npm: '>=7.0.0'} + /multimatch/5.0.0: resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} engines: {node: '>=10'} From dfe264d7e9d992f42e464804571599a2ab415298 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Wed, 15 Mar 2023 12:06:00 -0700 Subject: [PATCH 18/54] feat: expose session principal as account --- packages/access-client/src/agent.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 6992896bd..71bb95de1 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -190,6 +190,10 @@ export class Agent { return this.#data.spaces } + get account() { + return this.#data.sessionPrincipal + } + did() { return this.#data.principal.did() } From 459aa6fe86fc486336e369d1915c034b59c9529b Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 12:17:42 -0700 Subject: [PATCH 19/54] chore: 433 minimize public api (#545) --- packages/access-client/package.json | 19 ++++++++++++++++++- packages/access-client/src/agent-data.js | 24 ++++++++++++++---------- packages/access-client/src/agent.js | 4 ++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/packages/access-client/package.json b/packages/access-client/package.json index 2f1f398d9..a3cba524c 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -113,7 +113,24 @@ "rules": { "unicorn/prefer-number-properties": "off", "unicorn/prefer-export-from": "off", - "unicorn/no-array-reduce": "off" + "unicorn/no-array-reduce": "off", + "jsdoc/no-undefined-types": [ + "error", + { + "definedTypes": [ + "ArrayLike", + "AsyncIterable", + "AsyncIterableIterator", + "Iterable", + "IterableIterator", + "Generator", + "CryptoKeyPair", + "NodeJS", + "ErrorOptions", + "IDBTransactionMode" + ] + } + ] }, "env": { "mocha": true diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 7be6bf234..4d93c21cc 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -1,6 +1,7 @@ import { Signer } from '@ucanto/principal' import { Signer as EdSigner } from '@ucanto/principal/ed25519' import { importDAG } from '@ucanto/core/delegation' +import * as Ucanto from '@ucanto/interface' import { DID } from '@ucanto/core' import { CID } from 'multiformats' import { Access } from '@web3-storage/capabilities' @@ -161,16 +162,19 @@ export class AgentData { this.delegations.delete(cid.toString()) await this.#save(this.export()) } +} - /** - * The current session proof. - */ - sessionProof() { - for (const { delegation } of this.delegations.values()) { - const cap = delegation.capabilities.find( - (c) => c.can === Access.session.can // TODO we should make sure this is the current session proof - we were checking nb.key but that doesn't seem to exist in the staging ucan/attest at the moment - ) - if (cap && !isExpired(delegation)) return delegation - } +/** + * get session proof + * + * @param {AgentData} data + * @returns {Ucanto.Delegation | undefined} + */ +export function getSessionProof(data) { + for (const { delegation } of data.delegations.values()) { + const cap = delegation.capabilities.find( + (c) => c.can === Access.session.can // TODO we should make sure this is the current session proof - we were checking nb.key but that doesn't seem to exist in the staging ucan/attest at the moment + ) + if (cap && !isExpired(delegation)) return delegation } } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 71bb95de1..5864688e2 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -24,7 +24,7 @@ import { validate, canDelegateCapability, } from './delegations.js' -import { AgentData } from './agent-data.js' +import { AgentData, getSessionProof } from './agent-data.js' export { AgentData } @@ -269,7 +269,7 @@ export class Agent { */ proofs(caps) { const arr = [] - const session = this.#data.sessionProof() + const session = getSessionProof(this.#data) let hasSessionDelegations = false for (const { delegation } of this.#delegations(caps)) { From e5e9a431a87507e96af3c6854dad69c8a653465c Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 13:07:25 -0700 Subject: [PATCH 20/54] fix: rm mention of 'sessionPrincipal' from agent and agent-data (#546) Motivation: * https://github.com/web3-storage/w3protocol/pull/433/files#r1137631263 --- packages/access-client/src/agent-data.js | 16 ---- packages/access-client/src/agent.js | 82 ++++++++----------- packages/access-client/src/types.ts | 2 - .../access-client/src/utils/did-mailto.js | 15 ++++ 4 files changed, 47 insertions(+), 68 deletions(-) create mode 100644 packages/access-client/src/utils/did-mailto.js diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 4d93c21cc..f4c981212 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -2,7 +2,6 @@ import { Signer } from '@ucanto/principal' import { Signer as EdSigner } from '@ucanto/principal/ed25519' import { importDAG } from '@ucanto/core/delegation' import * as Ucanto from '@ucanto/interface' -import { DID } from '@ucanto/core' import { CID } from 'multiformats' import { Access } from '@web3-storage/capabilities' import { isExpired } from './delegations.js' @@ -21,7 +20,6 @@ export class AgentData { constructor(data, options = {}) { this.meta = data.meta this.principal = data.principal - this.sessionPrincipal = data.sessionPrincipal this.spaces = data.spaces this.delegations = data.delegations this.currentSpace = data.currentSpace @@ -40,7 +38,6 @@ export class AgentData { { meta: { name: 'agent', type: 'device', ...init.meta }, principal: init.principal ?? (await EdSigner.generate()), - sessionPrincipal: init.sessionPrincipal, spaces: init.spaces ?? new Map(), delegations: init.delegations ?? new Map(), currentSpace: init.currentSpace, @@ -80,10 +77,6 @@ export class AgentData { meta: raw.meta, // @ts-expect-error for some reason TS thinks this is a EdSigner principal: Signer.from(raw.principal), - // @ts-expect-error TODO figure out the types for this too - sessionPrincipal: raw.sessionPrincipal - ? DID.parse(raw.sessionPrincipal) - : undefined, currentSpace: raw.currentSpace, spaces: raw.spaces, delegations: dels, @@ -100,7 +93,6 @@ export class AgentData { const raw = { meta: this.meta, principal: this.principal.toArchive(), - sessionPrincipal: this.sessionPrincipal?.did(), currentSpace: this.currentSpace, spaces: this.spaces, delegations: new Map(), @@ -135,14 +127,6 @@ export class AgentData { await this.#save(this.export()) } - /** - * @param {import('@ucanto/interface').Principal>} principal - */ - async setSessionPrincipal(principal) { - this.sessionPrincipal = principal - await this.#save(this.export()) - } - /** * @param {import('@ucanto/interface').Delegation} delegation * @param {import('./types').DelegationMeta} [meta] diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 6487d6f76..a61ef6144 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -7,7 +7,7 @@ import * as CAR from '@ucanto/transport/car' import * as CBOR from '@ucanto/transport/cbor' import * as HTTP from '@ucanto/transport/http' import * as ucanto from '@ucanto/core' -import { URI } from '@ucanto/validator' +import { URI, DID as DIDValidator } from '@ucanto/validator' import { Peer } from './awake/peer.js' import * as Space from '@web3-storage/capabilities/space' import * as Access from '@web3-storage/capabilities/access' @@ -25,22 +25,13 @@ import { canDelegateCapability, } from './delegations.js' import { AgentData, getSessionProof } from './agent-data.js' +import { createDidMailtoFromEmail } from './utils/did-mailto.js' export { AgentData } const HOST = 'https://access.web3.storage' const PRINCIPAL = DID.parse('did:web:web3.storage') -/** - * - * @param {string} email - * @returns {Ucanto.Principal>} - */ -function emailToSessionPrincipal(email) { - const parts = email.split('@').map((s) => encodeURIComponent(s)) - return DID.parse(`did:mailto:${parts[1]}:${parts[0]}`) -} - /** * @param {Ucanto.Signer>} issuer * @param {Ucanto.DID} space @@ -190,10 +181,6 @@ export class Agent { return this.#data.spaces } - get account() { - return this.#data.sessionPrincipal - } - did() { return this.#data.principal.did() } @@ -500,18 +487,16 @@ export class Agent { * Request authorization of a session allowing this agent to issue UCANs * signed by the passed email address. * - * @param {string} email + * @param {`${string}@${string}`} email * @param {object} [opts] * @param {AbortSignal} [opts.signal] */ async authorize(email, opts) { - const sessionPrincipal = emailToSessionPrincipal(email) - const res = await this.invokeAndExecute(Access.authorize, { audience: this.connection.id, with: this.issuer.did(), nb: { - iss: sessionPrincipal.did(), + iss: createDidMailtoFromEmail(email), att: [{ can: 'store/*' }, { can: 'provider/add' }, { can: 'upload/*' }], }, }) @@ -533,7 +518,6 @@ export class Agent { } await this.addProof(sessionDelegation) - this.#data.setSessionPrincipal(sessionPrincipal) // claim delegations here because we will need an ucan/attest from the service to // pair with the session delegation we just claimed to make it work @@ -581,26 +565,21 @@ export class Agent { /** * @param {Ucanto.DID<'key'>} space + * @param {Ucanto.Principal>} account + * @param {Ucanto.DID<'web'>} provider - e.g. 'did:web:staging.web3.storage' */ - async addProvider(space) { - const sessionPrincipal = this.#data.sessionPrincipal - - if (!sessionPrincipal) { - throw new Error('cannot add provider, please authorize first') - } - + async addProvider(space, account, provider) { return this.invokeAndExecute(Provider.add, { audience: this.connection.id, - with: sessionPrincipal.did(), + with: account.did(), proofs: this.proofs([ { can: 'provider/add', - with: sessionPrincipal.did(), + with: account.did(), }, ]), nb: { - // TODO probably need to make it possible to pass other providers in - provider: 'did:web:staging.web3.storage', + provider, consumer: space, }, }) @@ -609,22 +588,11 @@ export class Agent { /** * * @param {Ucanto.DID<'key'>} space + * @param {Ucanto.Principal>} account */ - async delegateSpaceAccessToAccount(space) { - const sessionPrincipal = this.#data.sessionPrincipal - - if (!sessionPrincipal) { - throw new Error( - 'cannot add delegate space access to account, please authorize first' - ) - } - + async delegateSpaceAccessToAccount(space, account) { const spaceSaysAccountCanAdminSpace = - await createIssuerSaysAccountCanAdminSpace( - this.issuer, - space, - sessionPrincipal - ) + await createIssuerSaysAccountCanAdminSpace(this.issuer, space, account) return this.invokeAndExecute(Access.delegate, { audience: this.connection.id, with: space, @@ -652,12 +620,26 @@ export class Agent { * * It also adds a full space delegation to the service in the voucher/claim invocation to allow for recovery * + * @param {string} email * @param {object} [opts] * @param {AbortSignal} [opts.signal] + * @param {Ucanto.DID<'web'>} [opts.provider] - provider to register - defaults to this.connection.id */ - async registerSpace(opts) { + async registerSpace(email, opts) { const space = this.currentSpace() const spaceMeta = space ? this.#data.spaces.get(space) : undefined + const provider = + opts?.provider || + (() => { + const service = this.connection.id.did() + if (DIDValidator.match({ method: 'web' }).is(service)) { + // connection.id did is a valid provider value. Try using that. + return service + } + throw new Error( + `unable to determine provider to use to register space. Pass opts.provider` + ) + })() if (!space || !spaceMeta) { throw new Error('No space selected') @@ -666,14 +648,14 @@ export class Agent { if (spaceMeta && spaceMeta.isRegistered) { throw new Error('Space already registered with web3.storage.') } - const providerResult = await this.addProvider( - /** @type {Ucanto.DID<'key'>} */ (space) - ) + const account = { did: () => createDidMailtoFromEmail(email) } + const providerResult = await this.addProvider(space, account, provider) if (providerResult.error) { throw new Error(providerResult.message, { cause: providerResult }) } const delegateSpaceAccessResult = await this.delegateSpaceAccessToAccount( - space + space, + account ) if (delegateSpaceAccessResult.error) { // @ts-ignore it's very weird that this is throwing an error but line 692 above does not - ignore for now diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index 15be458cd..7342e7edc 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -170,7 +170,6 @@ export type CIDString = string export interface AgentDataModel { meta: AgentMeta principal: Signer> - sessionPrincipal?: Principal> currentSpace?: DID<'key'> spaces: Map delegations: Map @@ -184,7 +183,6 @@ export type AgentDataExport = Pick< 'meta' | 'currentSpace' | 'spaces' > & { principal: SignerArchive - sessionPrincipal?: string delegations: Map< CIDString, { diff --git a/packages/access-client/src/utils/did-mailto.js b/packages/access-client/src/utils/did-mailto.js new file mode 100644 index 000000000..45480755e --- /dev/null +++ b/packages/access-client/src/utils/did-mailto.js @@ -0,0 +1,15 @@ +/** + * @param {string} email + * @returns {`did:mailto:${string}:${string}`} + */ +export function createDidMailtoFromEmail(email) { + const emailParts = email.split('@') + if (emailParts.length !== 2) { + throw new Error(`unexpected email ${email}`) + } + const [local, domain] = emailParts + const did = /** @type {const} */ ( + `did:mailto:${encodeURIComponent(domain)}:${encodeURIComponent(local)}` + ) + return did +} From 0cc4e47fd8383927393c32e7d06fc5e56f0fe10a Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 13:17:51 -0700 Subject: [PATCH 21/54] @web3-storage/access/agent exports createDidMailtoFromEmail --- packages/access-client/src/agent.js | 2 +- packages/access-client/test/agent.test.js | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index a61ef6144..370f5c943 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -27,7 +27,7 @@ import { import { AgentData, getSessionProof } from './agent-data.js' import { createDidMailtoFromEmail } from './utils/did-mailto.js' -export { AgentData } +export { AgentData, createDidMailtoFromEmail } const HOST = 'https://access.web3.storage' const PRINCIPAL = DID.parse('did:web:web3.storage') diff --git a/packages/access-client/test/agent.test.js b/packages/access-client/test/agent.test.js index b58d8cb62..9e8c3dbdc 100644 --- a/packages/access-client/test/agent.test.js +++ b/packages/access-client/test/agent.test.js @@ -1,6 +1,6 @@ import assert from 'assert' import { URI } from '@ucanto/validator' -import { Agent, connection } from '../src/agent.js' +import { Agent, connection, createDidMailtoFromEmail } from '../src/agent.js' import * as Space from '@web3-storage/capabilities/space' import { createServer } from './helpers/utils.js' import * as fixtures from './helpers/fixtures.js' @@ -180,4 +180,11 @@ describe('Agent', function () { }, ]) }) + + it('exports createDidMailtoFromEmail', async () => { + assert.deepEqual( + createDidMailtoFromEmail('foo@dag.house'), + 'did:mailto:dag.house:foo' + ) + }) }) From 6d942f145c4bada144861dbf5d93bdce4b993453 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 13:36:10 -0700 Subject: [PATCH 22/54] upgrade everything to multiformats 11.0.2 --- package.json | 8 +- packages/access-api/package.json | 2 +- packages/access-client/package.json | 2 +- packages/upload-api/package.json | 4 +- packages/upload-client/package.json | 2 +- pnpm-lock.yaml | 2156 +++++++++++++++++---------- 6 files changed, 1401 insertions(+), 773 deletions(-) diff --git a/package.json b/package.json index 22866c270..e41622213 100644 --- a/package.json +++ b/package.json @@ -17,14 +17,14 @@ "docs:markdown": "pnpm run build && docusaurus generate-typedoc" }, "devDependencies": { - "@docusaurus/core": "^2.2.0", + "@docusaurus/core": "^2.3.1", "docusaurus-plugin-typedoc": "^0.18.0", - "lint-staged": "^13.1.0", + "lint-staged": "^13.2.0", "prettier": "2.8.3", "simple-git-hooks": "^2.8.1", "typedoc-plugin-markdown": "^3.14.0", "typescript": "4.9.5", - "wrangler": "^2.8.0" + "wrangler": "^2.12.3" }, "simple-git-hooks": { "pre-commit": "npx lint-staged" @@ -44,7 +44,7 @@ }, "dependencies": { "depcheck": "^1.4.3", - "typedoc": "^0.23.22", + "typedoc": "^0.23.26", "typedoc-plugin-missing-exports": "^1.0.0" }, "packageManager": "pnpm@7.24.3", diff --git a/packages/access-api/package.json b/packages/access-api/package.json index 485af5d49..d3610f0a8 100644 --- a/packages/access-api/package.json +++ b/packages/access-api/package.json @@ -29,7 +29,7 @@ "@web3-storage/worker-utils": "0.4.3-dev", "kysely": "^0.23.4", "kysely-d1": "^0.1.0", - "multiformats": "^11.0.1", + "multiformats": "^11.0.2", "p-retry": "^5.1.2", "preact": "^10.11.3", "preact-render-to-string": "^5.2.6", diff --git a/packages/access-client/package.json b/packages/access-client/package.json index a3cba524c..5b66a92de 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -73,7 +73,7 @@ "inquirer": "^9.1.4", "isomorphic-ws": "^5.0.0", "kysely": "^0.23.4", - "multiformats": "^11.0.1", + "multiformats": "^11.0.2", "one-webcrypto": "^1.0.3", "ora": "^6.1.2", "p-defer": "^4.0.0", diff --git a/packages/upload-api/package.json b/packages/upload-api/package.json index 935bb668b..7cca959dc 100644 --- a/packages/upload-api/package.json +++ b/packages/upload-api/package.json @@ -64,11 +64,11 @@ "@ucanto/server": "^6.1.0", "@ucanto/transport": "^5.1.1", "@web3-storage/capabilities": "workspace:^", - "multiformats": "^11.0.1", + "multiformats": "^11.0.2", "p-retry": "^5.1.2" }, "devDependencies": { - "@ipld/car": "^5.0.1", + "@ipld/car": "^5.1.1", "@types/mocha": "^10.0.1", "@ucanto/core": "^5.1.0", "@web-std/blob": "^3.0.4", diff --git a/packages/upload-client/package.json b/packages/upload-client/package.json index 26e76f7f4..42e5653e4 100644 --- a/packages/upload-client/package.json +++ b/packages/upload-client/package.json @@ -70,7 +70,7 @@ "@ucanto/interface": "^6.2.0", "@ucanto/transport": "^5.1.1", "@web3-storage/capabilities": "workspace:^", - "multiformats": "^11.0.1", + "multiformats": "^11.0.2", "p-queue": "^7.3.0", "p-retry": "^5.1.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 331df287e..a3afcf8c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,30 +4,30 @@ importers: .: specifiers: - '@docusaurus/core': ^2.2.0 + '@docusaurus/core': ^2.3.1 depcheck: ^1.4.3 docusaurus-plugin-typedoc: ^0.18.0 - lint-staged: ^13.1.0 + lint-staged: ^13.2.0 prettier: 2.8.3 simple-git-hooks: ^2.8.1 - typedoc: ^0.23.22 + typedoc: ^0.23.26 typedoc-plugin-markdown: ^3.14.0 typedoc-plugin-missing-exports: ^1.0.0 typescript: 4.9.5 - wrangler: ^2.8.0 + wrangler: ^2.12.3 dependencies: depcheck: 1.4.3 - typedoc: 0.23.24_typescript@4.9.5 - typedoc-plugin-missing-exports: 1.0.0_typedoc@0.23.24 + typedoc: 0.23.26_typescript@4.9.5 + typedoc-plugin-missing-exports: 1.0.0_typedoc@0.23.26 devDependencies: - '@docusaurus/core': 2.3.0_typescript@4.9.5 - docusaurus-plugin-typedoc: 0.18.0_res3k6jdwbtxmimicswazugz6i - lint-staged: 13.1.0 + '@docusaurus/core': 2.3.1_typescript@4.9.5 + docusaurus-plugin-typedoc: 0.18.0_fukoy6jgklpnhaz4jx7cggnvsu + lint-staged: 13.2.0 prettier: 2.8.3 simple-git-hooks: 2.8.1 - typedoc-plugin-markdown: 3.14.0_typedoc@0.23.24 + typedoc-plugin-markdown: 3.14.0_typedoc@0.23.26 typescript: 4.9.5 - wrangler: 2.9.0 + wrangler: 2.12.3 packages/access-api: specifiers: @@ -62,7 +62,7 @@ importers: kysely-d1: ^0.1.0 miniflare: ^2.11.0 mocha: ^10.2.0 - multiformats: ^11.0.1 + multiformats: ^11.0.2 p-retry: ^5.1.2 p-wait-for: ^5.0.0 preact: ^10.11.3 @@ -88,7 +88,7 @@ importers: '@web3-storage/worker-utils': 0.4.3-dev kysely: 0.23.4 kysely-d1: 0.1.0_kysely@0.23.4 - multiformats: 11.0.1 + multiformats: 11.0.2 p-retry: 5.1.2 preact: 10.11.3 preact-render-to-string: 5.2.6_preact@10.11.3 @@ -151,7 +151,7 @@ importers: kysely: ^0.23.4 miniflare: ^2.11.0 mocha: ^10.2.0 - multiformats: ^11.0.1 + multiformats: ^11.0.2 one-webcrypto: ^1.0.3 ora: ^6.1.2 p-defer: ^4.0.0 @@ -182,7 +182,7 @@ importers: inquirer: 9.1.4 isomorphic-ws: 5.0.0_ws@8.12.0 kysely: 0.23.4 - multiformats: 11.0.1 + multiformats: 11.0.2 one-webcrypto: 1.0.3 ora: 6.1.2 p-defer: 4.0.0 @@ -291,7 +291,7 @@ importers: packages/upload-api: specifiers: - '@ipld/car': ^5.0.1 + '@ipld/car': ^5.1.1 '@types/mocha': ^10.0.1 '@ucanto/client': ^5.1.0 '@ucanto/core': ^5.1.0 @@ -304,7 +304,7 @@ importers: '@web3-storage/sigv4': ^1.0.2 hd-scripts: ^4.1.0 mocha: ^10.2.0 - multiformats: ^11.0.1 + multiformats: ^11.0.2 p-retry: ^5.1.2 dependencies: '@ucanto/client': 5.1.0 @@ -313,10 +313,10 @@ importers: '@ucanto/server': 6.1.0 '@ucanto/transport': 5.1.1 '@web3-storage/capabilities': link:../capabilities - multiformats: 11.0.1 + multiformats: 11.0.2 p-retry: 5.1.2 devDependencies: - '@ipld/car': 5.1.0 + '@ipld/car': 5.1.1 '@types/mocha': 10.0.1 '@ucanto/core': 5.1.0 '@web-std/blob': 3.0.4 @@ -344,7 +344,7 @@ importers: hundreds: ^0.0.9 ipfs-unixfs-exporter: ^10.0.0 mocha: ^10.2.0 - multiformats: ^11.0.1 + multiformats: ^11.0.2 npm-run-all: ^4.1.5 p-queue: ^7.3.0 p-retry: ^5.1.2 @@ -358,7 +358,7 @@ importers: '@ucanto/interface': 6.2.0 '@ucanto/transport': 5.1.1 '@web3-storage/capabilities': link:../capabilities - multiformats: 11.0.1 + multiformats: 11.0.2 p-queue: 7.3.0 p-retry: 5.1.2 devDependencies: @@ -398,8 +398,8 @@ packages: dependencies: '@babel/highlight': 7.18.6 - /@babel/compat-data/7.20.14: - resolution: {integrity: sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==} + /@babel/compat-data/7.21.0: + resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==} engines: {node: '>=6.9.0'} dev: true @@ -408,13 +408,13 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.14 - '@babel/helper-module-transforms': 7.20.11 - '@babel/helpers': 7.20.13 - '@babel/parser': 7.20.13 + '@babel/generator': 7.21.3 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helpers': 7.21.0 + '@babel/parser': 7.21.3 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -427,20 +427,20 @@ packages: - supports-color dev: true - /@babel/core/7.20.12: - resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} + /@babel/core/7.21.3: + resolution: {integrity: sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.14 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-module-transforms': 7.20.11 - '@babel/helpers': 7.20.13 - '@babel/parser': 7.20.13 + '@babel/generator': 7.21.3 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.3 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helpers': 7.21.0 + '@babel/parser': 7.21.3 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -450,19 +450,20 @@ packages: - supports-color dev: true - /@babel/generator/7.20.14: - resolution: {integrity: sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==} + /@babel/generator/7.21.3: + resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 jsesc: 2.5.2 /@babel/helper-annotate-as-pure/7.18.6: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true /@babel/helper-builder-binary-assignment-operator-visitor/7.18.9: @@ -470,34 +471,34 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true - /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12: + /@babel/helper-compilation-targets/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.20.14 - '@babel/core': 7.20.12 - '@babel/helper-validator-option': 7.18.6 + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-validator-option': 7.21.0 browserslist: 4.21.5 lru-cache: 5.1.1 semver: 6.3.0 dev: true - /@babel/helper-create-class-features-plugin/7.20.12_@babel+core@7.20.12: - resolution: {integrity: sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==} + /@babel/helper-create-class-features-plugin/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.19.0 - '@babel/helper-member-expression-to-functions': 7.20.7 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-replace-supers': 7.20.7 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 @@ -506,24 +507,24 @@ packages: - supports-color dev: true - /@babel/helper-create-regexp-features-plugin/7.20.5_@babel+core@7.20.12: - resolution: {integrity: sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==} + /@babel/helper-create-regexp-features-plugin/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-annotate-as-pure': 7.18.6 - regexpu-core: 5.2.2 + regexpu-core: 5.3.2 dev: true - /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.20.12: + /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.21.3: resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} peerDependencies: '@babel/core': ^7.4.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 debug: 4.3.4 lodash.debounce: 4.0.8 @@ -541,38 +542,38 @@ packages: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true - /@babel/helper-function-name/7.19.0: - resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} + /@babel/helper-function-name/7.21.0: + resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 /@babel/helper-hoist-variables/7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 - /@babel/helper-member-expression-to-functions/7.20.7: - resolution: {integrity: sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==} + /@babel/helper-member-expression-to-functions/7.21.0: + resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true /@babel/helper-module-imports/7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true - /@babel/helper-module-transforms/7.20.11: - resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==} + /@babel/helper-module-transforms/7.21.2: + resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-environment-visitor': 7.18.9 @@ -581,8 +582,8 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color dev: true @@ -591,7 +592,7 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true /@babel/helper-plugin-utils/7.10.4: @@ -603,17 +604,17 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.20.12: + /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.21.3: resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color dev: true @@ -623,11 +624,11 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-member-expression-to-functions': 7.20.7 + '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color dev: true @@ -636,21 +637,21 @@ packages: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true /@babel/helper-skip-transparent-expression-wrappers/7.20.0: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: true /@babel/helper-split-export-declaration/7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 /@babel/helper-string-parser/7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} @@ -660,8 +661,8 @@ packages: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option/7.18.6: - resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + /@babel/helper-validator-option/7.21.0: + resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} engines: {node: '>=6.9.0'} dev: true @@ -669,21 +670,21 @@ packages: resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.19.0 + '@babel/helper-function-name': 7.21.0 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color dev: true - /@babel/helpers/7.20.13: - resolution: {integrity: sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==} + /@babel/helpers/7.21.0: + resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color dev: true @@ -701,144 +702,144 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 dev: false - /@babel/parser/7.20.13: - resolution: {integrity: sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==} + /@babel/parser/7.21.3: + resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.20.12: + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.20.7_@babel+core@7.20.12: + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.12 + '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-async-generator-functions/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-async-generator-functions/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.21.3 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.21.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-static-block/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==} + /@babel/plugin-proposal-class-static-block/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.21.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.20.12: + /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.21.3: resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-logical-assignment-operators/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-logical-assignment-operators/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.21.3 dev: true /@babel/plugin-proposal-object-rest-spread/7.12.1_@babel+core@7.12.9: @@ -849,147 +850,147 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.10.4 '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 - '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.12.9 + '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.12.9 dev: true - /@babel/plugin-proposal-object-rest-spread/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-object-rest-spread/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.20.14 - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.20.12 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-optional-chaining/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==} + /@babel/plugin-proposal-optional-chaining/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-private-property-in-object/7.20.5_@babel+core@7.20.12: - resolution: {integrity: sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==} + /@babel/plugin-proposal-private-property-in-object/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.21.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} engines: {node: '>=4'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.12: + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.21.3: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.12: + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.21.3: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.12: + /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.21.3: resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.21.3: resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.21.3: resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.20.12: + /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.21.3: resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.21.3: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -1002,40 +1003,40 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.12: + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.12: + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.21.3: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.21.3: resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.12: + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.21.3: resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -1048,118 +1049,118 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.21.3: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.21.3: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.21.3: resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.12: + /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.21.3: resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.12: + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.21.3: resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.12: + /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.21.3: resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-arrow-functions/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-arrow-functions/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-async-to-generator/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-async-to-generator/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.21.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-block-scoping/7.20.14_@babel+core@7.20.12: - resolution: {integrity: sha512-sMPepQtsOs5fM1bwNvuJJHvaCfOEQfmc01FGw0ELlTpTJj5Ql/zuNRRldYhAPys4ghXdBIQJbRVYi44/7QflQQ==} + /@babel/plugin-transform-block-scoping/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-classes/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==} + /@babel/plugin-transform-classes/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.3 '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.19.0 + '@babel/helper-function-name': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-replace-supers': 7.20.7 @@ -1169,192 +1170,192 @@ packages: - supports-color dev: true - /@babel/plugin-transform-computed-properties/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-computed-properties/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 '@babel/template': 7.20.7 dev: true - /@babel/plugin-transform-destructuring/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==} + /@babel/plugin-transform-destructuring/7.21.3_@babel+core@7.21.3: + resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.21.3: resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-for-of/7.18.8_@babel+core@7.20.12: - resolution: {integrity: sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==} + /@babel/plugin-transform-for-of/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.21.3: resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-function-name': 7.19.0 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.3 + '@babel/helper-function-name': 7.21.0 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-literals/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-literals/7.18.9_@babel+core@7.21.3: resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-modules-amd/7.20.11_@babel+core@7.20.12: + /@babel/plugin-transform-modules-amd/7.20.11_@babel+core@7.21.3: resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-module-transforms': 7.20.11 + '@babel/core': 7.21.3 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-commonjs/7.20.11_@babel+core@7.20.12: - resolution: {integrity: sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==} + /@babel/plugin-transform-modules-commonjs/7.21.2_@babel+core@7.21.3: + resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-module-transforms': 7.20.11 + '@babel/core': 7.21.3 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-simple-access': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-systemjs/7.20.11_@babel+core@7.20.12: + /@babel/plugin-transform-modules-systemjs/7.20.11_@babel+core@7.21.3: resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-module-transforms': 7.20.11 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-validator-identifier': 7.19.1 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-module-transforms': 7.20.11 + '@babel/core': 7.21.3 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-named-capturing-groups-regex/7.20.5_@babel+core@7.20.12: + /@babel/plugin-transform-named-capturing-groups-regex/7.20.5_@babel+core@7.21.3: resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-replace-supers': 7.20.7 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-parameters/7.20.7_@babel+core@7.12.9: - resolution: {integrity: sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==} + /@babel/plugin-transform-parameters/7.21.3_@babel+core@7.12.9: + resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1363,343 +1364,348 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-parameters/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==} + /@babel/plugin-transform-parameters/7.21.3_@babel+core@7.21.3: + resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-react-constant-elements/7.20.2_@babel+core@7.20.12: - resolution: {integrity: sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==} + /@babel/plugin-transform-react-constant-elements/7.21.3_@babel+core@7.21.3: + resolution: {integrity: sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-react-display-name/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-react-display-name/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-transform-react-jsx': 7.20.13_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/plugin-transform-react-jsx': 7.21.0_@babel+core@7.21.3 dev: true - /@babel/plugin-transform-react-jsx/7.20.13_@babel+core@7.20.12: - resolution: {integrity: sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw==} + /@babel/plugin-transform-react-jsx/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.12 - '@babel/types': 7.20.7 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.21.3 + '@babel/types': 7.21.3 dev: true - /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-regenerator/7.20.5_@babel+core@7.20.12: + /@babel/plugin-transform-regenerator/7.20.5_@babel+core@7.21.3: resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 regenerator-transform: 0.15.1 dev: true - /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-runtime/7.19.6_@babel+core@7.20.12: - resolution: {integrity: sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==} + /@babel/plugin-transform-runtime/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.12 - babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12 - babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12 + babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.21.3 + babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.21.3 + babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.21.3 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-spread/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-spread/7.20.7_@babel+core@7.21.3: resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 dev: true - /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.21.3: resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.21.3: resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-typescript/7.20.13_@babel+core@7.20.12: - resolution: {integrity: sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==} + /@babel/plugin-transform-typescript/7.21.3_@babel+core@7.21.3: + resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.12 + '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.21.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.20.12: + /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.21.3: resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/preset-env/7.20.2_@babel+core@7.20.12: + /@babel/preset-env/7.20.2_@babel+core@7.21.3: resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.20.14 - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-class-static-block': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-private-property-in-object': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.12 - '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-import-assertions': 7.20.0_@babel+core@7.20.12 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-async-to-generator': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.20.14_@babel+core@7.20.12 - '@babel/plugin-transform-classes': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-computed-properties': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-destructuring': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-for-of': 7.18.8_@babel+core@7.20.12 - '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-commonjs': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-regenerator': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.20.12 - '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.20.12 - '@babel/preset-modules': 0.1.5_@babel+core@7.20.12 - '@babel/types': 7.20.7 - babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.12 - babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12 - babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12 - core-js-compat: 3.27.2 + '@babel/helper-validator-option': 7.21.0 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-proposal-class-static-block': 7.21.0_@babel+core@7.21.3 + '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.21.3 + '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.3 + '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-proposal-private-property-in-object': 7.21.0_@babel+core@7.21.3 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.21.3 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.21.3 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.21.3 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-syntax-import-assertions': 7.20.0_@babel+core@7.21.3 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.21.3 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.21.3 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.21.3 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.21.3 + '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-transform-async-to-generator': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.21.3 + '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.21.3 + '@babel/plugin-transform-computed-properties': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.21.3 + '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.21.3 + '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-for-of': 7.21.0_@babel+core@7.21.3 + '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.21.3 + '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.21.3 + '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.21.3 + '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.21.3 + '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.21.3 + '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5_@babel+core@7.21.3 + '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.21.3 + '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-regenerator': 7.20.5_@babel+core@7.21.3 + '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.21.3 + '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.21.3 + '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.21.3 + '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.21.3 + '@babel/preset-modules': 0.1.5_@babel+core@7.21.3 + '@babel/types': 7.21.3 + babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.21.3 + babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.21.3 + babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.21.3 + core-js-compat: 3.29.1 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-modules/0.1.5_@babel+core@7.20.12: + /@babel/preset-modules/0.1.5_@babel+core@7.21.3: resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 - '@babel/types': 7.20.7 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.21.3 + '@babel/types': 7.21.3 esutils: 2.0.3 dev: true - /@babel/preset-react/7.18.6_@babel+core@7.20.12: + /@babel/preset-react/7.18.6_@babel+core@7.21.3: resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-transform-react-display-name': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-react-jsx': 7.20.13_@babel+core@7.20.12 - '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-react-pure-annotations': 7.18.6_@babel+core@7.20.12 + '@babel/helper-validator-option': 7.21.0 + '@babel/plugin-transform-react-display-name': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-react-jsx': 7.21.0_@babel+core@7.21.3 + '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-transform-react-pure-annotations': 7.18.6_@babel+core@7.21.3 dev: true - /@babel/preset-typescript/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==} + /@babel/preset-typescript/7.21.0_@babel+core@7.21.3: + resolution: {integrity: sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-transform-typescript': 7.20.13_@babel+core@7.20.12 + '@babel/helper-validator-option': 7.21.0 + '@babel/plugin-transform-typescript': 7.21.3_@babel+core@7.21.3 transitivePeerDependencies: - supports-color dev: true - /@babel/runtime-corejs3/7.20.13: - resolution: {integrity: sha512-p39/6rmY9uvlzRiLZBIB3G9/EBr66LBMcYm7fIDeSBNdRjF2AGD3rFZucUyAgGHC2N+7DdLvVi33uTjSE44FIw==} + /@babel/regjsgen/0.8.0: + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + dev: true + + /@babel/runtime-corejs3/7.21.0: + resolution: {integrity: sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw==} engines: {node: '>=6.9.0'} dependencies: - core-js-pure: 3.27.2 + core-js-pure: 3.29.1 regenerator-runtime: 0.13.11 dev: true - /@babel/runtime/7.20.13: - resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==} + /@babel/runtime/7.21.0: + resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.11 @@ -1710,28 +1716,28 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/parser': 7.20.13 - '@babel/types': 7.20.7 + '@babel/parser': 7.21.3 + '@babel/types': 7.21.3 - /@babel/traverse/7.20.13: - resolution: {integrity: sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==} + /@babel/traverse/7.21.3: + resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.14 + '@babel/generator': 7.21.3 '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.19.0 + '@babel/helper-function-name': 7.21.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.20.13 - '@babel/types': 7.20.7 + '@babel/parser': 7.21.3 + '@babel/types': 7.21.3 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color - /@babel/types/7.20.7: - resolution: {integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==} + /@babel/types/7.21.3: + resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.19.4 @@ -1771,8 +1777,13 @@ packages: resolution: {integrity: sha512-vj9huEy4mjJ48GS1Z8yvtMm4BYAnFYACUds25ym6Gd/gsnngkJ17fo62a6mmbNNwCBS/8467PmZR01Zs/06TjA==} dev: true - /@docusaurus/core/2.3.0_typescript@4.9.5: - resolution: {integrity: sha512-2AU5HfKyExO+/mi41SBnx5uY0aGZFXr3D93wntBY4lN1gsDKUpi7EE4lPBAXm9CoH4Pw6N24yDHy9CPR3sh/uA==} + /@discoveryjs/json-ext/0.5.7: + resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} + engines: {node: '>=10.0.0'} + dev: true + + /@docusaurus/core/2.3.1_typescript@4.9.5: + resolution: {integrity: sha512-0Jd4jtizqnRAr7svWaBbbrCCN8mzBNd2xFLoT/IM7bGfFie5y58oz97KzXliwiLY3zWjqMXjQcuP1a5VgCv2JA==} engines: {node: '>=16.14'} hasBin: true peerDependencies: @@ -1784,27 +1795,27 @@ packages: react-dom: optional: true dependencies: - '@babel/core': 7.20.12 - '@babel/generator': 7.20.14 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-runtime': 7.19.6_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@babel/preset-typescript': 7.18.6_@babel+core@7.20.12 - '@babel/runtime': 7.20.13 - '@babel/runtime-corejs3': 7.20.13 - '@babel/traverse': 7.20.13 - '@docusaurus/cssnano-preset': 2.3.0 - '@docusaurus/logger': 2.3.0 - '@docusaurus/mdx-loader': 2.3.0 + '@babel/core': 7.21.3 + '@babel/generator': 7.21.3 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.21.3 + '@babel/plugin-transform-runtime': 7.21.0_@babel+core@7.21.3 + '@babel/preset-env': 7.20.2_@babel+core@7.21.3 + '@babel/preset-react': 7.18.6_@babel+core@7.21.3 + '@babel/preset-typescript': 7.21.0_@babel+core@7.21.3 + '@babel/runtime': 7.21.0 + '@babel/runtime-corejs3': 7.21.0 + '@babel/traverse': 7.21.3 + '@docusaurus/cssnano-preset': 2.3.1 + '@docusaurus/logger': 2.3.1 + '@docusaurus/mdx-loader': 2.3.1 '@docusaurus/react-loadable': 5.5.2 - '@docusaurus/utils': 2.3.0 - '@docusaurus/utils-common': 2.3.0 - '@docusaurus/utils-validation': 2.3.0 + '@docusaurus/utils': 2.3.1 + '@docusaurus/utils-common': 2.3.1 + '@docusaurus/utils-validation': 2.3.1 '@slorber/static-site-generator-webpack-plugin': 4.0.7 '@svgr/webpack': 6.5.1 - autoprefixer: 10.4.13_postcss@8.4.21 - babel-loader: 8.3.0_la66t7xldg4uecmyawueag5wkm + autoprefixer: 10.4.14_postcss@8.4.21 + babel-loader: 8.3.0_h5x7dh6zbbyopr7jvxivhylqpa babel-plugin-dynamic-import-node: 2.3.3 boxen: 6.2.1 chalk: 4.1.2 @@ -1813,31 +1824,31 @@ packages: cli-table3: 0.6.3 combine-promises: 1.1.0 commander: 5.1.0 - copy-webpack-plugin: 11.0.0_webpack@5.75.0 - core-js: 3.27.2 - css-loader: 6.7.3_webpack@5.75.0 - css-minimizer-webpack-plugin: 4.2.2_dpcjkp5o5ztxuvt4quwwvenemi - cssnano: 5.1.14_postcss@8.4.21 + copy-webpack-plugin: 11.0.0_webpack@5.76.2 + core-js: 3.29.1 + css-loader: 6.7.3_webpack@5.76.2 + css-minimizer-webpack-plugin: 4.2.2_7uczfu7hmoatiedqewdadkdxue + cssnano: 5.1.15_postcss@8.4.21 del: 6.1.1 detect-port: 1.5.1 escape-html: 1.0.3 - eta: 1.14.2 - file-loader: 6.2.0_webpack@5.75.0 + eta: 2.0.1 + file-loader: 6.2.0_webpack@5.76.2 fs-extra: 10.1.0 html-minifier-terser: 6.1.0 html-tags: 3.2.0 - html-webpack-plugin: 5.5.0_webpack@5.75.0 + html-webpack-plugin: 5.5.0_webpack@5.76.2 import-fresh: 3.3.0 leven: 3.1.0 lodash: 4.17.21 - mini-css-extract-plugin: 2.7.2_webpack@5.75.0 + mini-css-extract-plugin: 2.7.3_webpack@5.76.2 postcss: 8.4.21 - postcss-loader: 7.0.2_6jdsrmfenkuhhw3gx4zvjlznce + postcss-loader: 7.0.2_6puvukfnwbwq425eep7g4z27be prompts: 2.4.2 - react-dev-utils: 12.0.1_hhrrucqyg4eysmfpujvov2ym5u + react-dev-utils: 12.0.1_a37q6j7dwawz22saey2vgkpwqm react-helmet-async: 1.3.0 react-loadable: /@docusaurus/react-loadable/5.5.2 - react-loadable-ssr-addon-v5-slorber: 1.0.1_pwfl7zyferpbeh35vaepqxwaky + react-loadable-ssr-addon-v5-slorber: 1.0.1_vycmgd62fyq7p6lzattvftnbcy react-router: 5.3.4 react-router-config: 5.1.1_react-router@5.3.4 react-router-dom: 5.3.4 @@ -1845,16 +1856,16 @@ packages: semver: 7.3.8 serve-handler: 6.1.5 shelljs: 0.8.5 - terser-webpack-plugin: 5.3.6_webpack@5.75.0 + terser-webpack-plugin: 5.3.7_webpack@5.76.2 tslib: 2.5.0 update-notifier: 5.1.0 - url-loader: 4.1.1_p5dl6emkcwslbw72e37w4ug7em + url-loader: 4.1.1_35ful32yo3wjb53le3l6xb5doy wait-on: 6.0.1 - webpack: 5.75.0 - webpack-bundle-analyzer: 4.7.0 - webpack-dev-server: 4.11.1_webpack@5.75.0 + webpack: 5.76.2 + webpack-bundle-analyzer: 4.8.0 + webpack-dev-server: 4.12.0_webpack@5.76.2 webpack-merge: 5.8.0 - webpackbar: 5.0.2_webpack@5.75.0 + webpackbar: 5.0.2_webpack@5.76.2 transitivePeerDependencies: - '@docusaurus/types' - '@parcel/css' @@ -1874,26 +1885,26 @@ packages: - webpack-cli dev: true - /@docusaurus/cssnano-preset/2.3.0: - resolution: {integrity: sha512-igmsXc1Q95lMeq07A1xua0/5wOPygDQ/ENSV7VVbiGhnvMv4gzkba8ZvbAtc7PmqK+kpYRfPzNCOk0GnQCvibg==} + /@docusaurus/cssnano-preset/2.3.1: + resolution: {integrity: sha512-7mIhAROES6CY1GmCjR4CZkUfjTL6B3u6rKHK0ChQl2d1IevYXq/k/vFgvOrJfcKxiObpMnE9+X6R2Wt1KqxC6w==} engines: {node: '>=16.14'} dependencies: - cssnano-preset-advanced: 5.3.9_postcss@8.4.21 + cssnano-preset-advanced: 5.3.10_postcss@8.4.21 postcss: 8.4.21 postcss-sort-media-queries: 4.3.0_postcss@8.4.21 tslib: 2.5.0 dev: true - /@docusaurus/logger/2.3.0: - resolution: {integrity: sha512-GO8s+FJpNT0vwt6kr/BZ/B1iB8EgHH/CF590i55Epy3TP2baQHGEHcAnQWvz5067OXIEke7Sa8sUNi0V9FrcJw==} + /@docusaurus/logger/2.3.1: + resolution: {integrity: sha512-2lAV/olKKVr9qJhfHFCaqBIl8FgYjbUFwgUnX76+cULwQYss+42ZQ3grHGFvI0ocN2X55WcYe64ellQXz7suqg==} engines: {node: '>=16.14'} dependencies: chalk: 4.1.2 tslib: 2.5.0 dev: true - /@docusaurus/mdx-loader/2.3.0: - resolution: {integrity: sha512-uxownG7dlg/l19rTIfUP0KDsbI8lTCgziWsdubMcWpGvOgXgm1p4mKSmWPzAwkRENn+un4L8DBhl3j1toeJy1A==} + /@docusaurus/mdx-loader/2.3.1: + resolution: {integrity: sha512-Gzga7OsxQRpt3392K9lv/bW4jGppdLFJh3luKRknCKSAaZrmVkOQv2gvCn8LAOSZ3uRg5No7AgYs/vpL8K94lA==} engines: {node: '>=16.14'} peerDependencies: react: ^16.8.4 || ^17.0.0 @@ -1904,13 +1915,13 @@ packages: react-dom: optional: true dependencies: - '@babel/parser': 7.20.13 - '@babel/traverse': 7.20.13 - '@docusaurus/logger': 2.3.0 - '@docusaurus/utils': 2.3.0 + '@babel/parser': 7.21.3 + '@babel/traverse': 7.21.3 + '@docusaurus/logger': 2.3.1 + '@docusaurus/utils': 2.3.1 '@mdx-js/mdx': 1.6.22 escape-html: 1.0.3 - file-loader: 6.2.0_webpack@5.75.0 + file-loader: 6.2.0_webpack@5.76.2 fs-extra: 10.1.0 image-size: 1.0.2 mdast-util-to-string: 2.0.0 @@ -1919,8 +1930,8 @@ packages: tslib: 2.5.0 unified: 9.2.2 unist-util-visit: 2.0.3 - url-loader: 4.1.1_p5dl6emkcwslbw72e37w4ug7em - webpack: 5.75.0 + url-loader: 4.1.1_35ful32yo3wjb53le3l6xb5doy + webpack: 5.76.2 transitivePeerDependencies: - '@docusaurus/types' - '@swc/core' @@ -1942,8 +1953,8 @@ packages: prop-types: 15.8.1 dev: true - /@docusaurus/utils-common/2.3.0: - resolution: {integrity: sha512-nu5An+26FS7SQTwvyFR4g9lw3NU1u2RLcxJPZF+NCOG8Ne96ciuQosa7+N1kllm/heEJqfTaAUD0sFxpTZrDtw==} + /@docusaurus/utils-common/2.3.1: + resolution: {integrity: sha512-pVlRpXkdNcxmKNxAaB1ya2hfCEvVsLDp2joeM6K6uv55Oc5nVIqgyYSgSNKZyMdw66NnvMfsu0RBylcwZQKo9A==} engines: {node: '>=16.14'} peerDependencies: '@docusaurus/types': '*' @@ -1954,13 +1965,13 @@ packages: tslib: 2.5.0 dev: true - /@docusaurus/utils-validation/2.3.0: - resolution: {integrity: sha512-TBJCLqwAoiQQJ6dbgBpuLvzsn/XiTgbZkd6eJFUIQYLb1d473Zv58QrHXVmVQDLWiCgmJpHW2LpMfumTpCDgJw==} + /@docusaurus/utils-validation/2.3.1: + resolution: {integrity: sha512-7n0208IG3k1HVTByMHlZoIDjjOFC8sbViHVXJx0r3Q+3Ezrx+VQ1RZ/zjNn6lT+QBCRCXlnlaoJ8ug4HIVgQ3w==} engines: {node: '>=16.14'} dependencies: - '@docusaurus/logger': 2.3.0 - '@docusaurus/utils': 2.3.0 - joi: 17.7.0 + '@docusaurus/logger': 2.3.1 + '@docusaurus/utils': 2.3.1 + joi: 17.8.4 js-yaml: 4.1.0 tslib: 2.5.0 transitivePeerDependencies: @@ -1972,8 +1983,8 @@ packages: - webpack-cli dev: true - /@docusaurus/utils/2.3.0: - resolution: {integrity: sha512-6+GCurDsePHHbLM3ktcjv8N4zrjgrl1O7gOQNG4UMktcwHssFFVm+geVcB4M8siOmwUjV2VaNrp0hpGy8DOQHw==} + /@docusaurus/utils/2.3.1: + resolution: {integrity: sha512-9WcQROCV0MmrpOQDXDGhtGMd52DHpSFbKLfkyaYumzbTstrbA5pPOtiGtxK1nqUHkiIv8UwexS54p0Vod2I1lg==} engines: {node: '>=16.14'} peerDependencies: '@docusaurus/types': '*' @@ -1981,10 +1992,10 @@ packages: '@docusaurus/types': optional: true dependencies: - '@docusaurus/logger': 2.3.0 + '@docusaurus/logger': 2.3.1 '@svgr/webpack': 6.5.1 escape-string-regexp: 4.0.0 - file-loader: 6.2.0_webpack@5.75.0 + file-loader: 6.2.0_webpack@5.76.2 fs-extra: 10.1.0 github-slugger: 1.5.0 globby: 11.1.0 @@ -1995,8 +2006,8 @@ packages: resolve-pathname: 3.0.0 shelljs: 0.8.5 tslib: 2.5.0 - url-loader: 4.1.1_p5dl6emkcwslbw72e37w4ug7em - webpack: 5.75.0 + url-loader: 4.1.1_35ful32yo3wjb53le3l6xb5doy + webpack: 5.76.2 transitivePeerDependencies: - '@swc/core' - esbuild @@ -2022,6 +2033,14 @@ packages: esbuild: 0.14.51 dev: true + /@esbuild-plugins/node-globals-polyfill/0.1.1_esbuild@0.16.3: + resolution: {integrity: sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==} + peerDependencies: + esbuild: '*' + dependencies: + esbuild: 0.16.3 + dev: true + /@esbuild-plugins/node-modules-polyfill/0.1.4_esbuild@0.14.51: resolution: {integrity: sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==} peerDependencies: @@ -2032,6 +2051,16 @@ packages: rollup-plugin-node-polyfills: 0.2.1 dev: true + /@esbuild-plugins/node-modules-polyfill/0.1.4_esbuild@0.16.3: + resolution: {integrity: sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==} + peerDependencies: + esbuild: '*' + dependencies: + esbuild: 0.16.3 + escape-string-regexp: 4.0.0 + rollup-plugin-node-polyfills: 0.2.1 + dev: true + /@esbuild/android-arm/0.16.10: resolution: {integrity: sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ==} engines: {node: '>=12'} @@ -2041,6 +2070,15 @@ packages: dev: true optional: true + /@esbuild/android-arm/0.16.3: + resolution: {integrity: sha512-mueuEoh+s1eRbSJqq9KNBQwI4QhQV6sRXIfTyLXSHGMpyew61rOK4qY21uKbXl1iBoMb0AdL1deWFCQVlN2qHA==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm/0.17.5: resolution: {integrity: sha512-crmPUzgCmF+qZXfl1YkiFoUta2XAfixR1tEnr/gXIixE+WL8Z0BGqfydP5oox0EUOgQMMRgtATtakyAcClQVqQ==} engines: {node: '>=12'} @@ -2059,6 +2097,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64/0.16.3: + resolution: {integrity: sha512-RolFVeinkeraDvN/OoRf1F/lP0KUfGNb5jxy/vkIMeRRChkrX/HTYN6TYZosRJs3a1+8wqpxAo5PI5hFmxyPRg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64/0.17.5: resolution: {integrity: sha512-KHWkDqYAMmKZjY4RAN1PR96q6UOtfkWlTS8uEwWxdLtkRt/0F/csUhXIrVfaSIFxnscIBMPynGfhsMwQDRIBQw==} engines: {node: '>=12'} @@ -2077,6 +2124,15 @@ packages: dev: true optional: true + /@esbuild/android-x64/0.16.3: + resolution: {integrity: sha512-SFpTUcIT1bIJuCCBMCQWq1bL2gPTjWoLZdjmIhjdcQHaUfV41OQfho6Ici5uvvkMmZRXIUGpM3GxysP/EU7ifQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64/0.17.5: resolution: {integrity: sha512-8fI/AnIdmWz/+1iza2WrCw8kwXK9wZp/yZY/iS8ioC+U37yJCeppi9EHY05ewJKN64ASoBIseufZROtcFnX5GA==} engines: {node: '>=12'} @@ -2095,6 +2151,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64/0.16.3: + resolution: {integrity: sha512-DO8WykMyB+N9mIDfI/Hug70Dk1KipavlGAecxS3jDUwAbTpDXj0Lcwzw9svkhxfpCagDmpaTMgxWK8/C/XcXvw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64/0.17.5: resolution: {integrity: sha512-EAvaoyIySV6Iif3NQCglUNpnMfHSUgC5ugt2efl3+QDntucJe5spn0udNZjTgNi6tKVqSceOw9tQ32liNZc1Xw==} engines: {node: '>=12'} @@ -2113,6 +2178,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64/0.16.3: + resolution: {integrity: sha512-uEqZQ2omc6BvWqdCiyZ5+XmxuHEi1SPzpVxXCSSV2+Sh7sbXbpeNhHIeFrIpRjAs0lI1FmA1iIOxFozKBhKgRQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64/0.17.5: resolution: {integrity: sha512-ha7QCJh1fuSwwCgoegfdaljowwWozwTDjBgjD3++WAy/qwee5uUi1gvOg2WENJC6EUyHBOkcd3YmLDYSZ2TPPA==} engines: {node: '>=12'} @@ -2131,6 +2205,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64/0.16.3: + resolution: {integrity: sha512-nJansp3sSXakNkOD5i5mIz2Is/HjzIhFs49b1tjrPrpCmwgBmH9SSzhC/Z1UqlkivqMYkhfPwMw1dGFUuwmXhw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64/0.17.5: resolution: {integrity: sha512-VbdXJkn2aI2pQ/wxNEjEcnEDwPpxt3CWWMFYmO7CcdFBoOsABRy2W8F3kjbF9F/pecEUDcI3b5i2w+By4VQFPg==} engines: {node: '>=12'} @@ -2149,6 +2232,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64/0.16.3: + resolution: {integrity: sha512-TfoDzLw+QHfc4a8aKtGSQ96Wa+6eimljjkq9HKR0rHlU83vw8aldMOUSJTUDxbcUdcgnJzPaX8/vGWm7vyV7ug==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64/0.17.5: resolution: {integrity: sha512-olgGYND1/XnnWxwhjtY3/ryjOG/M4WfcA6XH8dBTH1cxMeBemMODXSFhkw71Kf4TeZFFTN25YOomaNh0vq2iXg==} engines: {node: '>=12'} @@ -2167,6 +2259,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm/0.16.3: + resolution: {integrity: sha512-VwswmSYwVAAq6LysV59Fyqk3UIjbhuc6wb3vEcJ7HEJUtFuLK9uXWuFoH1lulEbE4+5GjtHi3MHX+w1gNHdOWQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm/0.17.5: resolution: {integrity: sha512-YBdCyQwA3OQupi6W2/WO4FnI+NWFWe79cZEtlbqSESOHEg7a73htBIRiE6uHPQe7Yp5E4aALv+JxkRLGEUL7tw==} engines: {node: '>=12'} @@ -2185,6 +2286,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64/0.16.3: + resolution: {integrity: sha512-7I3RlsnxEFCHVZNBLb2w7unamgZ5sVwO0/ikE2GaYvYuUQs9Qte/w7TqWcXHtCwxvZx/2+F97ndiUQAWs47ZfQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64/0.17.5: resolution: {integrity: sha512-8a0bqSwu3OlLCfu2FBbDNgQyBYdPJh1B9PvNX7jMaKGC9/KopgHs37t+pQqeMLzcyRqG6z55IGNQAMSlCpBuqg==} engines: {node: '>=12'} @@ -2203,6 +2313,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32/0.16.3: + resolution: {integrity: sha512-X8FDDxM9cqda2rJE+iblQhIMYY49LfvW4kaEjoFbTTQ4Go8G96Smj2w3BRTwA8IHGoi9dPOPGAX63dhuv19UqA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32/0.17.5: resolution: {integrity: sha512-uCwm1r/+NdP7vndctgq3PoZrnmhmnecWAr114GWMRwg2QMFFX+kIWnp7IO220/JLgnXK/jP7VKAFBGmeOYBQYQ==} engines: {node: '>=12'} @@ -2221,6 +2340,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64/0.16.3: + resolution: {integrity: sha512-hIbeejCOyO0X9ujfIIOKjBjNAs9XD/YdJ9JXAy1lHA+8UXuOqbFe4ErMCqMr8dhlMGBuvcQYGF7+kO7waj2KHw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64/0.17.5: resolution: {integrity: sha512-3YxhSBl5Sb6TtBjJu+HP93poBruFzgXmf3PVfIe4xOXMj1XpxboYZyw3W8BhoX/uwxzZz4K1I99jTE/5cgDT1g==} engines: {node: '>=12'} @@ -2239,6 +2367,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el/0.16.3: + resolution: {integrity: sha512-znFRzICT/V8VZQMt6rjb21MtAVJv/3dmKRMlohlShrbVXdBuOdDrGb+C2cZGQAR8RFyRe7HS6klmHq103WpmVw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el/0.17.5: resolution: {integrity: sha512-Hy5Z0YVWyYHdtQ5mfmfp8LdhVwGbwVuq8mHzLqrG16BaMgEmit2xKO+iDakHs+OetEx0EN/2mUzDdfdktI+Nmg==} engines: {node: '>=12'} @@ -2257,6 +2394,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64/0.16.3: + resolution: {integrity: sha512-EV7LuEybxhXrVTDpbqWF2yehYRNz5e5p+u3oQUS2+ZFpknyi1NXxr8URk4ykR8Efm7iu04//4sBg249yNOwy5Q==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64/0.17.5: resolution: {integrity: sha512-5dbQvBLbU/Y3Q4ABc9gi23hww1mQcM7KZ9KBqabB7qhJswYMf8WrDDOSw3gdf3p+ffmijMd28mfVMvFucuECyg==} engines: {node: '>=12'} @@ -2275,6 +2421,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64/0.16.3: + resolution: {integrity: sha512-uDxqFOcLzFIJ+r/pkTTSE9lsCEaV/Y6rMlQjUI9BkzASEChYL/aSQjZjchtEmdnVxDKETnUAmsaZ4pqK1eE5BQ==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64/0.17.5: resolution: {integrity: sha512-fp/KUB/ZPzEWGTEUgz9wIAKCqu7CjH1GqXUO2WJdik1UNBQ7Xzw7myIajpxztE4Csb9504ERiFMxZg5KZ6HlZQ==} engines: {node: '>=12'} @@ -2293,6 +2448,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x/0.16.3: + resolution: {integrity: sha512-NbeREhzSxYwFhnCAQOQZmajsPYtX71Ufej3IQ8W2Gxskfz9DK58ENEju4SbpIj48VenktRASC52N5Fhyf/aliQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x/0.17.5: resolution: {integrity: sha512-kRV3yw19YDqHTp8SfHXfObUFXlaiiw4o2lvT1XjsPZ++22GqZwSsYWJLjMi1Sl7j9qDlDUduWDze/nQx0d6Lzw==} engines: {node: '>=12'} @@ -2311,6 +2475,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64/0.16.3: + resolution: {integrity: sha512-SDiG0nCixYO9JgpehoKgScwic7vXXndfasjnD5DLbp1xltANzqZ425l7LSdHynt19UWOcDjG9wJJzSElsPvk0w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64/0.17.5: resolution: {integrity: sha512-vnxuhh9e4pbtABNLbT2ANW4uwQ/zvcHRCm1JxaYkzSehugoFd5iXyC4ci1nhXU13mxEwCnrnTIiiSGwa/uAF1g==} engines: {node: '>=12'} @@ -2329,6 +2502,15 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64/0.16.3: + resolution: {integrity: sha512-AzbsJqiHEq1I/tUvOfAzCY15h4/7Ivp3ff/o1GpP16n48JMNAtbW0qui2WCgoIZArEHD0SUQ95gvR0oSO7ZbdA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64/0.17.5: resolution: {integrity: sha512-cigBpdiSx/vPy7doUyImsQQBnBjV5f1M99ZUlaJckDAJjgXWl6y9W17FIfJTy8TxosEF6MXq+fpLsitMGts2nA==} engines: {node: '>=12'} @@ -2347,6 +2529,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64/0.16.3: + resolution: {integrity: sha512-gSABi8qHl8k3Cbi/4toAzHiykuBuWLZs43JomTcXkjMZVkp0gj3gg9mO+9HJW/8GB5H89RX/V0QP4JGL7YEEVg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64/0.17.5: resolution: {integrity: sha512-VdqRqPVIjjZfkf40LrqOaVuhw9EQiAZ/GNCSM2UplDkaIzYVsSnycxcFfAnHdWI8Gyt6dO15KHikbpxwx+xHbw==} engines: {node: '>=12'} @@ -2365,6 +2556,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64/0.16.3: + resolution: {integrity: sha512-SF9Kch5Ete4reovvRO6yNjMxrvlfT0F0Flm+NPoUw5Z4Q3r1d23LFTgaLwm3Cp0iGbrU/MoUI+ZqwCv5XJijCw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64/0.17.5: resolution: {integrity: sha512-ItxPaJ3MBLtI4nK+mALLEoUs6amxsx+J1ibnfcYMkqaCqHST1AkF4aENpBehty3czqw64r/XqL+W9WqU6kc2Qw==} engines: {node: '>=12'} @@ -2383,6 +2583,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64/0.16.3: + resolution: {integrity: sha512-u5aBonZIyGopAZyOnoPAA6fGsDeHByZ9CnEzyML9NqntK6D/xl5jteZUKm/p6nD09+v3pTM6TuUIqSPcChk5gg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64/0.17.5: resolution: {integrity: sha512-4u2Q6qsJTYNFdS9zHoAi80spzf78C16m2wla4eJPh4kSbRv+BpXIfl6TmBSWupD8e47B1NrTfrOlEuco7mYQtg==} engines: {node: '>=12'} @@ -2401,6 +2610,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32/0.16.3: + resolution: {integrity: sha512-GlgVq1WpvOEhNioh74TKelwla9KDuAaLZrdxuuUgsP2vayxeLgVc+rbpIv0IYF4+tlIzq2vRhofV+KGLD+37EQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32/0.17.5: resolution: {integrity: sha512-KYlm+Xu9TXsfTWAcocLuISRtqxKp/Y9ZBVg6CEEj0O5J9mn7YvBKzAszo2j1ndyzUPk+op+Tie2PJeN+BnXGqQ==} engines: {node: '>=12'} @@ -2419,6 +2637,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64/0.16.3: + resolution: {integrity: sha512-5/JuTd8OWW8UzEtyf19fbrtMJENza+C9JoPIkvItgTBQ1FO2ZLvjbPO6Xs54vk0s5JB5QsfieUEshRQfu7ZHow==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64/0.17.5: resolution: {integrity: sha512-XgA9qWRqby7xdYXuF6KALsn37QGBMHsdhmnpjfZtYxKxbTOwfnDM6MYi2WuUku5poNaX2n9XGVr20zgT/2QwCw==} engines: {node: '>=12'} @@ -2495,8 +2722,9 @@ packages: dependencies: '@ipld/dag-cbor': 9.0.0 cborg: 1.10.0 - multiformats: 11.0.1 + multiformats: 11.0.2 varint: 6.0.0 + dev: false /@ipld/car/5.1.1: resolution: {integrity: sha512-HoFTUqUJL9cPGhC9qRmHCvamfIsj1JllQSQ/Xu9/KN/VNJp8To9Ms4qiZPEMOwcrNFclfYqrahjGYbf4KL/d9A==} @@ -2506,21 +2734,20 @@ packages: cborg: 1.10.0 multiformats: 11.0.2 varint: 6.0.0 - dev: false /@ipld/dag-cbor/9.0.0: resolution: {integrity: sha512-zdsiSiYDEOIDW7mmWOYWC9gukjXO+F8wqxz/LfN7iSwTfIyipC8+UQrCbPupFMRb/33XQTZk8yl3My8vUQBRoA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: cborg: 1.10.0 - multiformats: 11.0.1 + multiformats: 11.0.2 /@ipld/dag-json/10.0.1: resolution: {integrity: sha512-XE1Eqw3eNVrSfOhtqCM/gwCxEgYFBzkDlkwhEeMmMvhd0rLBfSyVzXbahZSlv97tiTPEIx5rt41gcFAda3W8zg==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: cborg: 1.10.0 - multiformats: 11.0.1 + multiformats: 11.0.2 /@ipld/dag-pb/4.0.2: resolution: {integrity: sha512-me9oEPb7UNPWSplUFCXyxnQE3/WlsjOljqO2RZN44XOmGkBY0/WVklbXorVE1eiv0Rt3p6dBS2x36Rq8A0Am8A==} @@ -2533,7 +2760,7 @@ packages: dependencies: '@ipld/dag-cbor': 9.0.0 '@ipld/dag-json': 10.0.1 - multiformats: 11.0.1 + multiformats: 11.0.2 /@ipld/unixfs/2.1.1: resolution: {integrity: sha512-g3gr/3XvfQs4x2VFjlICae09ul5fbWCKRInN6Vgeot2+GH0h/krr3PqZCIo4dy4Ou2mQOsIddxUvG8UZ4p9SbQ==} @@ -2543,7 +2770,7 @@ packages: '@perma/map': 1.0.2 '@web-std/stream': 1.0.1 actor: 2.3.1 - multiformats: 11.0.1 + multiformats: 11.0.2 protobufjs: 7.2.0 rabin-rs: 2.1.0 dev: false @@ -2553,21 +2780,21 @@ packages: engines: {node: '>=8'} dev: true - /@jest/schemas/29.4.0: - resolution: {integrity: sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ==} + /@jest/schemas/29.4.3: + resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@sinclair/typebox': 0.25.21 + '@sinclair/typebox': 0.25.24 dev: true - /@jest/types/29.4.1: - resolution: {integrity: sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA==} + /@jest/types/29.5.0: + resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.4.0 + '@jest/schemas': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.11.18 + '@types/node': 18.15.3 '@types/yargs': 17.0.22 chalk: 4.1.2 dev: true @@ -2656,6 +2883,16 @@ packages: undici: 5.9.1 dev: true + /@miniflare/cache/2.12.1: + resolution: {integrity: sha512-6Pj5avy53qULTa13gWxGTDBuwX0yAzr4Zkzb0ZBh40bcbHp4vRkOk7PvHBoxV0M76JxQDHotGaW+ik510z5Xrg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.12.1 + '@miniflare/shared': 2.12.1 + http-cache-semantics: 4.1.1 + undici: 5.20.0 + dev: true + /@miniflare/cli-parser/2.11.0: resolution: {integrity: sha512-JUmyRzEGAS6CouvXJwBh8p44onfw3KRpfq5JGXEuHModOGjTp6li7PQyCTNPV2Hv/7StAXWnTFGXeAqyDHuTig==} engines: {node: '>=16.13'} @@ -2664,6 +2901,14 @@ packages: kleur: 4.1.5 dev: true + /@miniflare/cli-parser/2.12.1: + resolution: {integrity: sha512-iCh4wEyQow8Dha+zpKhjCCXEp6QWbsvE18H5CgeUFT1pX4B+akYIHzdn47Cr5zpuYyjenoL78bAz0IIHIeyeWw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.12.1 + kleur: 4.1.5 + dev: true + /@miniflare/core/2.11.0: resolution: {integrity: sha512-UFMFiCG0co36VpZkgFrSBnrxo71uf1x+cjlzzJi3khmMyDlnLu4RuIQsAqvKbYom6fi3G9Q8lTgM7JuOXFyjhw==} engines: {node: '>=16.13'} @@ -2680,6 +2925,22 @@ packages: urlpattern-polyfill: 4.0.3 dev: true + /@miniflare/core/2.12.1: + resolution: {integrity: sha512-729xXL6uoMgtja5J7B2WdWAjFfxb74Pk2QqM3VqkWqY3XNlKWI7+ofvb8S6kI6uFEPGj4ma263uYkEAgsvzBWg==} + engines: {node: '>=16.13'} + dependencies: + '@iarna/toml': 2.2.5 + '@miniflare/queues': 2.12.1 + '@miniflare/shared': 2.12.1 + '@miniflare/watcher': 2.12.1 + busboy: 1.6.0 + dotenv: 10.0.0 + kleur: 4.1.5 + set-cookie-parser: 2.5.1 + undici: 5.20.0 + urlpattern-polyfill: 4.0.3 + dev: true + /@miniflare/d1/2.11.0: resolution: {integrity: sha512-aDdBVQZ2C0Zs3+Y9ZbRctmuQxozPfpumwJ/6NG6fBadANvune/hW7ddEoxyteIEU9W3IgzVj8s4by4VvasX90A==} engines: {node: '>=16.7'} @@ -2688,6 +2949,14 @@ packages: '@miniflare/shared': 2.11.0 dev: true + /@miniflare/d1/2.12.1: + resolution: {integrity: sha512-2ldT7xEC7KxoaEJ7nCY9/AB/xwPjbm3mrmpiIspT0b5OgS640Pe9EU4c5bSmzGoUbLvwF+jb+LhLE1QaEbWkBw==} + engines: {node: '>=16.7'} + dependencies: + '@miniflare/core': 2.12.1 + '@miniflare/shared': 2.12.1 + dev: true + /@miniflare/durable-objects/2.11.0: resolution: {integrity: sha512-0cKJaMgraTEU1b4kqK8cjD2oTeOjA6QU3Y+lWiZT/k1PMHZULovrSFnjii7qZ8npf4VHSIN6XYPxhyxRyEM65Q==} engines: {node: '>=16.13'} @@ -2698,6 +2967,16 @@ packages: undici: 5.9.1 dev: true + /@miniflare/durable-objects/2.12.1: + resolution: {integrity: sha512-/n9WIxvHavVUgT+Nf280wNOcmJQBG+eZuqOlORWW9RmXXbAzqzS2Mk2lmRDCzbq3xTXAcsndx6cdarQLNRUzBg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.12.1 + '@miniflare/shared': 2.12.1 + '@miniflare/storage-memory': 2.12.1 + undici: 5.20.0 + dev: true + /@miniflare/html-rewriter/2.11.0: resolution: {integrity: sha512-olTqmuYTHnoTNtiA0vjQ/ixRfbwgPzDrAUbtXDCYW45VFbHfDVJrJGZX3Jg0HpSlxy86Zclle1SUxGbVDzxsBg==} engines: {node: '>=16.13'} @@ -2708,6 +2987,16 @@ packages: undici: 5.9.1 dev: true + /@miniflare/html-rewriter/2.12.1: + resolution: {integrity: sha512-yezYzGRBxy7d/oomAUEftdnL4fq6YIek82LtQlXgzcdcbBDnkYADj8WqGV41tAI+V2+rjrFEc1RuCXx/I1yISw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.12.1 + '@miniflare/shared': 2.12.1 + html-rewriter-wasm: 0.4.1 + undici: 5.20.0 + dev: true + /@miniflare/http-server/2.11.0: resolution: {integrity: sha512-sMLcrDFzqqAvnQmAUH0hRTo8sBjW79VZYfnIH5FAGSGcKX6kdAGs9RStdYZ4CftQCBAEQScX0KBsMx5FwJRe9Q==} engines: {node: '>=16.13'} @@ -2725,6 +3014,23 @@ packages: - utf-8-validate dev: true + /@miniflare/http-server/2.12.1: + resolution: {integrity: sha512-nC6POgDKFHxnyXbKCdR9FGZSsu5frXQUETvSVcoETd5RP+Iws0xZ+XkzVMqiiIZk3ifUC9LzdGUOD0J2PlhHJw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.12.1 + '@miniflare/shared': 2.12.1 + '@miniflare/web-sockets': 2.12.1 + kleur: 4.1.5 + selfsigned: 2.1.1 + undici: 5.20.0 + ws: 8.13.0 + youch: 2.2.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@miniflare/kv/2.11.0: resolution: {integrity: sha512-3m9dL2HBBN170V1JvwjjucR5zl4G3mlcsV6C1E7A2wLl2Z2TWvIx/tSY9hrhkD96dFnejwJ9qmPMbXMMuynhjg==} engines: {node: '>=16.13'} @@ -2732,6 +3038,13 @@ packages: '@miniflare/shared': 2.11.0 dev: true + /@miniflare/kv/2.12.1: + resolution: {integrity: sha512-8h8wLDMEaWaKAqYTwrckOcNjAz52bzDyLmU4t/lh1/AQOE9eSg/T+H6xQCv0fPGrWPeHmG8iXaFI1JQ+CtkcHw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.12.1 + dev: true + /@miniflare/queues/2.11.0: resolution: {integrity: sha512-fLHjdrNLKhn0LZM/aii/9GsAttFd+lWlGzK8HOg1R0vhfKBwEub4zntjMmOfFbDm1ntc21tdMK7n3ldUphwh5w==} engines: {node: '>=16.7'} @@ -2739,6 +3052,13 @@ packages: '@miniflare/shared': 2.11.0 dev: true + /@miniflare/queues/2.12.1: + resolution: {integrity: sha512-L/YJkWWvg1RS3sCB5DLZOsf/kAmkwhvshpl+LmGQT7z/PYXlplbBmuhPwVBXaHqZdYE7063XfTzgAIhVPoo72Q==} + engines: {node: '>=16.7'} + dependencies: + '@miniflare/shared': 2.12.1 + dev: true + /@miniflare/r2/2.11.0: resolution: {integrity: sha512-MKuyJ/gGNsK3eWbGdygvozqcyaZhM3C6NGHvoaZwH503dwN569j5DpatTWiHGFeDeSu64VqcIsGehz05GDUaag==} engines: {node: '>=16.13'} @@ -2747,6 +3067,14 @@ packages: undici: 5.9.1 dev: true + /@miniflare/r2/2.12.1: + resolution: {integrity: sha512-xp8fSSap6o5xSAWp9BtOGgZ4tuf5iHTqrfbAH66LF151j8y69eQtQJ5pxpSvrDJok/F1VOLGc4ihSLmUqxyXhw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.12.1 + undici: 5.20.0 + dev: true + /@miniflare/runner-vm/2.11.0: resolution: {integrity: sha512-bkVSuvCf5+VylqN8lTiLxIYqYcKFbl+BywZGwGQndPC/3wh42J00mM0jw4hRbvXgwuBhlUyCVpEXtYlftFFT/g==} engines: {node: '>=16.13'} @@ -2754,6 +3082,13 @@ packages: '@miniflare/shared': 2.11.0 dev: true + /@miniflare/runner-vm/2.12.1: + resolution: {integrity: sha512-pGY/aoQzbvyXOGR6/d3hv5/QsyUXGGbOxAyXdvjlz8h7ZiKOX4dBRS5TUAPS0kb/ofUWCyoYJi8dCVwRGdTYRw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.12.1 + dev: true + /@miniflare/scheduler/2.11.0: resolution: {integrity: sha512-DPdzINhdWeS99eIicGoluMsD4pLTTAWNQbgCv3CTwgdKA3dxdvMSCkNqZzQLiALzvk9+rSfj46FlH++HE7o7/w==} engines: {node: '>=16.13'} @@ -2763,6 +3098,15 @@ packages: cron-schedule: 3.0.6 dev: true + /@miniflare/scheduler/2.12.1: + resolution: {integrity: sha512-AbOP8YpWNqR/t7zMuTmn6q27USCDBQaYaULRVaNNfCsxMTXAUjYfM85iFvnV9mshw+K0HIEU4zR4Xjd2FeJubg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.12.1 + '@miniflare/shared': 2.12.1 + cron-schedule: 3.0.6 + dev: true + /@miniflare/shared/2.11.0: resolution: {integrity: sha512-fWMqq3ZkWAg+k7CnyzMV/rZHugwn+/JxvVzCxrtvxzwotTN547THlOxgZe8JAP23U9BiTxOfpTfnLvFEjAmegw==} engines: {node: '>=16.13'} @@ -2773,6 +3117,16 @@ packages: picomatch: 2.3.1 dev: true + /@miniflare/shared/2.12.1: + resolution: {integrity: sha512-N8sHNM5vcvjvO+znQ7Mbqf0FChRlWxy/svUpQf1GGpii9aTXzOTWB+WkFvJrJNx44SUReEGxUAzxpdeWnHahmA==} + engines: {node: '>=16.13'} + dependencies: + '@types/better-sqlite3': 7.6.3 + kleur: 4.1.5 + npx-import: 1.1.4 + picomatch: 2.3.1 + dev: true + /@miniflare/sites/2.11.0: resolution: {integrity: sha512-qbefKdWZUJgsdLf+kCw03sn3h/92LZgJAbkOpP6bCrfWkXlJ37EQXO4KWdhn4Ghc7A6GwU1s1I/mdB64B3AewQ==} engines: {node: '>=16.13'} @@ -2782,6 +3136,15 @@ packages: '@miniflare/storage-file': 2.11.0 dev: true + /@miniflare/sites/2.12.1: + resolution: {integrity: sha512-LW4r82cfGJvmJFwoBdXfsRcdDggVf8ppjMZGU3zk7xo+u5yD1uHzO2Arf3XbKNiOp7f9WyC/mXxs4zxF605iLA==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/kv': 2.12.1 + '@miniflare/shared': 2.12.1 + '@miniflare/storage-file': 2.12.1 + dev: true + /@miniflare/storage-file/2.11.0: resolution: {integrity: sha512-beWF/lTX74x7AiaSB+xQxywPSNdhtEKvqDkRui8eOJ5kqN2o4UaleLKQGgqmCw3WyHRIsckV7If1qpbNiLtWMw==} engines: {node: '>=16.13'} @@ -2790,6 +3153,14 @@ packages: '@miniflare/storage-memory': 2.11.0 dev: true + /@miniflare/storage-file/2.12.1: + resolution: {integrity: sha512-eq5wzBwxQC5GVxBfji9svb9FRdSOlA8D8DTgzL29DDjuOYtG9j8ydOlo0J7/2MB/Gq0HYFUHYWHhrklzzwdKQQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.12.1 + '@miniflare/storage-memory': 2.12.1 + dev: true + /@miniflare/storage-memory/2.11.0: resolution: {integrity: sha512-s0AhPww7fq/Jz80NbPb+ffhcVRKnfPi7H1dHTRTre2Ud23EVJjAWl2gat42x8NOT/Fu3/o/7A72DWQQJqfO98A==} engines: {node: '>=16.13'} @@ -2797,6 +3168,13 @@ packages: '@miniflare/shared': 2.11.0 dev: true + /@miniflare/storage-memory/2.12.1: + resolution: {integrity: sha512-E9jbrX0L9N7YIHXj2G4td1EKboVLBdHkwh7RvKEZBwOhxDze5h+jMOou57NIbfC5kLOZPOC1fGXjzpp7xUUE6w==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.12.1 + dev: true + /@miniflare/watcher/2.11.0: resolution: {integrity: sha512-RUfjz2iYcsQXLcGySemJl98CJ2iierbWsPGWZhIVZI+NNhROkEy77g/Q+lvP2ATwexG3/dUSfdJ3P8aH+sI4Ig==} engines: {node: '>=16.13'} @@ -2804,6 +3182,13 @@ packages: '@miniflare/shared': 2.11.0 dev: true + /@miniflare/watcher/2.12.1: + resolution: {integrity: sha512-3IG/6g38id5ppbZHB/gMfEvoIEFYdmTTLRsHaPNyWIk/r3LMhHLluVsMcs+Lr/fphkPk6Diou4cBLD2GeeoP7A==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.12.1 + dev: true + /@miniflare/web-sockets/2.11.0: resolution: {integrity: sha512-NC8RKrmxrO0hZmwpzn5g4hPGA2VblnFTIBobmWoxuK95eW49zfs7dtE/PyFs+blsGv3CjTIjHVSQ782K+C6HFA==} engines: {node: '>=16.13'} @@ -2817,6 +3202,19 @@ packages: - utf-8-validate dev: true + /@miniflare/web-sockets/2.12.1: + resolution: {integrity: sha512-Z+zqZqhVdrbmTQf+ETP5H1TPdXC2tUiYPiHRLWTHUks6VVkuwnUtIKxNPBEBXjCjKYYEm8VLclUAt+0yTucLWA==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.12.1 + '@miniflare/shared': 2.12.1 + undici: 5.20.0 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@multiformats/murmur3/2.1.2: resolution: {integrity: sha512-4gCptOviYuu8ts5iUPwAcyIgl1FAyOAtWkQMAdu7FpgWveV5uVmA/919+QhgiZu8lhBGLWvRRTigOEdYNX9y0A==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} @@ -2829,7 +3227,7 @@ packages: resolution: {integrity: sha512-YvLK1IrLnRckPsvXhOkZjaIGNonsEdD1dL3NPSaLilV/WjVYeBgnNZXTUsaPzFXGrIFM7motx+yCmmqzXO6gtQ==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - multiformats: 11.0.1 + multiformats: 11.0.2 murmurhash3js-revisited: 3.0.0 dev: false @@ -2990,8 +3388,8 @@ packages: resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} dev: true - /@sinclair/typebox/0.25.21: - resolution: {integrity: sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==} + /@sinclair/typebox/0.25.24: + resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} dev: true /@sindresorhus/is/0.14.0: @@ -3008,101 +3406,101 @@ packages: webpack-sources: 3.2.3 dev: true - /@svgr/babel-plugin-add-jsx-attribute/6.5.1_@babel+core@7.20.12: + /@svgr/babel-plugin-add-jsx-attribute/6.5.1_@babel+core@7.21.3: resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-plugin-remove-jsx-attribute/6.5.0_@babel+core@7.20.12: + /@svgr/babel-plugin-remove-jsx-attribute/6.5.0_@babel+core@7.21.3: resolution: {integrity: sha512-8zYdkym7qNyfXpWvu4yq46k41pyNM9SOstoWhKlm+IfdCE1DdnRKeMUPsWIEO/DEkaWxJ8T9esNdG3QwQ93jBA==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression/6.5.0_@babel+core@7.20.12: + /@svgr/babel-plugin-remove-jsx-empty-expression/6.5.0_@babel+core@7.21.3: resolution: {integrity: sha512-NFdxMq3xA42Kb1UbzCVxplUc0iqSyM9X8kopImvFnB+uSDdzIHOdbs1op8ofAvVRtbg4oZiyRl3fTYeKcOe9Iw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value/6.5.1_@babel+core@7.20.12: + /@svgr/babel-plugin-replace-jsx-attribute-value/6.5.1_@babel+core@7.21.3: resolution: {integrity: sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-plugin-svg-dynamic-title/6.5.1_@babel+core@7.20.12: + /@svgr/babel-plugin-svg-dynamic-title/6.5.1_@babel+core@7.21.3: resolution: {integrity: sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-plugin-svg-em-dimensions/6.5.1_@babel+core@7.20.12: + /@svgr/babel-plugin-svg-em-dimensions/6.5.1_@babel+core@7.21.3: resolution: {integrity: sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-plugin-transform-react-native-svg/6.5.1_@babel+core@7.20.12: + /@svgr/babel-plugin-transform-react-native-svg/6.5.1_@babel+core@7.21.3: resolution: {integrity: sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-plugin-transform-svg-component/6.5.1_@babel+core@7.20.12: + /@svgr/babel-plugin-transform-svg-component/6.5.1_@babel+core@7.21.3: resolution: {integrity: sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 dev: true - /@svgr/babel-preset/6.5.1_@babel+core@7.20.12: + /@svgr/babel-preset/6.5.1_@babel+core@7.21.3: resolution: {integrity: sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@svgr/babel-plugin-add-jsx-attribute': 6.5.1_@babel+core@7.20.12 - '@svgr/babel-plugin-remove-jsx-attribute': 6.5.0_@babel+core@7.20.12 - '@svgr/babel-plugin-remove-jsx-empty-expression': 6.5.0_@babel+core@7.20.12 - '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1_@babel+core@7.20.12 - '@svgr/babel-plugin-svg-dynamic-title': 6.5.1_@babel+core@7.20.12 - '@svgr/babel-plugin-svg-em-dimensions': 6.5.1_@babel+core@7.20.12 - '@svgr/babel-plugin-transform-react-native-svg': 6.5.1_@babel+core@7.20.12 - '@svgr/babel-plugin-transform-svg-component': 6.5.1_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@svgr/babel-plugin-add-jsx-attribute': 6.5.1_@babel+core@7.21.3 + '@svgr/babel-plugin-remove-jsx-attribute': 6.5.0_@babel+core@7.21.3 + '@svgr/babel-plugin-remove-jsx-empty-expression': 6.5.0_@babel+core@7.21.3 + '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1_@babel+core@7.21.3 + '@svgr/babel-plugin-svg-dynamic-title': 6.5.1_@babel+core@7.21.3 + '@svgr/babel-plugin-svg-em-dimensions': 6.5.1_@babel+core@7.21.3 + '@svgr/babel-plugin-transform-react-native-svg': 6.5.1_@babel+core@7.21.3 + '@svgr/babel-plugin-transform-svg-component': 6.5.1_@babel+core@7.21.3 dev: true /@svgr/core/6.5.1: resolution: {integrity: sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.20.12 - '@svgr/babel-preset': 6.5.1_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@svgr/babel-preset': 6.5.1_@babel+core@7.21.3 '@svgr/plugin-jsx': 6.5.1_@svgr+core@6.5.1 camelcase: 6.3.0 cosmiconfig: 7.1.0 @@ -3114,7 +3512,7 @@ packages: resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} engines: {node: '>=10'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.21.3 entities: 4.4.0 dev: true @@ -3124,8 +3522,8 @@ packages: peerDependencies: '@svgr/core': ^6.0.0 dependencies: - '@babel/core': 7.20.12 - '@svgr/babel-preset': 6.5.1_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@svgr/babel-preset': 6.5.1_@babel+core@7.21.3 '@svgr/core': 6.5.1 '@svgr/hast-util-to-babel-ast': 6.5.1 svg-parser: 2.0.4 @@ -3149,11 +3547,11 @@ packages: resolution: {integrity: sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-transform-react-constant-elements': 7.20.2_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@babel/preset-typescript': 7.18.6_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/plugin-transform-react-constant-elements': 7.21.3_@babel+core@7.21.3 + '@babel/preset-env': 7.20.2_@babel+core@7.21.3 + '@babel/preset-react': 7.18.6_@babel+core@7.21.3 + '@babel/preset-typescript': 7.21.0_@babel+core@7.21.3 '@svgr/core': 6.5.1 '@svgr/plugin-jsx': 6.5.1_@svgr+core@6.5.1 '@svgr/plugin-svgo': 6.5.1_@svgr+core@6.5.1 @@ -3180,33 +3578,33 @@ packages: /@types/better-sqlite3/7.6.3: resolution: {integrity: sha512-YS64N9SNDT/NAvou3QNdzAu3E2om/W/0dhORimtPGLef+zSK5l1vDzfsWb4xgXOgfhtOI5ZDTRxnvRPb22AIVQ==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/body-parser/1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/bonjour/3.5.10: resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/connect-history-api-fallback/1.3.5: resolution: {integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==} dependencies: '@types/express-serve-static-core': 4.17.33 - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/connect/3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/cookie/0.5.0: @@ -3216,12 +3614,12 @@ packages: /@types/eslint-scope/3.7.4: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: - '@types/eslint': 8.4.10 + '@types/eslint': 8.21.2 '@types/estree': 0.0.51 dev: true - /@types/eslint/8.4.10: - resolution: {integrity: sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==} + /@types/eslint/8.21.2: + resolution: {integrity: sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw==} dependencies: '@types/estree': 0.0.51 '@types/json-schema': 7.0.11 @@ -3234,18 +3632,18 @@ packages: /@types/express-serve-static-core/4.17.33: resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true - /@types/express/4.17.16: - resolution: {integrity: sha512-LkKpqRZ7zqXJuvoELakaFYuETHjZkSol8EV6cNnyishutDBCCdv6+dsKPbKkCcIk57qRphOLY5sEgClw1bO3gA==} + /@types/express/4.17.17: + resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} dependencies: '@types/body-parser': 1.19.2 '@types/express-serve-static-core': 4.17.33 '@types/qs': 6.9.7 - '@types/serve-static': 1.15.0 + '@types/serve-static': 1.15.1 dev: true /@types/git-rev-sync/2.0.0: @@ -3262,10 +3660,10 @@ packages: resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} dev: true - /@types/http-proxy/1.17.9: - resolution: {integrity: sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==} + /@types/http-proxy/1.17.10: + resolution: {integrity: sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/inquirer/9.0.3: @@ -3302,7 +3700,7 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/mdast/3.0.10: @@ -3326,6 +3724,10 @@ packages: /@types/node/18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} + /@types/node/18.15.3: + resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} + dev: true + /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true @@ -3366,7 +3768,7 @@ packages: /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/retry/0.12.0: @@ -3388,20 +3790,20 @@ packages: /@types/serve-index/1.9.1: resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==} dependencies: - '@types/express': 4.17.16 + '@types/express': 4.17.17 dev: true - /@types/serve-static/1.15.0: - resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} + /@types/serve-static/1.15.1: + resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 3.0.1 - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/sockjs/0.3.33: resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/stack-trace/0.0.29: @@ -3427,7 +3829,7 @@ packages: /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 dev: true /@types/yargs-parser/21.0.0: @@ -3587,32 +3989,32 @@ packages: resolution: {integrity: sha512-Rr2q3ARDmiaaVnvNkPNsIhS+1ORyCqQhGRSqf8ugfJVmvbvjLFA6EYxjknUdg5tqt0aVnYTNuZ/GwIxaqMzliA==} dependencies: '@ucanto/interface': 6.2.0 - multiformats: 11.0.1 + multiformats: 11.0.2 /@ucanto/core/5.1.0: resolution: {integrity: sha512-vunFhsBgeioZhLGmLqpEeydXeCVNoYEClQLivMMa5jZP0B7GnBoMSYKc95n8iX6n6mYb3mKFpWQ0BGKJUxrK/A==} dependencies: - '@ipld/car': 5.1.0 + '@ipld/car': 5.1.1 '@ipld/dag-cbor': 9.0.0 '@ipld/dag-ucan': 3.2.0 '@ucanto/interface': 6.2.0 - multiformats: 11.0.1 + multiformats: 11.0.2 dev: true /@ucanto/core/5.2.0: resolution: {integrity: sha512-Eblo2LfJyojRKmBk5/w25u1hhSCs6K3zUH/zNknwTrJg7CJYxw0hgsGcXrlkQf1TnSRzJVFEduK1ZzYCV55/Uw==} dependencies: - '@ipld/car': 5.1.0 + '@ipld/car': 5.1.1 '@ipld/dag-cbor': 9.0.0 '@ipld/dag-ucan': 3.2.0 '@ucanto/interface': 6.2.0 - multiformats: 11.0.1 + multiformats: 11.0.2 /@ucanto/interface/6.2.0: resolution: {integrity: sha512-b37bjTxNWQE+O4f18fvb7/woe41Dvb4AfdbevPLmaJj1fZogssH9fVgWlZdVg8ZsJQhMxRyHDuH40QAvuKRR1w==} dependencies: '@ipld/dag-ucan': 3.2.0 - multiformats: 11.0.1 + multiformats: 11.0.2 /@ucanto/principal/5.1.0: resolution: {integrity: sha512-niZzojYPYAgdszTmra82wGbl5YGHugUblMTPrEjuz3RFjXDCxW50IJFwzus3Z6k6Q6zIbXPU+yVxTbzJWg8/JA==} @@ -3620,7 +4022,7 @@ packages: '@ipld/dag-ucan': 3.2.0 '@noble/ed25519': 1.7.3 '@ucanto/interface': 6.2.0 - multiformats: 11.0.1 + multiformats: 11.0.2 one-webcrypto: 1.0.3 /@ucanto/server/6.1.0: @@ -3643,62 +4045,62 @@ packages: /@ucanto/validator/6.1.0: resolution: {integrity: sha512-vZ40paByLgosllG+YfuI4eD7m3KyYG1ebEa9jZEkLDYjWh7WWBtYvBn40pziIiLfBCzum2zU1uP1SMOf63EqqQ==} dependencies: - '@ipld/car': 5.1.0 + '@ipld/car': 5.1.1 '@ipld/dag-cbor': 9.0.0 '@ucanto/core': 5.2.0 '@ucanto/interface': 6.2.0 - multiformats: 11.0.1 + multiformats: 11.0.2 - /@vue/compiler-core/3.2.45: - resolution: {integrity: sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==} + /@vue/compiler-core/3.2.47: + resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: '@babel/parser': 7.16.4 - '@vue/shared': 3.2.45 + '@vue/shared': 3.2.47 estree-walker: 2.0.2 source-map: 0.6.1 dev: false - /@vue/compiler-dom/3.2.45: - resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==} + /@vue/compiler-dom/3.2.47: + resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} dependencies: - '@vue/compiler-core': 3.2.45 - '@vue/shared': 3.2.45 + '@vue/compiler-core': 3.2.47 + '@vue/shared': 3.2.47 dev: false - /@vue/compiler-sfc/3.2.45: - resolution: {integrity: sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==} + /@vue/compiler-sfc/3.2.47: + resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} dependencies: '@babel/parser': 7.16.4 - '@vue/compiler-core': 3.2.45 - '@vue/compiler-dom': 3.2.45 - '@vue/compiler-ssr': 3.2.45 - '@vue/reactivity-transform': 3.2.45 - '@vue/shared': 3.2.45 + '@vue/compiler-core': 3.2.47 + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-ssr': 3.2.47 + '@vue/reactivity-transform': 3.2.47 + '@vue/shared': 3.2.47 estree-walker: 2.0.2 magic-string: 0.25.9 postcss: 8.4.21 source-map: 0.6.1 dev: false - /@vue/compiler-ssr/3.2.45: - resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==} + /@vue/compiler-ssr/3.2.47: + resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} dependencies: - '@vue/compiler-dom': 3.2.45 - '@vue/shared': 3.2.45 + '@vue/compiler-dom': 3.2.47 + '@vue/shared': 3.2.47 dev: false - /@vue/reactivity-transform/3.2.45: - resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==} + /@vue/reactivity-transform/3.2.47: + resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} dependencies: '@babel/parser': 7.16.4 - '@vue/compiler-core': 3.2.45 - '@vue/shared': 3.2.45 + '@vue/compiler-core': 3.2.47 + '@vue/shared': 3.2.47 estree-walker: 2.0.2 magic-string: 0.25.9 dev: false - /@vue/shared/3.2.45: - resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==} + /@vue/shared/3.2.47: + resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} dev: false /@web-std/blob/3.0.4: @@ -4030,6 +4432,9 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} + /ansi-sequence-parser/1.1.0: + resolution: {integrity: sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==} + /ansi-styles/3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -4186,15 +4591,15 @@ packages: engines: {node: '>=10.12.0'} dev: false - /autoprefixer/10.4.13_postcss@8.4.21: - resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} + /autoprefixer/10.4.14_postcss@8.4.21: + resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001450 + caniuse-lite: 1.0.30001466 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -4273,19 +4678,19 @@ packages: - debug dev: true - /babel-loader/8.3.0_la66t7xldg4uecmyawueag5wkm: + /babel-loader/8.3.0_h5x7dh6zbbyopr7jvxivhylqpa: resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} engines: {node: '>= 8.9'} peerDependencies: '@babel/core': ^7.0.0 webpack: '>=2' dependencies: - '@babel/core': 7.20.12 + '@babel/core': 7.21.3 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /babel-plugin-apply-mdx-type-prop/1.6.22_@babel+core@7.12.9: @@ -4310,38 +4715,38 @@ packages: '@babel/helper-plugin-utils': 7.10.4 dev: true - /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.20.12: + /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.21.3: resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.20.14 - '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.3 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.21.3 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.20.12: + /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.21.3: resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 - core-js-compat: 3.27.2 + '@babel/core': 7.21.3 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.21.3 + core-js-compat: 3.29.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.20.12: + /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.21.3: resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + '@babel/core': 7.21.3 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.21.3 transitivePeerDependencies: - supports-color dev: true @@ -4417,7 +4822,7 @@ packages: it-drain: 2.0.0 it-filter: 2.0.0 it-take: 2.0.0 - multiformats: 11.0.1 + multiformats: 11.0.2 dev: true /blueimp-md5/2.19.0: @@ -4511,9 +4916,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001450 - electron-to-chromium: 1.4.284 - node-releases: 2.0.9 + caniuse-lite: 1.0.30001466 + electron-to-chromium: 1.4.330 + node-releases: 2.0.10 update-browserslist-db: 1.0.10_browserslist@4.21.5 dev: true @@ -4553,7 +4958,7 @@ packages: dev: true /bytes/3.0.0: - resolution: {integrity: sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=} + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} dev: true @@ -4639,13 +5044,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001450 + caniuse-lite: 1.0.30001466 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true - /caniuse-lite/1.0.30001450: - resolution: {integrity: sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==} + /caniuse-lite/1.0.30001466: + resolution: {integrity: sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==} dev: true /cbor/8.1.0: @@ -4735,6 +5140,11 @@ packages: engines: {node: '>=8'} dev: true + /ci-info/3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + dev: true + /ci-parallel-vars/1.0.1: resolution: {integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==} dev: true @@ -4920,6 +5330,11 @@ packages: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} dev: true + /commander/10.0.0: + resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==} + engines: {node: '>=14'} + dev: true + /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true @@ -5038,7 +5453,7 @@ packages: dev: true /content-disposition/0.5.2: - resolution: {integrity: sha1-DPaLud318r55YcOoUXjLhdunjLQ=} + resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} engines: {node: '>= 0.6'} dev: true @@ -5076,7 +5491,7 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} - /copy-webpack-plugin/11.0.0_webpack@5.75.0: + /copy-webpack-plugin/11.0.0_webpack@5.76.2: resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -5088,22 +5503,22 @@ packages: normalize-path: 3.0.0 schema-utils: 4.0.0 serialize-javascript: 6.0.1 - webpack: 5.75.0 + webpack: 5.76.2 dev: true - /core-js-compat/3.27.2: - resolution: {integrity: sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==} + /core-js-compat/3.29.1: + resolution: {integrity: sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==} dependencies: browserslist: 4.21.5 dev: true - /core-js-pure/3.27.2: - resolution: {integrity: sha512-Cf2jqAbXgWH3VVzjyaaFkY1EBazxugUepGymDoeteyYr9ByX51kD2jdHZlsEF/xnJMyN3Prua7mQuzwMg6Zc9A==} + /core-js-pure/3.29.1: + resolution: {integrity: sha512-4En6zYVi0i0XlXHVz/bi6l1XDjCqkKRq765NXuX+SnaIatlE96Odt5lMLjdxUiNI1v9OXI5DSLWYPlmTfkTktg==} requiresBuild: true dev: true - /core-js/3.27.2: - resolution: {integrity: sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w==} + /core-js/3.29.1: + resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==} requiresBuild: true dev: true @@ -5201,7 +5616,7 @@ packages: postcss: 8.4.21 dev: true - /css-loader/6.7.3_webpack@5.75.0: + /css-loader/6.7.3_webpack@5.76.2: resolution: {integrity: sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -5215,10 +5630,10 @@ packages: postcss-modules-values: 4.0.0_postcss@8.4.21 postcss-value-parser: 4.2.0 semver: 7.3.8 - webpack: 5.75.0 + webpack: 5.76.2 dev: true - /css-minimizer-webpack-plugin/4.2.2_dpcjkp5o5ztxuvt4quwwvenemi: + /css-minimizer-webpack-plugin/4.2.2_7uczfu7hmoatiedqewdadkdxue: resolution: {integrity: sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -5244,13 +5659,13 @@ packages: optional: true dependencies: clean-css: 5.3.2 - cssnano: 5.1.14_postcss@8.4.21 - jest-worker: 29.4.1 + cssnano: 5.1.15_postcss@8.4.21 + jest-worker: 29.5.0 postcss: 8.4.21 schema-utils: 4.0.0 serialize-javascript: 6.0.1 source-map: 0.6.1 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /css-select/4.3.0: @@ -5282,14 +5697,14 @@ packages: hasBin: true dev: true - /cssnano-preset-advanced/5.3.9_postcss@8.4.21: - resolution: {integrity: sha512-njnh4pp1xCsibJcEHnWZb4EEzni0ePMqPuPNyuWT4Z+YeXmsgqNuTPIljXFEXhxGsWs9183JkXgHxc1TcsahIg==} + /cssnano-preset-advanced/5.3.10_postcss@8.4.21: + resolution: {integrity: sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - autoprefixer: 10.4.13_postcss@8.4.21 - cssnano-preset-default: 5.2.13_postcss@8.4.21 + autoprefixer: 10.4.14_postcss@8.4.21 + cssnano-preset-default: 5.2.14_postcss@8.4.21 postcss: 8.4.21 postcss-discard-unused: 5.1.0_postcss@8.4.21 postcss-merge-idents: 5.1.1_postcss@8.4.21 @@ -5297,8 +5712,8 @@ packages: postcss-zindex: 5.1.0_postcss@8.4.21 dev: true - /cssnano-preset-default/5.2.13_postcss@8.4.21: - resolution: {integrity: sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==} + /cssnano-preset-default/5.2.14_postcss@8.4.21: + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 @@ -5307,14 +5722,14 @@ packages: cssnano-utils: 3.1.0_postcss@8.4.21 postcss: 8.4.21 postcss-calc: 8.2.4_postcss@8.4.21 - postcss-colormin: 5.3.0_postcss@8.4.21 + postcss-colormin: 5.3.1_postcss@8.4.21 postcss-convert-values: 5.1.3_postcss@8.4.21 postcss-discard-comments: 5.1.2_postcss@8.4.21 postcss-discard-duplicates: 5.1.0_postcss@8.4.21 postcss-discard-empty: 5.1.1_postcss@8.4.21 postcss-discard-overridden: 5.1.0_postcss@8.4.21 postcss-merge-longhand: 5.1.7_postcss@8.4.21 - postcss-merge-rules: 5.1.3_postcss@8.4.21 + postcss-merge-rules: 5.1.4_postcss@8.4.21 postcss-minify-font-values: 5.1.0_postcss@8.4.21 postcss-minify-gradients: 5.1.1_postcss@8.4.21 postcss-minify-params: 5.1.4_postcss@8.4.21 @@ -5329,7 +5744,7 @@ packages: postcss-normalize-url: 5.1.0_postcss@8.4.21 postcss-normalize-whitespace: 5.1.1_postcss@8.4.21 postcss-ordered-values: 5.1.3_postcss@8.4.21 - postcss-reduce-initial: 5.1.1_postcss@8.4.21 + postcss-reduce-initial: 5.1.2_postcss@8.4.21 postcss-reduce-transforms: 5.1.0_postcss@8.4.21 postcss-svgo: 5.1.0_postcss@8.4.21 postcss-unique-selectors: 5.1.1_postcss@8.4.21 @@ -5344,14 +5759,14 @@ packages: postcss: 8.4.21 dev: true - /cssnano/5.1.14_postcss@8.4.21: - resolution: {integrity: sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==} + /cssnano/5.1.15_postcss@8.4.21: + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-preset-default: 5.2.13_postcss@8.4.21 - lilconfig: 2.0.6 + cssnano-preset-default: 5.2.14_postcss@8.4.21 + lilconfig: 2.1.0 postcss: 8.4.21 yaml: 1.10.2 dev: true @@ -5528,6 +5943,14 @@ packages: object-keys: 1.1.1 dev: true + /define-properties/1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + /defined/1.0.1: resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} dev: true @@ -5575,8 +5998,8 @@ packages: hasBin: true dependencies: '@babel/parser': 7.16.4 - '@babel/traverse': 7.20.13 - '@vue/compiler-sfc': 3.2.45 + '@babel/traverse': 7.21.3 + '@vue/compiler-sfc': 3.2.47 camelcase: 6.3.0 cosmiconfig: 7.1.0 debug: 4.3.4 @@ -5593,7 +6016,7 @@ packages: readdirp: 3.6.0 require-package-name: 2.0.1 resolve: 1.22.1 - sass: 1.58.0 + sass: 1.59.3 scss-parser: 1.0.6 semver: 7.3.8 yargs: 16.2.0 @@ -5697,14 +6120,14 @@ packages: esutils: 2.0.3 dev: true - /docusaurus-plugin-typedoc/0.18.0_res3k6jdwbtxmimicswazugz6i: + /docusaurus-plugin-typedoc/0.18.0_fukoy6jgklpnhaz4jx7cggnvsu: resolution: {integrity: sha512-kurIUu8LhVIOPT88HoeBcu0/D2GMDdg0pUYaFlqeuXT9an6Wlgvuy0C22ZMYcJUcp/gA/Mw2XdUHubsLK2M4uA==} peerDependencies: typedoc: '>=0.23.0' typedoc-plugin-markdown: '>=3.13.0' dependencies: - typedoc: 0.23.24_typescript@4.9.5 - typedoc-plugin-markdown: 3.14.0_typedoc@0.23.24 + typedoc: 0.23.26_typescript@4.9.5 + typedoc-plugin-markdown: 3.14.0_typedoc@0.23.26 dev: true /dom-converter/0.2.0: @@ -5793,8 +6216,8 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true - /electron-to-chromium/1.4.284: - resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} + /electron-to-chromium/1.4.330: + resolution: {integrity: sha512-PqyefhybrVdjAJ45HaPLtuVaehiSw7C3ya0aad+rvmV53IVyXmYRk3pwIOb2TxTDTnmgQdn46NjMMaysx79/6Q==} dev: true /emittery/1.0.1: @@ -6192,6 +6615,36 @@ packages: '@esbuild/win32-x64': 0.16.10 dev: true + /esbuild/0.16.3: + resolution: {integrity: sha512-71f7EjPWTiSguen8X/kxEpkAS7BFHwtQKisCDDV3Y4GLGWBaoSCyD5uXkaUew6JDzA9FEN1W23mdnSwW9kqCeg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.16.3 + '@esbuild/android-arm64': 0.16.3 + '@esbuild/android-x64': 0.16.3 + '@esbuild/darwin-arm64': 0.16.3 + '@esbuild/darwin-x64': 0.16.3 + '@esbuild/freebsd-arm64': 0.16.3 + '@esbuild/freebsd-x64': 0.16.3 + '@esbuild/linux-arm': 0.16.3 + '@esbuild/linux-arm64': 0.16.3 + '@esbuild/linux-ia32': 0.16.3 + '@esbuild/linux-loong64': 0.16.3 + '@esbuild/linux-mips64el': 0.16.3 + '@esbuild/linux-ppc64': 0.16.3 + '@esbuild/linux-riscv64': 0.16.3 + '@esbuild/linux-s390x': 0.16.3 + '@esbuild/linux-x64': 0.16.3 + '@esbuild/netbsd-x64': 0.16.3 + '@esbuild/openbsd-x64': 0.16.3 + '@esbuild/sunos-x64': 0.16.3 + '@esbuild/win32-arm64': 0.16.3 + '@esbuild/win32-ia32': 0.16.3 + '@esbuild/win32-x64': 0.16.3 + dev: true + /esbuild/0.17.5: resolution: {integrity: sha512-Bu6WLCc9NMsNoMJUjGl3yBzTjVLXdysMltxQWiLAypP+/vQrf+3L1Xe8fCXzxaECus2cEJ9M7pk4yKatEwQMqQ==} engines: {node: '>=12'} @@ -6668,8 +7121,8 @@ packages: engines: {node: '>=0.10.0'} dev: true - /eta/1.14.2: - resolution: {integrity: sha512-wZmJAV7EFUG5W8XNXSazIdichnWEhGB1OWg4tnXWPj0CPNUcFdgorGNO6N9p6WBUgoUe4P0OziJYn1+6zxP2aQ==} + /eta/2.0.1: + resolution: {integrity: sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg==} engines: {node: '>=6.0.0'} dev: true @@ -6682,7 +7135,7 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 require-like: 0.1.2 dev: true @@ -6735,6 +7188,21 @@ packages: strip-final-newline: 3.0.0 dev: true + /execa/7.1.1: + resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + /expand-template/2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -6858,7 +7326,7 @@ packages: flat-cache: 3.0.4 dev: true - /file-loader/6.2.0_webpack@5.75.0: + /file-loader/6.2.0_webpack@5.76.2: resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -6866,7 +7334,7 @@ packages: dependencies: loader-utils: 2.0.4 schema-utils: 3.1.1 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /file-uri-to-path/1.0.0: @@ -6977,8 +7445,8 @@ packages: signal-exit: 3.0.7 dev: true - /fork-ts-checker-webpack-plugin/6.5.2_hhrrucqyg4eysmfpujvov2ym5u: - resolution: {integrity: sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==} + /fork-ts-checker-webpack-plugin/6.5.3_a37q6j7dwawz22saey2vgkpwqm: + resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: eslint: '>= 6' @@ -7005,7 +7473,7 @@ packages: semver: 7.3.8 tapable: 1.1.3 typescript: 4.9.5 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /forwarded/0.2.0: @@ -7332,7 +7800,7 @@ packages: engines: {node: '>=0.4.7'} hasBin: true dependencies: - minimist: 1.2.7 + minimist: 1.2.8 neo-async: 2.6.2 source-map: 0.6.1 wordwrap: 1.0.0 @@ -7526,7 +7994,7 @@ packages: /history/4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} dependencies: - '@babel/runtime': 7.20.13 + '@babel/runtime': 7.21.0 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.1 @@ -7549,7 +8017,7 @@ packages: dependencies: inherits: 2.0.4 obuf: 1.1.2 - readable-stream: 2.3.7 + readable-stream: 2.3.8 wbuf: 1.7.3 dev: true @@ -7572,7 +8040,7 @@ packages: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.16.2 + terser: 5.16.6 dev: true /html-rewriter-wasm/0.4.1: @@ -7588,7 +8056,7 @@ packages: resolution: {integrity: sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==} dev: true - /html-webpack-plugin/5.5.0_webpack@5.75.0: + /html-webpack-plugin/5.5.0_webpack@5.76.2: resolution: {integrity: sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==} engines: {node: '>=10.13.0'} peerDependencies: @@ -7599,7 +8067,7 @@ packages: lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /htmlparser2/6.1.0: @@ -7644,7 +8112,7 @@ packages: resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} dev: true - /http-proxy-middleware/2.0.6_@types+express@4.17.16: + /http-proxy-middleware/2.0.6_@types+express@4.17.17: resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} engines: {node: '>=12.0.0'} peerDependencies: @@ -7653,8 +8121,8 @@ packages: '@types/express': optional: true dependencies: - '@types/express': 4.17.16 - '@types/http-proxy': 1.17.9 + '@types/express': 4.17.17 + '@types/http-proxy': 1.17.10 http-proxy: 1.18.1 is-glob: 4.0.3 is-plain-obj: 3.0.0 @@ -7694,6 +8162,11 @@ packages: engines: {node: '>=12.20.0'} dev: true + /human-signals/4.3.0: + resolution: {integrity: sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==} + engines: {node: '>=14.18.0'} + dev: true + /hundreds/0.0.9: resolution: {integrity: sha512-4pHIJQl4SiVeMeg00w6WypEjVrSgVUxQS8YlOWW3KehCdlz/PoEhKlJY2DYeR56lPoF5KA+YjHKgnwhCsKJ7IQ==} hasBin: true @@ -7740,8 +8213,8 @@ packages: resolution: {integrity: sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==} dev: true - /immutable/4.2.2: - resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==} + /immutable/4.3.0: + resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==} dev: false /import-fresh/3.3.0: @@ -7877,7 +8350,7 @@ packages: it-parallel: 3.0.0 it-pipe: 2.0.5 it-pushable: 3.1.2 - multiformats: 11.0.1 + multiformats: 11.0.2 p-queue: 7.3.0 uint8arrays: 4.0.3 dev: true @@ -8356,14 +8829,14 @@ packages: engines: {node: '>=16.0.0', npm: '>=7.0.0'} dev: true - /jest-util/29.4.1: - resolution: {integrity: sha512-bQy9FPGxVutgpN4VRc0hk6w7Hx/m6L53QxpDreTZgJd9gfx/AV2MjyPde9tGyZRINAUrSv57p2inGBu2dRLmkQ==} + /jest-util/29.5.0: + resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.1 - '@types/node': 18.11.18 + '@jest/types': 29.5.0 + '@types/node': 18.15.3 chalk: 4.1.2 - ci-info: 3.7.1 + ci-info: 3.8.0 graceful-fs: 4.2.10 picomatch: 2.3.1 dev: true @@ -8372,23 +8845,23 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.15.3 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest-worker/29.4.1: - resolution: {integrity: sha512-O9doU/S1EBe+yp/mstQ0VpPwpv0Clgn68TkNwGxL6/usX/KUW9Arnn4ag8C3jc6qHcXznhsT5Na1liYzAsuAbQ==} + /jest-worker/29.5.0: + resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.11.18 - jest-util: 29.4.1 + '@types/node': 18.15.3 + jest-util: 29.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /joi/17.7.0: - resolution: {integrity: sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==} + /joi/17.8.4: + resolution: {integrity: sha512-jjdRHb5WtL+KgSHvOULQEPPv4kcl+ixd1ybOFQq3rWLgEEqc03QMmilodL0GVJE14U/SQDXkUhQUSZANGDH/AA==} dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 @@ -8445,7 +8918,7 @@ packages: dev: true /json-buffer/3.0.0: - resolution: {integrity: sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=} + resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} dev: true /json-parse-better-errors/1.0.2: @@ -8552,6 +9025,13 @@ packages: package-json: 6.5.0 dev: true + /launch-editor/2.6.0: + resolution: {integrity: sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==} + dependencies: + picocolors: 1.0.0 + shell-quote: 1.8.0 + dev: true + /leven/3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -8570,6 +9050,11 @@ packages: engines: {node: '>=10'} dev: true + /lilconfig/2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -8596,6 +9081,29 @@ packages: - supports-color dev: true + /lint-staged/13.2.0: + resolution: {integrity: sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + dependencies: + chalk: 5.2.0 + cli-truncate: 3.1.0 + commander: 10.0.0 + debug: 4.3.4 + execa: 7.1.1 + lilconfig: 2.1.0 + listr2: 5.0.8 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-inspect: 1.12.3 + pidtree: 0.6.0 + string-argv: 0.3.1 + yaml: 2.2.1 + transitivePeerDependencies: + - enquirer + - supports-color + dev: true + /listr2/5.0.7: resolution: {integrity: sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==} engines: {node: ^14.13.1 || >=16.0.0} @@ -8615,6 +9123,25 @@ packages: wrap-ansi: 7.0.0 dev: true + /listr2/5.0.8: + resolution: {integrity: sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.19 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.8.0 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + /load-json-file/4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -8973,14 +9500,14 @@ packages: engines: {node: '>=4'} dev: true - /mini-css-extract-plugin/2.7.2_webpack@5.75.0: - resolution: {integrity: sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==} + /mini-css-extract-plugin/2.7.3_webpack@5.76.2: + resolution: {integrity: sha512-CD9cXeKeXLcnMw8FZdtfrRrLaM7gwCl4nKuKn2YkY2Bw5wdlB8zU2cCzw+w2zS9RFvbrufTBkMCJACNPwqQA0w==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: schema-utils: 4.0.0 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /miniflare/2.11.0: @@ -9025,6 +9552,48 @@ packages: - utf-8-validate dev: true + /miniflare/2.12.1: + resolution: {integrity: sha512-pym6gzg8AQZ1NRChRV1hC4K55N49wndoaDEVRMvZPJrFsmGkNnXkWmlvmZ7SB3BN5UkP5MZwKhLqiJ49Ry8tFA==} + engines: {node: '>=16.13'} + hasBin: true + peerDependencies: + '@miniflare/storage-redis': 2.12.1 + cron-schedule: ^3.0.4 + ioredis: ^4.27.9 + peerDependenciesMeta: + '@miniflare/storage-redis': + optional: true + cron-schedule: + optional: true + ioredis: + optional: true + dependencies: + '@miniflare/cache': 2.12.1 + '@miniflare/cli-parser': 2.12.1 + '@miniflare/core': 2.12.1 + '@miniflare/d1': 2.12.1 + '@miniflare/durable-objects': 2.12.1 + '@miniflare/html-rewriter': 2.12.1 + '@miniflare/http-server': 2.12.1 + '@miniflare/kv': 2.12.1 + '@miniflare/queues': 2.12.1 + '@miniflare/r2': 2.12.1 + '@miniflare/runner-vm': 2.12.1 + '@miniflare/scheduler': 2.12.1 + '@miniflare/shared': 2.12.1 + '@miniflare/sites': 2.12.1 + '@miniflare/storage-file': 2.12.1 + '@miniflare/storage-memory': 2.12.1 + '@miniflare/web-sockets': 2.12.1 + kleur: 4.1.5 + semiver: 1.1.0 + source-map-support: 0.5.21 + undici: 5.20.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /minimalistic-assert/1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} dev: true @@ -9041,8 +9610,8 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch/5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + /minimatch/7.4.2: + resolution: {integrity: sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==} engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 @@ -9051,6 +9620,10 @@ packages: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} dev: true + /minimist/1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + /mkdirp-classic/0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: true @@ -9111,10 +9684,6 @@ packages: thunky: 1.1.0 dev: true - /multiformats/11.0.1: - resolution: {integrity: sha512-atWruyH34YiknSdL5yeIir00EDlJRpHzELYQxG7Iy29eCyL+VrZHpPrX5yqlik3jnuqpLpRKVZ0SGVb9UzKaSA==} - engines: {node: '>=16.0.0', npm: '>=7.0.0'} - /multiformats/11.0.2: resolution: {integrity: sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} @@ -9225,8 +9794,8 @@ packages: engines: {node: '>= 6.13.0'} dev: true - /node-releases/2.0.9: - resolution: {integrity: sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA==} + /node-releases/2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} dev: true /nofilter/3.1.0: @@ -9344,7 +9913,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 has-symbols: 1.0.3 object-keys: 1.1.1 dev: true @@ -9421,8 +9990,8 @@ packages: mimic-fn: 4.0.0 dev: true - /open/8.4.0: - resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} + /open/8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} dependencies: define-lazy-prop: 2.0.0 @@ -9889,8 +10458,8 @@ packages: postcss-value-parser: 4.2.0 dev: true - /postcss-colormin/5.3.0_postcss@8.4.21: - resolution: {integrity: sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==} + /postcss-colormin/5.3.1_postcss@8.4.21: + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 @@ -9959,7 +10528,7 @@ packages: postcss-selector-parser: 6.0.11 dev: true - /postcss-loader/7.0.2_6jdsrmfenkuhhw3gx4zvjlznce: + /postcss-loader/7.0.2_6puvukfnwbwq425eep7g4z27be: resolution: {integrity: sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -9970,7 +10539,7 @@ packages: klona: 2.0.6 postcss: 8.4.21 semver: 7.3.8 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /postcss-merge-idents/5.1.1_postcss@8.4.21: @@ -9995,8 +10564,8 @@ packages: stylehacks: 5.1.1_postcss@8.4.21 dev: true - /postcss-merge-rules/5.1.3_postcss@8.4.21: - resolution: {integrity: sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==} + /postcss-merge-rules/5.1.4_postcss@8.4.21: + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 @@ -10205,8 +10774,8 @@ packages: postcss-value-parser: 4.2.0 dev: true - /postcss-reduce-initial/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==} + /postcss-reduce-initial/5.1.2_postcss@8.4.21: + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 @@ -10522,11 +11091,11 @@ packages: dependencies: deep-extend: 0.6.0 ini: 1.3.8 - minimist: 1.2.7 + minimist: 1.2.8 strip-json-comments: 2.0.1 dev: true - /react-dev-utils/12.0.1_hhrrucqyg4eysmfpujvov2ym5u: + /react-dev-utils/12.0.1_a37q6j7dwawz22saey2vgkpwqm: resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} engines: {node: '>=14'} peerDependencies: @@ -10545,14 +11114,14 @@ packages: escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.2_hhrrucqyg4eysmfpujvov2ym5u + fork-ts-checker-webpack-plugin: 6.5.3_a37q6j7dwawz22saey2vgkpwqm global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 immer: 9.0.19 is-root: 2.1.0 loader-utils: 3.2.1 - open: 8.4.0 + open: 8.4.2 pkg-up: 3.1.0 prompts: 2.4.2 react-error-overlay: 6.0.11 @@ -10561,7 +11130,7 @@ packages: strip-ansi: 6.0.1 text-table: 0.2.0 typescript: 4.9.5 - webpack: 5.75.0 + webpack: 5.76.2 transitivePeerDependencies: - eslint - supports-color @@ -10587,7 +11156,7 @@ packages: react-dom: optional: true dependencies: - '@babel/runtime': 7.20.13 + '@babel/runtime': 7.21.0 invariant: 2.2.4 prop-types: 15.8.1 react-fast-compare: 3.2.0 @@ -10598,7 +11167,7 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true - /react-loadable-ssr-addon-v5-slorber/1.0.1_pwfl7zyferpbeh35vaepqxwaky: + /react-loadable-ssr-addon-v5-slorber/1.0.1_vycmgd62fyq7p6lzattvftnbcy: resolution: {integrity: sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==} engines: {node: '>=10.13.0'} peerDependencies: @@ -10608,9 +11177,9 @@ packages: react-loadable: optional: true dependencies: - '@babel/runtime': 7.20.13 + '@babel/runtime': 7.21.0 react-loadable: /@docusaurus/react-loadable/5.5.2 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /react-router-config/5.1.1_react-router@5.3.4: @@ -10624,7 +11193,7 @@ packages: react-router: optional: true dependencies: - '@babel/runtime': 7.20.13 + '@babel/runtime': 7.21.0 react-router: 5.3.4 dev: true @@ -10636,7 +11205,7 @@ packages: react: optional: true dependencies: - '@babel/runtime': 7.20.13 + '@babel/runtime': 7.21.0 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -10653,7 +11222,7 @@ packages: react: optional: true dependencies: - '@babel/runtime': 7.20.13 + '@babel/runtime': 7.21.0 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -10692,8 +11261,8 @@ packages: type-fest: 0.6.0 dev: true - /readable-stream/2.3.7: - resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + /readable-stream/2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -10712,6 +11281,15 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readable-stream/3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + /readable-stream/4.3.0: resolution: {integrity: sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -10760,7 +11338,7 @@ packages: /regenerator-transform/0.15.1: resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==} dependencies: - '@babel/runtime': 7.20.13 + '@babel/runtime': 7.21.0 dev: true /regexp-tree/0.1.24: @@ -10782,13 +11360,13 @@ packages: engines: {node: '>=8'} dev: true - /regexpu-core/5.2.2: - resolution: {integrity: sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==} + /regexpu-core/5.3.2: + resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} dependencies: + '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 regenerate-unicode-properties: 10.1.0 - regjsgen: 0.7.1 regjsparser: 0.9.1 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.1.0 @@ -10808,10 +11386,6 @@ packages: rc: 1.2.8 dev: true - /regjsgen/0.7.1: - resolution: {integrity: sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==} - dev: true - /regjsparser/0.9.1: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true @@ -11078,13 +11652,13 @@ packages: /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sass/1.58.0: - resolution: {integrity: sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==} + /sass/1.59.3: + resolution: {integrity: sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==} engines: {node: '>=12.0.0'} hasBin: true dependencies: chokidar: 3.5.3 - immutable: 4.2.2 + immutable: 4.3.0 source-map-js: 1.0.2 dev: false @@ -11329,9 +11903,10 @@ packages: rechoir: 0.6.2 dev: true - /shiki/0.12.1: - resolution: {integrity: sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==} + /shiki/0.14.1: + resolution: {integrity: sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==} dependencies: + ansi-sequence-parser: 1.1.0 jsonc-parser: 3.2.0 vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 @@ -11507,7 +12082,7 @@ packages: detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 - readable-stream: 3.6.0 + readable-stream: 3.6.2 wbuf: 1.7.3 transitivePeerDependencies: - supports-color @@ -11584,8 +12159,8 @@ packages: engines: {node: '>= 0.8'} dev: true - /std-env/3.3.1: - resolution: {integrity: sha512-3H20QlwQsSm2OvAxWIYhs+j01MzzqwMwGiiO1NQaJYZgJZFPuAbf95/DiKRBSTYIJ2FeGUc+B/6mPGcWP9dO3Q==} + /std-env/3.3.2: + resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==} dev: true /stop-iteration-iterator/1.0.0: @@ -11895,8 +12470,8 @@ packages: unique-string: 3.0.0 dev: true - /terser-webpack-plugin/5.3.6_webpack@5.75.0: - resolution: {integrity: sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==} + /terser-webpack-plugin/5.3.7_webpack@5.76.2: + resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -11915,12 +12490,12 @@ packages: jest-worker: 27.5.1 schema-utils: 3.1.1 serialize-javascript: 6.0.1 - terser: 5.16.2 - webpack: 5.75.0 + terser: 5.16.6 + webpack: 5.76.2 dev: true - /terser/5.16.2: - resolution: {integrity: sha512-JKuM+KvvWVqT7muHVyrwv7FVRPnmHDwF6XwoIxdbF5Witi0vu99RYpxDexpJndXt3jbZZmmWr2/mQa6HvSNdSg==} + /terser/5.16.6: + resolution: {integrity: sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==} engines: {node: '>=10'} hasBin: true dependencies: @@ -12022,6 +12597,7 @@ packages: /trim/0.0.1: resolution: {integrity: sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==} + deprecated: Use String.prototype.trim() instead dev: true /trough/1.0.5: @@ -12147,25 +12723,25 @@ packages: is-typedarray: 1.0.0 dev: true - /typedoc-plugin-markdown/3.14.0_typedoc@0.23.24: + /typedoc-plugin-markdown/3.14.0_typedoc@0.23.26: resolution: {integrity: sha512-UyQLkLRkfTFhLdhSf3RRpA3nNInGn+k6sll2vRXjflaMNwQAAiB61SYbisNZTg16t4K1dt1bPQMMGLrxS0GZ0Q==} peerDependencies: typedoc: '>=0.23.0' dependencies: handlebars: 4.7.7 - typedoc: 0.23.24_typescript@4.9.5 + typedoc: 0.23.26_typescript@4.9.5 dev: true - /typedoc-plugin-missing-exports/1.0.0_typedoc@0.23.24: + /typedoc-plugin-missing-exports/1.0.0_typedoc@0.23.26: resolution: {integrity: sha512-7s6znXnuAj1eD9KYPyzVzR1lBF5nwAY8IKccP5sdoO9crG4lpd16RoFpLsh2PccJM+I2NASpr0+/NMka6ThwVA==} peerDependencies: typedoc: 0.22.x || 0.23.x dependencies: - typedoc: 0.23.24_typescript@4.9.5 + typedoc: 0.23.26_typescript@4.9.5 dev: false - /typedoc/0.23.24_typescript@4.9.5: - resolution: {integrity: sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==} + /typedoc/0.23.26_typescript@4.9.5: + resolution: {integrity: sha512-5m4KwR5tOLnk0OtMaRn9IdbeRM32uPemN9kur7YK9wFqx8U0CYrvO9aVq6ysdZSV1c824BTm+BuQl2Ze/k1HtA==} engines: {node: '>= 14.14'} hasBin: true peerDependencies: @@ -12173,8 +12749,8 @@ packages: dependencies: lunr: 2.3.9 marked: 4.2.12 - minimatch: 5.1.6 - shiki: 0.12.1 + minimatch: 7.4.2 + shiki: 0.14.1 typescript: 4.9.5 /typescript/4.9.5: @@ -12194,7 +12770,7 @@ packages: resolution: {integrity: sha512-b+aKlI2oTnxnfeSQWV1sMacqSNxqhtXySaH6bflvONGxF8V/fT3ZlYH7z2qgGfydsvpVo4JUgM/Ylyfl2YouCg==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - multiformats: 11.0.1 + multiformats: 11.0.2 /unbox-primitive/1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -12205,6 +12781,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undici/5.20.0: + resolution: {integrity: sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==} + engines: {node: '>=12.18'} + dependencies: + busboy: 1.6.0 + dev: true + /undici/5.9.1: resolution: {integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==} engines: {node: '>=12.18'} @@ -12373,7 +12956,7 @@ packages: dependencies: punycode: 2.3.0 - /url-loader/4.1.1_p5dl6emkcwslbw72e37w4ug7em: + /url-loader/4.1.1_35ful32yo3wjb53le3l6xb5doy: resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -12383,11 +12966,11 @@ packages: file-loader: optional: true dependencies: - file-loader: 6.2.0_webpack@5.75.0 + file-loader: 6.2.0_webpack@5.76.2 loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.1.1 - webpack: 5.75.0 + webpack: 5.76.2 dev: true /url-parse-lax/3.0.0: @@ -12494,9 +13077,9 @@ packages: hasBin: true dependencies: axios: 0.25.0 - joi: 17.7.0 + joi: 17.8.4 lodash: 4.17.21 - minimist: 1.2.7 + minimist: 1.2.8 rxjs: 7.8.0 transitivePeerDependencies: - debug @@ -12549,11 +13132,12 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true - /webpack-bundle-analyzer/4.7.0: - resolution: {integrity: sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==} + /webpack-bundle-analyzer/4.8.0: + resolution: {integrity: sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==} engines: {node: '>= 10.13.0'} hasBin: true dependencies: + '@discoveryjs/json-ext': 0.5.7 acorn: 8.8.2 acorn-walk: 8.2.0 chalk: 4.1.2 @@ -12568,7 +13152,7 @@ packages: - utf-8-validate dev: true - /webpack-dev-middleware/5.3.3_webpack@5.75.0: + /webpack-dev-middleware/5.3.3_webpack@5.76.2: resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -12579,11 +13163,11 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.0.0 - webpack: 5.75.0 + webpack: 5.76.2 dev: true - /webpack-dev-server/4.11.1_webpack@5.75.0: - resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==} + /webpack-dev-server/4.12.0_webpack@5.76.2: + resolution: {integrity: sha512-XRN9YRnvOj3TQQ5w/0pR1y1xDcVnbWtNkTri46kuEbaWUPTHsWUvOyAAI7PZHLY+hsFki2kRltJjKMw7e+IiqA==} engines: {node: '>= 12.13.0'} hasBin: true peerDependencies: @@ -12595,9 +13179,9 @@ packages: dependencies: '@types/bonjour': 3.5.10 '@types/connect-history-api-fallback': 1.3.5 - '@types/express': 4.17.16 + '@types/express': 4.17.17 '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.0 + '@types/serve-static': 1.15.1 '@types/sockjs': 0.3.33 '@types/ws': 8.5.4 ansi-html-community: 0.0.8 @@ -12610,9 +13194,10 @@ packages: express: 4.18.2 graceful-fs: 4.2.10 html-entities: 2.3.3 - http-proxy-middleware: 2.0.6_@types+express@4.17.16 + http-proxy-middleware: 2.0.6_@types+express@4.17.17 ipaddr.js: 2.0.1 - open: 8.4.0 + launch-editor: 2.6.0 + open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 schema-utils: 4.0.0 @@ -12620,9 +13205,9 @@ packages: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.75.0 - webpack-dev-middleware: 5.3.3_webpack@5.75.0 - ws: 8.12.0 + webpack: 5.76.2 + webpack-dev-middleware: 5.3.3_webpack@5.76.2 + ws: 8.13.0 transitivePeerDependencies: - bufferutil - debug @@ -12643,8 +13228,8 @@ packages: engines: {node: '>=10.13.0'} dev: true - /webpack/5.75.0: - resolution: {integrity: sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==} + /webpack/5.76.2: + resolution: {integrity: sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -12674,7 +13259,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.1.1 tapable: 2.2.1 - terser-webpack-plugin: 5.3.6_webpack@5.75.0 + terser-webpack-plugin: 5.3.7_webpack@5.76.2 watchpack: 2.4.0 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -12683,7 +13268,7 @@ packages: - uglify-js dev: true - /webpackbar/5.0.2_webpack@5.75.0: + /webpackbar/5.0.2_webpack@5.76.2: resolution: {integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==} engines: {node: '>=12'} peerDependencies: @@ -12692,8 +13277,8 @@ packages: chalk: 4.1.2 consola: 2.15.3 pretty-time: 1.1.0 - std-env: 3.3.1 - webpack: 5.75.0 + std-env: 3.3.2 + webpack: 5.76.2 dev: true /websocket-driver/0.7.4: @@ -12808,6 +13393,36 @@ packages: resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} dev: true + /wrangler/2.12.3: + resolution: {integrity: sha512-0skqT9qBbchiVY57YwQKFCFnuUo2Lg7e0vCChJ9FWcWZ/CXsPXeBMeVA8N/p72LuhrtZlqTFN7TsNOd8jm8KcQ==} + engines: {node: '>=16.13.0'} + hasBin: true + dependencies: + '@cloudflare/kv-asset-handler': 0.2.0 + '@esbuild-plugins/node-globals-polyfill': 0.1.1_esbuild@0.16.3 + '@esbuild-plugins/node-modules-polyfill': 0.1.4_esbuild@0.16.3 + '@miniflare/core': 2.12.1 + '@miniflare/d1': 2.12.1 + '@miniflare/durable-objects': 2.12.1 + blake3-wasm: 2.1.5 + chokidar: 3.5.3 + esbuild: 0.16.3 + miniflare: 2.12.1 + nanoid: 3.3.4 + path-to-regexp: 6.2.1 + selfsigned: 2.1.1 + source-map: 0.7.4 + xxhash-wasm: 1.0.2 + optionalDependencies: + fsevents: 2.3.2 + transitivePeerDependencies: + - '@miniflare/storage-redis' + - bufferutil + - cron-schedule + - ioredis + - utf-8-validate + dev: true + /wrangler/2.9.0: resolution: {integrity: sha512-5nyyR4bXKG/Rwz0dH+nOx4SWvJWmTZVSbceLyTV+ZOH1sd2vvPnnW14NUzTNEjY3XaT93XH+28mc5+UNSYsFHw==} engines: {node: '>=16.13.0'} @@ -12908,6 +13523,19 @@ packages: utf-8-validate: optional: true + /ws/8.13.0: + resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /xdg-basedir/4.0.0: resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} engines: {node: '>=8'} From 1ea805e12b5f9ffcedca6e927a41ff58b7c85f28 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:10:29 -0700 Subject: [PATCH 23/54] createIssuerSaysAccountCanAdminSpace has capabilities as param, and defaults to can='*' --- packages/access-client/src/agent.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 370f5c943..e7112fea1 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -36,26 +36,24 @@ const PRINCIPAL = DID.parse('did:web:web3.storage') * @param {Ucanto.Signer>} issuer * @param {Ucanto.DID} space * @param {Ucanto.Principal>} account + * @param {Ucanto.Capabilities} capabilities * @returns */ -async function createIssuerSaysAccountCanAdminSpace(issuer, space, account) { +async function createIssuerSaysAccountCanAdminSpace( + issuer, + space, + account, + capabilities = [ + { + can: '*', + with: space, + }, + ] +) { return ucanto.delegate({ issuer, audience: account, - capabilities: [ - { - can: 'space/*', - with: space, - }, - { - can: 'store/*', - with: space, - }, - { - can: 'upload/*', - with: space, - }, - ], + capabilities, }) } From 50402a4bb478ffedc25bca4de8f4acb3317019a0 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:17:44 -0700 Subject: [PATCH 24/54] access-client agent can pass opts.capabilities --- packages/access-client/src/agent.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index e7112fea1..5fb99f800 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -488,14 +488,18 @@ export class Agent { * @param {`${string}@${string}`} email * @param {object} [opts] * @param {AbortSignal} [opts.signal] + * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] */ async authorize(email, opts) { + const capabilityRequest = opts?.capabilities + ? [...opts.capabilities] + : [{ can: 'store/*' }, { can: 'provider/add' }, { can: 'upload/*' }] const res = await this.invokeAndExecute(Access.authorize, { audience: this.connection.id, with: this.issuer.did(), nb: { iss: createDidMailtoFromEmail(email), - att: [{ can: 'store/*' }, { can: 'provider/add' }, { can: 'upload/*' }], + att: capabilityRequest, }, }) From 53c63b8003dd52c4f2c6d7c4a4e4e872083138f3 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Wed, 15 Mar 2023 15:54:12 -0700 Subject: [PATCH 25/54] feat: restore registerSpace and only use newRegisterSpace if a provider is passed --- packages/access-client/src/agent.js | 84 ++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 5fb99f800..61a4c3d41 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -10,6 +10,7 @@ import * as ucanto from '@ucanto/core' import { URI, DID as DIDValidator } from '@ucanto/validator' import { Peer } from './awake/peer.js' import * as Space from '@web3-storage/capabilities/space' +import * as Voucher from '@web3-storage/capabilities/voucher' import * as Access from '@web3-storage/capabilities/access' import * as Provider from '@web3-storage/capabilities/provider' @@ -627,7 +628,7 @@ export class Agent { * @param {AbortSignal} [opts.signal] * @param {Ucanto.DID<'web'>} [opts.provider] - provider to register - defaults to this.connection.id */ - async registerSpace(email, opts) { + async #newRegisterSpace(email, opts) { const space = this.currentSpace() const spaceMeta = space ? this.#data.spaces.get(space) : undefined const provider = @@ -669,6 +670,87 @@ export class Agent { this.#data.addSpace(space, spaceMeta) } + /** + * Invokes voucher/redeem for the free tier, wait on the websocket for the voucher/claim and invokes it + * + * It also adds a full space delegation to the service in the voucher/claim invocation to allow for recovery + * + * @param {string} email + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + * @param {Ucanto.DID<'web'>} [opts.provider] - provider to register - defaults to this.connection.id + */ + async registerSpace(email, opts = {}) { + // if the client passes `provider` use the new space registration flow + if (opts.provider) { + return this.#newRegisterSpace(email, opts) + } + + const space = this.currentSpace() + const service = this.connection.id + const spaceMeta = space ? this.#data.spaces.get(space) : undefined + + if (!space || !spaceMeta) { + throw new Error('No space selected') + } + + if (spaceMeta && spaceMeta.isRegistered) { + throw new Error('Space already registered with web3.storage.') + } + + const inv = await this.invokeAndExecute(Voucher.claim, { + nb: { + identity: URI.from(`mailto:${email}`), + product: 'product:free', + service: service.did(), + }, + }) + + if (inv && inv.error) { + throw new Error('Voucher claim failed', { cause: inv }) + } + + const voucherRedeem = + /** @type {Ucanto.Delegation<[import('./types').VoucherRedeem]>} */ ( + await this.#waitForDelegation(opts) + ) + await this.addProof(voucherRedeem) + const delegationToService = await this.delegate({ + abilities: ['*'], + audience: service, + expiration: Infinity, + audienceMeta: { + name: 'w3access', + type: 'service', + }, + }) + + const accInv = await this.invokeAndExecute(Voucher.redeem, { + with: URI.from(service.did()), + nb: { + space, + identity: voucherRedeem.capabilities[0].nb.identity, + product: voucherRedeem.capabilities[0].nb.product, + }, + proofs: [delegationToService], + facts: [ + { + space: spaceMeta, + agent: this.meta, + }, + ], + }) + + if (accInv && accInv.error) { + throw new Error('Space registration failed', { cause: accInv }) + } + + spaceMeta.isRegistered = true + + this.#data.addSpace(space, spaceMeta) + this.#data.removeDelegation(voucherRedeem.cid) + } + /** * * @param {object} [opts] From c0672ca192646a96e2811391fe9090d2b1141d71 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 17:20:39 -0700 Subject: [PATCH 26/54] test: test access/authorize, access/confirm, access/claim (#548) --- .../src/service/access-authorize.js | 3 +- .../test/access-client-agent.test.js | 177 +++++++++++++++--- packages/access-api/test/helpers/context.js | 13 +- .../test/helpers/ucanto-test-utils.js | 48 +++-- 4 files changed, 194 insertions(+), 47 deletions(-) diff --git a/packages/access-api/src/service/access-authorize.js b/packages/access-api/src/service/access-authorize.js index 8ce7807c0..686589d69 100644 --- a/packages/access-api/src/service/access-authorize.js +++ b/packages/access-api/src/service/access-authorize.js @@ -34,8 +34,9 @@ export function accessAuthorizeProvider(ctx) { nb: { // we copy request details and set the `aud` field to the agent DID // that requested the authorization. - ...capability.nb, + iss: capability.nb.iss, aud: capability.with, + att: capability.nb.att, }, }) .delegate() diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 0b0da17ab..11529ace8 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -7,7 +7,10 @@ import * as w3caps from '@web3-storage/capabilities' import * as assert from 'assert' import * as Ucanto from '@ucanto/interface' import { createEmail } from './helpers/utils.js' -import { stringToDelegations } from '@web3-storage/access/encoding' +import { + stringToDelegation, + stringToDelegations, +} from '@web3-storage/access/encoding' import * as delegationsResponse from '../src/utils/delegations-response.js' for (const accessApiVariant of /** @type {const} */ ([ @@ -18,48 +21,53 @@ for (const accessApiVariant of /** @type {const} */ ([ did: () => /** @type {const} */ ('did:mailto:dag.house:foo'), } const spaceWithStorageProvider = principal.ed25519.generate() - /** @type {{to:string, url:string}[]} */ - const emails = [] - const email = createEmail(emails) + async function createContext() { + /** @type {{url:string}[]} */ + const emails = [] + const email = createEmail(emails) + const ctx = await context({ + globals: { + email, + }, + }) + return { + ...ctx, + emails, + connection: ctx.conn, + } + } return { spaceWithStorageProvider, - emails, - ...createTesterFromContext( - () => - context({ - globals: { - email, - }, - }), - { - account, - registerSpaces: [spaceWithStorageProvider], - } - ), + ...createTesterFromContext(createContext, { + account, + registerSpaces: [spaceWithStorageProvider], + }), } })(), }, ])) { describe(`access-client-agent ${accessApiVariant.name}`, () => { it('can createSpace', async () => { + const { connection } = await accessApiVariant.create() const accessAgent = await AccessAgent.create(undefined, { - connection: await accessApiVariant.connection, + connection, }) const space = await accessAgent.createSpace('test-add') const delegations = accessAgent.proofs() assert.equal(space.proof.cid, delegations[0].cid) }) it.skip('can authorize', async () => { + const { connection } = await accessApiVariant.create() const accessAgent = await AccessAgent.create(undefined, { - connection: await accessApiVariant.connection, + connection, }) await accessAgent.authorize('example@dag.house') }) - it('can be used to do session authorization', async () => { - const { emails, connection, service } = accessApiVariant + it('can testSessionAuthorization', async () => { + const { connection, service, emails } = await accessApiVariant.create() const accessAgent = await AccessAgent.create(undefined, { - connection: await connection, + connection, }) /** @type {Ucanto.Principal>} */ const account = { did: () => 'did:mailto:dag.house:example' } @@ -70,6 +78,103 @@ for (const accessApiVariant of /** @type {const} */ ([ emails ) }) + + it('can requestAuthorization', async () => { + const { connection } = await accessApiVariant.create() + /** @type {Ucanto.Principal>} */ + const account = { did: () => 'did:mailto:dag.house:example' } + const accessAgent = await AccessAgent.create(undefined, { connection }) + await requestAuthorization(accessAgent, account, [{ can: '*' }]) + }) + + it('can requestAuthorization, then click confirm email, then claim', async () => { + const { emails, connection } = await accessApiVariant.create() + /** @type {Ucanto.Principal>} */ + const account = { did: () => 'did:mailto:dag.house:example' } + const accessAgent = await AccessAgent.create(undefined, { + connection, + }) + console.log('accessAgent.issuer', accessAgent.issuer.did()) + + // request that account authorizes accessAgent + // this should result in sending a confirmation email + const requestAllAbilities = requestAuthorization(accessAgent, account, [ + { can: '*' }, + ]) + + // in parallel: + // * request authorization + // * keep checking your email for the confirmation email that sends, then return it + // await both succeeding + const abort = new AbortController() + after(() => abort.abort()) + const [, confirmEmail] = await Promise.all([ + requestAllAbilities, + watchForEmail(emails, 100, abort.signal).then((email) => { + return email + }), + ]) + + // extract confirmation invocation from email that was sent by service while handling access/authorize + const confirm = await extractConfirmInvocation( + connection, + new URL(confirmEmail.url) + ) + // invoke the access/confirm invocation as if the user had clicked the email + const [confirmResult] = await connection.execute(confirm) + console.log({ confirmResult }) + assert.notEqual( + confirmResult.error, + true, + 'access/confirm result is not an error' + ) + + const claimed = await accessAgent.claimDelegations() + assert.deepEqual(claimed.length, 2) + }) + }) +} + +/** + * @param {Ucanto.Connection} connection + * @param {URL} confirmationUrl + * @returns {Promise>} + */ +async function extractConfirmInvocation(connection, confirmationUrl) { + const delegation = stringToDelegation( + confirmationUrl.searchParams.get('ucan') ?? '' + ) + if ( + delegation.capabilities.length !== 1 || + delegation.capabilities[0].can !== 'access/confirm' + ) { + throw new Error(`parsed unexpected delegation from confirmationUrl`) + } + const confirm = /** @type {Ucanto.Invocation} */ (delegation) + return confirm +} + +/** + * @param {Array<{ url: string }>} emails + * @param {number} [retryAfter] + * @param {AbortSignal} [abort] + * @returns {Promise<{ url: string }>} latest email, once received + */ +function watchForEmail(emails, retryAfter, abort) { + return new Promise((resolve, reject) => { + if (abort) { + abort.addEventListener('abort', () => reject(new Error('aborted'))) + } + const latestEmail = emails.at(-1) + if (latestEmail) { + return resolve(latestEmail) + } + if (typeof retryAfter === 'number') { + setTimeout( + () => watchForEmail(emails, retryAfter).then(resolve).catch(reject), + retryAfter + ) + } }) } @@ -79,11 +184,37 @@ for (const accessApiVariant of /** @type {const} */ ([ * @typedef {import('./helpers/ucanto-test-utils.js').AccessService} AccessService */ +/** + * request authorization using access-api access/authorize + * + * @param {AccessAgent} access + * @param {Ucanto.Principal>} authorizer - who you are requesting authorization from + * @param {Iterable<{ can: Ucanto.Ability }>} abilities - e.g. [{ can: '*' }] + */ +async function requestAuthorization(access, authorizer, abilities) { + const authorizeResult = await access.invokeAndExecute( + w3caps.Access.authorize, + { + audience: access.connection.id, + with: access.issuer.did(), + nb: { + iss: authorizer.did(), + att: [...abilities], + }, + } + ) + assert.notDeepStrictEqual( + authorizeResult.error, + true, + 'authorize result is not an error' + ) +} + /** * @param {principal.ed25519.Signer.Signer<`did:web:${string}`, principal.ed25519.Signer.UCAN.SigAlg>} service * @param {AccessAgent} access * @param {Ucanto.Principal>} account - * @param {{to:string, url:string}[]} emails + * @param {{url:string}[]} emails */ async function testSessionAuthorization(service, access, account, emails) { const authorizeResult = await access.invokeAndExecute( diff --git a/packages/access-api/test/helpers/context.js b/packages/access-api/test/helpers/context.js index d3de155b3..dae2c2e3c 100644 --- a/packages/access-api/test/helpers/context.js +++ b/packages/access-api/test/helpers/context.js @@ -66,14 +66,15 @@ export async function context({ env = {}, globals } = {}) { const db = /** @type {D1Database} */ (binds.__D1_BETA__) await migrate(db) + const conn = connection({ + principal: servicePrincipal, + // @ts-ignore + fetch: mf.dispatchFetch.bind(mf), + url: new URL('http://localhost:8787'), + }) return { mf, - conn: connection({ - principal: servicePrincipal, - // @ts-ignore - fetch: mf.dispatchFetch.bind(mf), - url: new URL('http://localhost:8787'), - }), + conn, service: servicePrincipal, issuer: await Signer.generate(), d1: db, diff --git a/packages/access-api/test/helpers/ucanto-test-utils.js b/packages/access-api/test/helpers/ucanto-test-utils.js index 7d53c3acc..3915c3983 100644 --- a/packages/access-api/test/helpers/ucanto-test-utils.js +++ b/packages/access-api/test/helpers/ucanto-test-utils.js @@ -11,28 +11,33 @@ import * as delegationsResponse from '../../src/utils/delegations-response.js' * Tests using context from "./helpers/context.js", which sets up a testable access-api inside miniflare. * * @template {Record} Service - * @param {() => Promise>} createContext + * @template {import('./types').HelperTestContext} Context + * @param {() => Promise} createContext * @param {object} [options] * @param {Ucanto.Principal>} options.account - account to register spaces with * @param {Iterable>>>} options.registerSpaces - spaces to register in access-api. Some access-api functionality on a space requires it to be registered. */ export function createTesterFromContext(createContext, options) { - const context = createContext().then(async (ctx) => { - const registeredSpaceAgent = await principal.ed25519.generate() - if (options) { - await registerSpaces(options?.registerSpaces ?? [], { + const create = () => + createContext().then(async (ctx) => { + const registeredSpaceAgent = await principal.ed25519.generate() + if (options) { + await registerSpaces(options?.registerSpaces ?? [], { + ...ctx, + account: options.account, + agent: registeredSpaceAgent, + }) + } + /** @type {Ucanto.ConnectionView} */ + const connection = ctx.conn + return { ...ctx, - account: options.account, - agent: registeredSpaceAgent, - }) - } - return { - ...ctx, - registeredSpaceAgent, - } - }) - /** @type {Promise>} */ - const connection = context.then((ctx) => ctx.conn) + connection, + registeredSpaceAgent, + } + }) + const context = create() + const connection = context.then(({ connection }) => connection) const issuer = context.then(({ issuer }) => issuer) const audience = context.then(({ service }) => service) const service = context.then(({ service }) => service) @@ -45,7 +50,16 @@ export function createTesterFromContext(createContext, options) { const [result] = await conn.execute(invocation) return result } - return { issuer, audience, invoke, miniflare, context, connection, service } + return { + issuer, + audience, + invoke, + miniflare, + context, + connection, + service, + create, + } } /** From 9aa3a26350f1fc26ac755febfa7283ed9643ab8d Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 17:23:14 -0700 Subject: [PATCH 27/54] rm console.logs in test --- packages/access-api/test/access-client-agent.test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 11529ace8..24dbe301a 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import { context } from './helpers/context.js' import { createTesterFromContext } from './helpers/ucanto-test-utils.js' import * as principal from '@ucanto/principal' @@ -94,7 +93,6 @@ for (const accessApiVariant of /** @type {const} */ ([ const accessAgent = await AccessAgent.create(undefined, { connection, }) - console.log('accessAgent.issuer', accessAgent.issuer.did()) // request that account authorizes accessAgent // this should result in sending a confirmation email @@ -122,7 +120,6 @@ for (const accessApiVariant of /** @type {const} */ ([ ) // invoke the access/confirm invocation as if the user had clicked the email const [confirmResult] = await connection.execute(confirm) - console.log({ confirmResult }) assert.notEqual( confirmResult.error, true, From 36628f3339b8524cb03c49bd0bb53cdeb61b133e Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 17:35:38 -0700 Subject: [PATCH 28/54] lint --- packages/access-api/test/access-client-agent.test.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 24dbe301a..5a5a26d5b 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -114,10 +114,7 @@ for (const accessApiVariant of /** @type {const} */ ([ ]) // extract confirmation invocation from email that was sent by service while handling access/authorize - const confirm = await extractConfirmInvocation( - connection, - new URL(confirmEmail.url) - ) + const confirm = await extractConfirmInvocation(new URL(confirmEmail.url)) // invoke the access/confirm invocation as if the user had clicked the email const [confirmResult] = await connection.execute(confirm) assert.notEqual( @@ -133,11 +130,10 @@ for (const accessApiVariant of /** @type {const} */ ([ } /** - * @param {Ucanto.Connection} connection * @param {URL} confirmationUrl * @returns {Promise>} */ -async function extractConfirmInvocation(connection, confirmationUrl) { +async function extractConfirmInvocation(confirmationUrl) { const delegation = stringToDelegation( confirmationUrl.searchParams.get('ucan') ?? '' ) From 08db79b39dee856bf97ddaec5e26e8593124e187 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Wed, 15 Mar 2023 18:43:06 -0700 Subject: [PATCH 29/54] feat: Agent#requestAuthorization (#550) Motivation: * move towards unbundling `authorize()` into e.g. `requestAuthorization()` + (future) `wait.....` --- .../test/access-client-agent.test.js | 13 ++++++-- packages/access-client/src/agent.js | 31 ++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 5a5a26d5b..7b3eb80a8 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -55,12 +55,19 @@ for (const accessApiVariant of /** @type {const} */ ([ const delegations = accessAgent.proofs() assert.equal(space.proof.cid, delegations[0].cid) }) - it.skip('can authorize', async () => { - const { connection } = await accessApiVariant.create() + it('can requestAuthorization', async () => { + const { connection, emails } = await accessApiVariant.create() const accessAgent = await AccessAgent.create(undefined, { connection, }) - await accessAgent.authorize('example@dag.house') + const emailCount = emails.length + const abort = new AbortController() + after(() => abort.abort()) + await accessAgent.requestAuthorization('example@dag.house', { + signal: abort.signal, + capabilities: [{ can: '*' }], + }) + assert.deepEqual(emails.length, emailCount + 1) }) it('can testSessionAuthorization', async () => { diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 42be4506e..c047a9597 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -487,26 +487,43 @@ export class Agent { * signed by the passed email address. * * @param {`${string}@${string}`} email - * @param {object} [opts] + * @param {object} opts * @param {AbortSignal} [opts.signal] - * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] + * @param {Iterable<{ can: Ucanto.Ability }>} opts.capabilities */ - async authorize(email, opts) { - const capabilityRequest = opts?.capabilities - ? [...opts.capabilities] - : [{ can: 'store/*' }, { can: 'provider/add' }, { can: 'upload/*' }] + async requestAuthorization(email, opts) { const res = await this.invokeAndExecute(Access.authorize, { audience: this.connection.id, with: this.issuer.did(), nb: { iss: createDidMailtoFromEmail(email), - att: capabilityRequest, + att: [...opts.capabilities], }, }) if (res?.error) { throw new Error('failed to authorize session', { cause: res }) } + } + + /** + * Request authorization of a session allowing this agent to issue UCANs + * signed by the passed email address. + * + * @param {`${string}@${string}`} email + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] + */ + async authorize(email, opts) { + await this.requestAuthorization(email, { + ...opts, + capabilities: opts?.capabilities || [ + { can: 'store/*' }, + { can: 'provider/add' }, + { can: 'upload/*' }, + ], + }) const sessionDelegation = /** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */ From 7aa68d48daa1c19cfe647c90e1998ae2f0d8138f Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 09:50:51 -0700 Subject: [PATCH 30/54] assert which delegations are claimed in access-client-agent.test --- .../test/access-client-agent.test.js | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 7b3eb80a8..e3d468d9e 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -93,7 +93,7 @@ for (const accessApiVariant of /** @type {const} */ ([ await requestAuthorization(accessAgent, account, [{ can: '*' }]) }) - it('can requestAuthorization, then click confirm email, then claim', async () => { + it('can authorize session with account and use', async () => { const { emails, connection } = await accessApiVariant.create() /** @type {Ucanto.Principal>} */ const account = { did: () => 'did:mailto:dag.house:example' } @@ -130,8 +130,49 @@ for (const accessApiVariant of /** @type {const} */ ([ 'access/confirm result is not an error' ) - const claimed = await accessAgent.claimDelegations() - assert.deepEqual(claimed.length, 2) + // these are delegations with audience=accessAgent.issuer + const claimedAsAgent = await accessAgent.claimDelegations() + assert.deepEqual(claimedAsAgent.length, 2) + assert.ok( + claimedAsAgent.every( + (d) => d.audience.did() === accessAgent.issuer.did() + ) + ) + + // we expect these to have sessionProofs + // one ucan/attest and one that is attested to + const delegationFromAccountToSession = claimedAsAgent.find( + (d) => d.issuer.did() === account.did() + ) + assert.ok( + delegationFromAccountToSession, + 'claimed delegationFromAccountToSession' + ) + const attestation = claimedAsAgent.find( + (d) => d.capabilities[0].can === 'ucan/attest' + ) + assert.ok(attestation, 'claimed attestation') + assert.equal( + /** @type {any} */ (attestation).capabilities[0].nb.proof.toString(), + delegationFromAccountToSession.cid.toString(), + 'ucan/attest proof cid matches delegation cid' + ) + + const accountProofs = [delegationFromAccountToSession, attestation] + assert.ok(accountProofs) + // @todo we should be able to use the accountProofs to provider/add + // const providerAddResult = await accessAgent.invokeAndExecute() + }) + + it.skip('can registerSpace', async () => { + const { connection } = await accessApiVariant.create() + const accountEmail = 'foo@dag.house' + // const account = { did: () => createDidMailtoFromEmail(accountEmail) } + const accessAgent = await AccessAgent.create(undefined, { + connection, + }) + + await accessAgent.registerSpace(accountEmail) }) }) } From 48903aa26856cf575987909b2494410503f0a628 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 11:09:47 -0700 Subject: [PATCH 31/54] chore: test registerSpace (#553) --- .../test/access-client-agent.test.js | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index e3d468d9e..09be07786 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -1,7 +1,10 @@ import { context } from './helpers/context.js' import { createTesterFromContext } from './helpers/ucanto-test-utils.js' import * as principal from '@ucanto/principal' -import { Agent as AccessAgent } from '@web3-storage/access/agent' +import { + Agent as AccessAgent, + createDidMailtoFromEmail, +} from '@web3-storage/access/agent' import * as w3caps from '@web3-storage/capabilities' import * as assert from 'assert' import * as Ucanto from '@ucanto/interface' @@ -164,19 +167,62 @@ for (const accessApiVariant of /** @type {const} */ ([ // const providerAddResult = await accessAgent.invokeAndExecute() }) - it.skip('can registerSpace', async () => { - const { connection } = await accessApiVariant.create() + it('can registerSpace', async () => { + const { connection, emails } = await accessApiVariant.create() const accountEmail = 'foo@dag.house' - // const account = { did: () => createDidMailtoFromEmail(accountEmail) } + const account = { did: () => createDidMailtoFromEmail(accountEmail) } const accessAgent = await AccessAgent.create(undefined, { connection, }) + const abort = new AbortController() + after(() => abort.abort()) + + // request agent authorization from account + requestAuthorization(accessAgent, account, [{ can: '*' }]) + // confirm authorization + const confirmationEmail = await watchForEmail(emails, 100, abort.signal) + await confirmConfirmationUrl(accessAgent.connection, confirmationEmail) + // claim delegations after confirmation + await accessAgent.claimDelegations() + + // create space + const spaceName = `space-test-${Math.random().toString().slice(2)}` + const spaceCreation = await accessAgent.createSpace(spaceName) + await accessAgent.setCurrentSpace(spaceCreation.did) + + // 'register space' - i.e. add a storage provider as an account + await accessAgent.registerSpace(accountEmail, { + provider: /** @type {Ucanto.DID<'web'>} */ (connection.id.did()), + }) + }) - await accessAgent.registerSpace(accountEmail) + it.skip('same agent, multiple accounts, try to provider/add', () => { + throw new Error('todo') + }) + + it.skip('can can use second device with same account', () => { + throw new Error('todo') }) }) } +/** + * + * @param {Ucanto.ConnectionView} connection + * @param {{ url: string|URL }} confirmation + */ +async function confirmConfirmationUrl(connection, confirmation) { + // extract confirmation invocation from email that was sent by service while handling access/authorize + const confirm = await extractConfirmInvocation(new URL(confirmation.url)) + // invoke the access/confirm invocation as if the user had clicked the email + const [confirmResult] = await connection.execute(confirm) + assert.notEqual( + confirmResult.error, + true, + 'access/confirm result is not an error' + ) +} + /** * @param {URL} confirmationUrl * @returns {Promise>} From 3d70dd9e6a1ff121389c566437744b769aaebfb2 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Thu, 16 Mar 2023 11:54:36 -0700 Subject: [PATCH 32/54] fix: update `proofs` function so that it adds all necessary session proofs after finding the proofs that we need for a particular operation, look through the list of session proofs we have access to and add as necessary --- packages/access-client/src/agent-data.js | 43 ++++++++++++++++++++---- packages/access-client/src/agent.js | 32 ++++++------------ 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index f4c981212..dda2a564a 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -1,4 +1,5 @@ import { Signer } from '@ucanto/principal' +import { Link } from '@ucanto/validator' import { Signer as EdSigner } from '@ucanto/principal/ed25519' import { importDAG } from '@ucanto/core/delegation' import * as Ucanto from '@ucanto/interface' @@ -149,16 +150,44 @@ export class AgentData { } /** - * get session proof + * Is the given capability a session attestation? + * + * @param {EdSigner.Capability} cap + * @returns {boolean} + */ +const isSessionCapability = (cap) => cap.can === Access.session.can + +/** + * Is the given delegation a session proof? + * + * @param {Ucanto.Delegation} delegation + * @returns {boolean} + */ +const isSessionProof = (delegation) => + delegation.capabilities.some((cap) => isSessionCapability(cap)) + +/** + * Get a map from CIDs to the session proofs that reference them * * @param {AgentData} data - * @returns {Ucanto.Delegation | undefined} + * @returns {Record} */ -export function getSessionProof(data) { +export function getSessionProofs(data) { + /** @type {Record} */ + const proofs = {} for (const { delegation } of data.delegations.values()) { - const cap = delegation.capabilities.find( - (c) => c.can === Access.session.can // TODO we should make sure this is the current session proof - we were checking nb.key but that doesn't seem to exist in the staging ucan/attest at the moment - ) - if (cap && !isExpired(delegation)) return delegation + if (isSessionProof(delegation)) { + const cap = delegation.capabilities.find((cap) => + isSessionCapability(cap) + ) + if (cap && !isExpired(delegation)) { + // @ts-expect-error "proof" does not exist in caveats, unless it's a session capability - TODO: is there a better way to type this? + const proof = /** @type {Link | undefined} */ (cap.nb?.proof) + if (proof) { + proofs[proof.toString()] = delegation + } + } + } } + return proofs } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index c047a9597..671b8e636 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -25,7 +25,7 @@ import { validate, canDelegateCapability, } from './delegations.js' -import { AgentData, getSessionProof } from './agent-data.js' +import { AgentData, getSessionProofs } from './agent-data.js' import { createDidMailtoFromEmail } from './utils/did-mailto.js' export { AgentData, createDidMailtoFromEmail } @@ -255,26 +255,20 @@ export class Agent { */ proofs(caps) { const arr = [] - const session = getSessionProof(this.#data) - let hasSessionDelegations = false for (const { delegation } of this.#delegations(caps)) { - const aud = delegation.audience - if ( - aud.did() === this.issuer.did() || - aud.did() === session?.audience.did() - ) { + if (delegation.audience.did() === this.issuer.did()) { arr.push(delegation) } - if (aud.did() === session?.audience.did()) { - hasSessionDelegations = true - } } - if (session && hasSessionDelegations) { - arr.push(session) + const sessions = getSessionProofs(this.#data) + for (const proof of arr) { + const session = sessions[proof.asCID.toString()] + if (session) { + arr.push(session) + } } - return arr } @@ -530,8 +524,8 @@ export class Agent { (await this.#waitForDelegation(opts)) const cap = sessionDelegation.capabilities.find( - // @ts-expect-error "key" does not exist in object, unless it's a session capability - (c) => c.can === Access.session.can && c.nb.key === this.issuer.did() + // @ts-expect-error "proof" does not exist in caveats, unless it's a session capability - TODO: is there a better way to type this? + (c) => c.can === Access.session.can && c.nb.proof === this.issuer.did() ) if (!cap && isExpired(sessionDelegation)) { throw new Error('received invalid delegation') @@ -592,12 +586,6 @@ export class Agent { return this.invokeAndExecute(Provider.add, { audience: this.connection.id, with: account.did(), - proofs: this.proofs([ - { - can: 'provider/add', - with: account.did(), - }, - ]), nb: { provider, consumer: space, From 14390afbc6a5d42375ef534d8f0779ee1f0d5772 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 13:55:01 -0700 Subject: [PATCH 33/54] chore: test access-client-agent same agent, multiple accounts, addProvider (#554) --- .../test/access-client-agent.test.js | 115 ++++++++++++------ packages/access-client/src/agent-use-cases.js | 75 ++++++++++++ packages/access-client/src/agent.js | 106 +++------------- 3 files changed, 176 insertions(+), 120 deletions(-) create mode 100644 packages/access-client/src/agent-use-cases.js diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 09be07786..853470d24 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -1,9 +1,13 @@ +/* eslint-disable unicorn/consistent-function-scoping */ import { context } from './helpers/context.js' import { createTesterFromContext } from './helpers/ucanto-test-utils.js' import * as principal from '@ucanto/principal' import { + addProvider, Agent as AccessAgent, + claimDelegations, createDidMailtoFromEmail, + requestAuthorization, } from '@web3-storage/access/agent' import * as w3caps from '@web3-storage/capabilities' import * as assert from 'assert' @@ -14,6 +18,7 @@ import { stringToDelegations, } from '@web3-storage/access/encoding' import * as delegationsResponse from '../src/utils/delegations-response.js' +import { AgentData } from '@web3-storage/access' for (const accessApiVariant of /** @type {const} */ ([ { @@ -66,10 +71,10 @@ for (const accessApiVariant of /** @type {const} */ ([ const emailCount = emails.length const abort = new AbortController() after(() => abort.abort()) - await accessAgent.requestAuthorization('example@dag.house', { - signal: abort.signal, - capabilities: [{ can: '*' }], - }) + const account = { + did: () => createDidMailtoFromEmail('example@dag.house'), + } + await requestAuthorization(accessAgent, account, [{ can: '*' }]) assert.deepEqual(emails.length, emailCount + 1) }) @@ -134,7 +139,11 @@ for (const accessApiVariant of /** @type {const} */ ([ ) // these are delegations with audience=accessAgent.issuer - const claimedAsAgent = await accessAgent.claimDelegations() + const claimedAsAgent = await claimDelegations( + accessAgent, + accessAgent.issuer.did(), + { addProofs: true } + ) assert.deepEqual(claimedAsAgent.length, 2) assert.ok( claimedAsAgent.every( @@ -183,7 +192,9 @@ for (const accessApiVariant of /** @type {const} */ ([ const confirmationEmail = await watchForEmail(emails, 100, abort.signal) await confirmConfirmationUrl(accessAgent.connection, confirmationEmail) // claim delegations after confirmation - await accessAgent.claimDelegations() + await claimDelegations(accessAgent, accessAgent.issuer.did(), { + addProofs: true, + }) // create space const spaceName = `space-test-${Math.random().toString().slice(2)}` @@ -196,8 +207,70 @@ for (const accessApiVariant of /** @type {const} */ ([ }) }) - it.skip('same agent, multiple accounts, try to provider/add', () => { - throw new Error('todo') + it('same agent, multiple accounts, provider/add', async () => { + const accounts = /** @type {const} */ ([ + 'test-a@dag.house', + 'test-b@dag.house', + ]).map((email) => ({ + email, + did: function thisEmailDidMailto() { + return createDidMailtoFromEmail(this.email) + }, + })) + const { connection, emails } = await accessApiVariant.create() + const accessAgentData = await AgentData.create() + const accessAgent = await AccessAgent.create(accessAgentData, { + connection, + }) + const abort = new AbortController() + after(() => abort.abort()) + /** @param {AgentData} agentData */ + const countDelegations = ({ delegations }) => + [...delegations.values()].length + assert.deepEqual( + countDelegations(accessAgentData), + 0, + 'agentData has zero delegations initially' + ) + let expectedDataDelegations = 0 + for (const account of accounts) { + // request agent authorization from account + await requestAuthorization(accessAgent, account, [{ can: '*' }]) + // confirm authorization + const confirmationEmail = await watchForEmail(emails, 100, abort.signal) + await confirmConfirmationUrl(accessAgent.connection, confirmationEmail) + // claim delegations after confirmation + await claimDelegations(accessAgent, accessAgent.issuer.did(), { + addProofs: true, + }) + // expect two new delegations, [delegationFromAccount, attestationFromService] + expectedDataDelegations += 2 + assert.deepEqual( + countDelegations(accessAgentData), + expectedDataDelegations, + `agentData has ${expectedDataDelegations} after authorizing account ${account.did()} and claiming` + ) + } + + // create space + const spaceName = `space-test-${Math.random().toString().slice(2)}` + const spaceCreation = await accessAgent.createSpace(spaceName) + // expect 1 new delegation from space.did() -> accessAgent.issuer.did() + expectedDataDelegations += 1 + assert.deepEqual( + countDelegations(accessAgentData), + expectedDataDelegations, + `agentData has ${expectedDataDelegations} after calling accessClientAgent.createSpace(...)` + ) + + await accessAgent.setCurrentSpace(spaceCreation.did) + + const provider = /** @type {Ucanto.DID<'web'>} */ ( + accessAgent.connection.id.did() + ) + for (const account of accounts) { + await addProvider(accessAgent, spaceCreation.did, account, provider) + } }) it.skip('can can use second device with same account', () => { @@ -271,32 +344,6 @@ function watchForEmail(emails, retryAfter, abort) { * @typedef {import('./helpers/ucanto-test-utils.js').AccessService} AccessService */ -/** - * request authorization using access-api access/authorize - * - * @param {AccessAgent} access - * @param {Ucanto.Principal>} authorizer - who you are requesting authorization from - * @param {Iterable<{ can: Ucanto.Ability }>} abilities - e.g. [{ can: '*' }] - */ -async function requestAuthorization(access, authorizer, abilities) { - const authorizeResult = await access.invokeAndExecute( - w3caps.Access.authorize, - { - audience: access.connection.id, - with: access.issuer.did(), - nb: { - iss: authorizer.did(), - att: [...abilities], - }, - } - ) - assert.notDeepStrictEqual( - authorizeResult.error, - true, - 'authorize result is not an error' - ) -} - /** * @param {principal.ed25519.Signer.Signer<`did:web:${string}`, principal.ed25519.Signer.UCAN.SigAlg>} service * @param {AccessAgent} access diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js new file mode 100644 index 000000000..6df332b68 --- /dev/null +++ b/packages/access-client/src/agent-use-cases.js @@ -0,0 +1,75 @@ +import { Agent as AccessAgent } from './agent.js' +import * as Ucanto from '@ucanto/interface' +import * as Access from '@web3-storage/capabilities/access' +import { bytesToDelegations } from './encoding.js' +import { Provider } from '@web3-storage/capabilities' + +/** + * Request authorization of a session allowing this agent to issue UCANs + * signed by the passed email address. + * + * @param {AccessAgent} access + * @param {Ucanto.Principal>} account + * @param {Iterable<{ can: Ucanto.Ability }>} capabilities + */ +export async function requestAuthorization(access, account, capabilities) { + const res = await access.invokeAndExecute(Access.authorize, { + audience: access.connection.id, + with: access.issuer.did(), + nb: { + iss: account.did(), + att: [...capabilities], + }, + }) + if (res?.error) { + throw new Error('failed to authorize session', { cause: res }) + } +} + +/** + * claim delegations delegated to an audience + * + * @param {AccessAgent} access + * @param {Ucanto.DID} [delegee] - audience of claimed delegations. defaults to access.connection.id.did() + * @param {object} options + * @param {boolean} [options.addProofs] - whether to addProof to access agent + * @returns + */ +export async function claimDelegations( + access, + delegee = access.connection.id.did(), + { addProofs = false } = {} +) { + const res = await access.invokeAndExecute(Access.claim, { + audience: access.connection.id, + with: delegee, + }) + if (res.error) { + throw new Error('error claiming delegations') + } + const delegations = Object.values(res.delegations).flatMap((bytes) => + bytesToDelegations(bytes) + ) + if (addProofs) for (const d of delegations) access.addProof(d) + return delegations +} + +/** + * @param {AccessAgent} access + * @param {Ucanto.DID<'key'>} space + * @param {Ucanto.Principal>} account + * @param {Ucanto.DID<'web'>} provider - e.g. 'did:web:staging.web3.storage' + */ +export async function addProvider(access, space, account, provider) { + const result = await access.invokeAndExecute(Provider.add, { + audience: access.connection.id, + with: account.did(), + nb: { + provider, + consumer: space, + }, + }) + if (result.error) { + throw new Error(`error adding provider`, { cause: result }) + } +} diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 671b8e636..659110692 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -12,9 +12,8 @@ import { Peer } from './awake/peer.js' import * as Space from '@web3-storage/capabilities/space' import * as Voucher from '@web3-storage/capabilities/voucher' import * as Access from '@web3-storage/capabilities/access' -import * as Provider from '@web3-storage/capabilities/provider' -import { stringToDelegation, bytesToDelegations } from './encoding.js' +import { stringToDelegation } from './encoding.js' import { Websocket, AbortError } from './utils/ws.js' import { Signer } from '@ucanto/principal/ed25519' import { Verifier } from '@ucanto/principal' @@ -27,8 +26,14 @@ import { } from './delegations.js' import { AgentData, getSessionProofs } from './agent-data.js' import { createDidMailtoFromEmail } from './utils/did-mailto.js' +import { + addProvider, + claimDelegations, + requestAuthorization, +} from './agent-use-cases.js' export { AgentData, createDidMailtoFromEmail } +export * from './agent-use-cases.js' const HOST = 'https://access.web3.storage' const PRINCIPAL = DID.parse('did:web:web3.storage') @@ -476,30 +481,6 @@ export class Agent { } } - /** - * Request authorization of a session allowing this agent to issue UCANs - * signed by the passed email address. - * - * @param {`${string}@${string}`} email - * @param {object} opts - * @param {AbortSignal} [opts.signal] - * @param {Iterable<{ can: Ucanto.Ability }>} opts.capabilities - */ - async requestAuthorization(email, opts) { - const res = await this.invokeAndExecute(Access.authorize, { - audience: this.connection.id, - with: this.issuer.did(), - nb: { - iss: createDidMailtoFromEmail(email), - att: [...opts.capabilities], - }, - }) - - if (res?.error) { - throw new Error('failed to authorize session', { cause: res }) - } - } - /** * Request authorization of a session allowing this agent to issue UCANs * signed by the passed email address. @@ -510,14 +491,16 @@ export class Agent { * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] */ async authorize(email, opts) { - await this.requestAuthorization(email, { - ...opts, - capabilities: opts?.capabilities || [ + const account = { did: () => createDidMailtoFromEmail(email) } + await requestAuthorization( + this, + account, + opts?.capabilities || [ { can: 'store/*' }, { can: 'provider/add' }, { can: 'upload/*' }, - ], - }) + ] + ) const sessionDelegation = /** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */ @@ -535,46 +518,7 @@ export class Agent { // claim delegations here because we will need an ucan/attest from the service to // pair with the session delegation we just claimed to make it work - await this.claimDelegations() - } - - async claimDelegations() { - const res = await this.invokeAndExecute(Access.claim, { - audience: this.connection.id, - with: this.issuer.did(), - }) - if (res.error) { - throw new Error('error claiming delegations') - } - const delegations = Object.values(res.delegations).flatMap((bytes) => - bytesToDelegations(bytes) - ) - for (const delegation of delegations) { - this.addProof(delegation) - - // if we can find a store/* capability in this delegation, look in the proofs - // for the concrete capabilities where space DIDs will be specified - // TODO: this was my first attempt at inferring spaces from claimed delegations, but I think it needs work - tv - // if (delegation.capabilities.some((cap) => cap.can === 'store/*')) { - // const spaceListingProof = delegation.proofs.find((del) => - // del.capabilities.some((cap) => cap.can === 'store/list') - // ) - // const spaceListingCap = spaceListingProof.capabilities.find( - // (cap) => cap.can === 'store/list' - // ) - // if (spaceListingCap) { - // this.#data.addSpace( - // spaceListingCap.with, - // { isRegistered: true }, - // delegation - // ) - // } - // } - } - - // TODO: should we be inferring which spaces we have access to here and updating local space state? - - return delegations + await claimDelegations(this, this.issuer.did(), { addProofs: true }) } /** @@ -582,15 +526,8 @@ export class Agent { * @param {Ucanto.Principal>} account * @param {Ucanto.DID<'web'>} provider - e.g. 'did:web:staging.web3.storage' */ - async addProvider(space, account, provider) { - return this.invokeAndExecute(Provider.add, { - audience: this.connection.id, - with: account.did(), - nb: { - provider, - consumer: space, - }, - }) + async #addProvider(space, account, provider) { + return addProvider(this, space, account, provider) } /** @@ -598,7 +535,7 @@ export class Agent { * @param {Ucanto.DID<'key'>} space * @param {Ucanto.Principal>} account */ - async delegateSpaceAccessToAccount(space, account) { + async #delegateSpaceAccessToAccount(space, account) { const spaceSaysAccountCanAdminSpace = await createIssuerSaysAccountCanAdminSpace(this.issuer, space, account) return this.invokeAndExecute(Access.delegate, { @@ -657,11 +594,8 @@ export class Agent { throw new Error('Space already registered with web3.storage.') } const account = { did: () => createDidMailtoFromEmail(email) } - const providerResult = await this.addProvider(space, account, provider) - if (providerResult.error) { - throw new Error(providerResult.message, { cause: providerResult }) - } - const delegateSpaceAccessResult = await this.delegateSpaceAccessToAccount( + await this.#addProvider(space, account, provider) + const delegateSpaceAccessResult = await this.#delegateSpaceAccessToAccount( space, account ) From ea385ba2cf689545267098bfc4237178e0b49f53 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:17:14 -0700 Subject: [PATCH 34/54] chore: 433 test multidevice (#555) --- .../test/access-client-agent.test.js | 76 +++++++++++++++++-- packages/access-client/src/agent.js | 6 +- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 853470d24..95aeec860 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -213,9 +213,7 @@ for (const accessApiVariant of /** @type {const} */ ([ 'test-b@dag.house', ]).map((email) => ({ email, - did: function thisEmailDidMailto() { - return createDidMailtoFromEmail(this.email) - }, + did: thisEmailDidMailto, })) const { connection, emails } = await accessApiVariant.create() const accessAgentData = await AgentData.create() @@ -273,8 +271,68 @@ for (const accessApiVariant of /** @type {const} */ ([ } }) - it.skip('can can use second device with same account', () => { - throw new Error('todo') + it('can use second device with same account', async () => { + const account = { + email: 'example@dag.house', + did: thisEmailDidMailto, + } + const { connection, emails } = await accessApiVariant.create() + const provider = /** @type {Ucanto.DID<'web'>} */ (connection.id.did()) + const abort = new AbortController() + after(() => abort.abort()) + + // first device + const deviceAAgentData = await AgentData.create() + const deviceA = await AccessAgent.create(deviceAAgentData, { + connection, + }) + + // deviceA authorization + await requestAuthorization(deviceA, account, [{ can: '*' }]) + await confirmConfirmationUrl( + deviceA.connection, + await watchForEmail(emails, 100, abort.signal) + ) + await claimDelegations(deviceA, deviceA.issuer.did(), { + addProofs: true, + }) + + // deviceA creates a space + const spaceCreation = await deviceA.createSpace( + `space-test-${Math.random().toString().slice(2)}` + ) + assert.ok(spaceCreation.did) + // deviceA registers a space + await deviceA.registerSpace(account.email, { + provider, + space: spaceCreation.did, + }) + + /** + * second device - deviceB + */ + const deviceBData = await AgentData.create() + const deviceB = await AccessAgent.create(deviceBData, { + connection, + }) + // authorize deviceB + await requestAuthorization(deviceB, account, [{ can: '*' }]) + await confirmConfirmationUrl( + deviceB.connection, + await watchForEmail(emails, 100, abort.signal) + ) + // claim delegations after confirmation + const deviceBClaimed = await claimDelegations( + deviceB, + deviceB.issuer.did(), + { + addProofs: true, + } + ) + assert.equal(deviceBClaimed.length, 2, 'deviceB claimed delegations') + + // try to addProvider + await addProvider(deviceB, spaceCreation.did, account, provider) }) }) } @@ -412,3 +470,11 @@ async function testSessionAuthorization(service, access, account, emails) { ] assert.ok(claimedDelegations1.length > 0, 'claimed some delegations') } + +/** + * @this {{email: string}} + * @returns {Ucanto.DID<'mailto'>} + */ +function thisEmailDidMailto() { + return createDidMailtoFromEmail(this.email) +} diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 659110692..c62d1da0e 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -568,10 +568,11 @@ export class Agent { * @param {string} email * @param {object} [opts] * @param {AbortSignal} [opts.signal] + * @param {Ucanto.DID<'key'>} [opts.space] * @param {Ucanto.DID<'web'>} [opts.provider] - provider to register - defaults to this.connection.id */ async #newRegisterSpace(email, opts) { - const space = this.currentSpace() + const space = opts?.space || this.currentSpace() const spaceMeta = space ? this.#data.spaces.get(space) : undefined const provider = opts?.provider || @@ -617,6 +618,7 @@ export class Agent { * @param {string} email * @param {object} [opts] * @param {AbortSignal} [opts.signal] + * @param {Ucanto.DID<'key'>} [opts.space] - space to register * @param {Ucanto.DID<'web'>} [opts.provider] - provider to register - defaults to this.connection.id */ async registerSpace(email, opts = {}) { @@ -625,7 +627,7 @@ export class Agent { return this.#newRegisterSpace(email, opts) } - const space = this.currentSpace() + const space = opts.space ?? this.currentSpace() const service = this.connection.id const spaceMeta = space ? this.#data.spaces.get(space) : undefined From e28f993c5698a9eac2ff9833e271e01e9600ec30 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:59:57 -0700 Subject: [PATCH 35/54] fix: authorize claims as account, not only this.issuer (#556) --- .../test/access-client-agent.test.js | 44 +++++++++++++++++-- packages/access-client/src/agent.js | 22 +++++++--- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 95aeec860..7c338ea8d 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -1,6 +1,9 @@ /* eslint-disable unicorn/consistent-function-scoping */ import { context } from './helpers/context.js' -import { createTesterFromContext } from './helpers/ucanto-test-utils.js' +import { + assertNotError, + createTesterFromContext, +} from './helpers/ucanto-test-utils.js' import * as principal from '@ucanto/principal' import { addProvider, @@ -321,18 +324,51 @@ for (const accessApiVariant of /** @type {const} */ ([ deviceB.connection, await watchForEmail(emails, 100, abort.signal) ) - // claim delegations after confirmation - const deviceBClaimed = await claimDelegations( + // claim delegations aud=deviceB.issuer + const deviceBIssuerClaimed = await claimDelegations( deviceB, deviceB.issuer.did(), { addProofs: true, } ) - assert.equal(deviceBClaimed.length, 2, 'deviceB claimed delegations') + assert.equal( + deviceBIssuerClaimed.length, + 2, + 'deviceBIssuerClaimed delegations' + ) + // claim delegations aud=account + const deviceBAccountClaimed = await claimDelegations( + deviceB, + account.did(), + { + addProofs: true, + } + ) + assert.equal( + deviceBAccountClaimed.length, + 1, + 'deviceBAccountClaimed delegations' + ) // try to addProvider await addProvider(deviceB, spaceCreation.did, account, provider) + + // issuer + account proofs should authorize deviceB to invoke space/info + const spaceInfoResult = await deviceB.invokeAndExecute( + w3caps.Space.info, + { + with: spaceCreation.did, + } + ) + assertNotError(spaceInfoResult) + assert.notEqual( + spaceInfoResult.error, + true, + 'spaceInfoResult is not an error' + ) + assert.ok(!spaceInfoResult.error) + assert.deepEqual(spaceInfoResult.did, spaceCreation.did) }) }) } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index c62d1da0e..996746b90 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -43,6 +43,7 @@ const PRINCIPAL = DID.parse('did:web:web3.storage') * @param {Ucanto.DID} space * @param {Ucanto.Principal>} account * @param {Ucanto.Capabilities} capabilities + * @param {Ucanto.Delegation[]} proofs * @returns */ async function createIssuerSaysAccountCanAdminSpace( @@ -54,12 +55,14 @@ async function createIssuerSaysAccountCanAdminSpace( can: '*', with: space, }, - ] + ], + proofs = [] ) { return ucanto.delegate({ issuer, audience: account, capabilities, + proofs, }) } @@ -519,6 +522,7 @@ export class Agent { // claim delegations here because we will need an ucan/attest from the service to // pair with the session delegation we just claimed to make it work await claimDelegations(this, this.issuer.did(), { addProofs: true }) + await claimDelegations(this, account.did(), { addProofs: true }) } /** @@ -536,16 +540,22 @@ export class Agent { * @param {Ucanto.Principal>} account */ async #delegateSpaceAccessToAccount(space, account) { - const spaceSaysAccountCanAdminSpace = - await createIssuerSaysAccountCanAdminSpace(this.issuer, space, account) + const issuerSaysAccountCanAdminSpace = + await createIssuerSaysAccountCanAdminSpace( + this.issuer, + space, + account, + undefined, + this.proofs([{ with: space, can: '*' }]) + ) return this.invokeAndExecute(Access.delegate, { audience: this.connection.id, with: space, expiration: Infinity, nb: { delegations: { - [spaceSaysAccountCanAdminSpace.cid.toString()]: - spaceSaysAccountCanAdminSpace.cid, + [issuerSaysAccountCanAdminSpace.cid.toString()]: + issuerSaysAccountCanAdminSpace.cid, }, }, proofs: [ @@ -555,7 +565,7 @@ export class Agent { this.issuer ), // must be embedded here because it's referenced by cid in .nb.delegations - spaceSaysAccountCanAdminSpace, + issuerSaysAccountCanAdminSpace, ], }) } From 50960f0f20b24e64c0cd23d5e055d6b24467f70e Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:28:13 -0700 Subject: [PATCH 36/54] fix: 433 rm todo (#557) --- packages/access-api/test/access-client-agent.test.js | 2 -- packages/access-client/src/agent-data.js | 12 ++++-------- packages/access-client/src/agent.js | 9 --------- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 7c338ea8d..99af8e0fd 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -175,8 +175,6 @@ for (const accessApiVariant of /** @type {const} */ ([ const accountProofs = [delegationFromAccountToSession, attestation] assert.ok(accountProofs) - // @todo we should be able to use the accountProofs to provider/add - // const providerAddResult = await accessAgent.invokeAndExecute() }) it('can registerSpace', async () => { diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index dda2a564a..6a94438c4 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -1,5 +1,4 @@ import { Signer } from '@ucanto/principal' -import { Link } from '@ucanto/validator' import { Signer as EdSigner } from '@ucanto/principal/ed25519' import { importDAG } from '@ucanto/core/delegation' import * as Ucanto from '@ucanto/interface' @@ -152,7 +151,7 @@ export class AgentData { /** * Is the given capability a session attestation? * - * @param {EdSigner.Capability} cap + * @param {Ucanto.Capability} cap * @returns {boolean} */ const isSessionCapability = (cap) => cap.can === Access.session.can @@ -161,7 +160,7 @@ const isSessionCapability = (cap) => cap.can === Access.session.can * Is the given delegation a session proof? * * @param {Ucanto.Delegation} delegation - * @returns {boolean} + * @returns {delegation is Ucanto.Delegation<[import('./types').AccessSession]>} */ const isSessionProof = (delegation) => delegation.capabilities.some((cap) => isSessionCapability(cap)) @@ -177,12 +176,9 @@ export function getSessionProofs(data) { const proofs = {} for (const { delegation } of data.delegations.values()) { if (isSessionProof(delegation)) { - const cap = delegation.capabilities.find((cap) => - isSessionCapability(cap) - ) + const cap = delegation.capabilities[0] if (cap && !isExpired(delegation)) { - // @ts-expect-error "proof" does not exist in caveats, unless it's a session capability - TODO: is there a better way to type this? - const proof = /** @type {Link | undefined} */ (cap.nb?.proof) + const proof = cap.nb.proof if (proof) { proofs[proof.toString()] = delegation } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 996746b90..a430c3814 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -461,7 +461,6 @@ export class Agent { return } - // TODO cache these const proofs = this.proofs([ { can: 'space/info', @@ -509,14 +508,6 @@ export class Agent { /** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */ (await this.#waitForDelegation(opts)) - const cap = sessionDelegation.capabilities.find( - // @ts-expect-error "proof" does not exist in caveats, unless it's a session capability - TODO: is there a better way to type this? - (c) => c.can === Access.session.can && c.nb.proof === this.issuer.did() - ) - if (!cap && isExpired(sessionDelegation)) { - throw new Error('received invalid delegation') - } - await this.addProof(sessionDelegation) // claim delegations here because we will need an ucan/attest from the service to From 68857437be460e882f5195ea72f1b1e228dbdc75 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Thu, 16 Mar 2023 16:36:28 -0700 Subject: [PATCH 37/54] feat: store spaces after claiming delegations (#558) some hacky logic to enable the "claim spaces on second device" flow - @bengo and I are planning on refactoring this whole API to pull spaces out of the agent but want to get it working with the current API first --- packages/access-client/src/agent-use-cases.js | 4 ++++ packages/access-client/src/agent.js | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 6df332b68..00626ce84 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -51,6 +51,10 @@ export async function claimDelegations( bytesToDelegations(bytes) ) if (addProofs) for (const d of delegations) access.addProof(d) + + // TODO get rid of this - we'd like to move responsibility for storing space metadata out of agent-data soon + if (addProofs) access._addSpacesFromDelegations(delegations) + return delegations } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index a430c3814..57a05f7a2 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -516,6 +516,30 @@ export class Agent { await claimDelegations(this, account.did(), { addProofs: true }) } + /** + * Given a list of delegations, add to agent data spaces list. + * + * TODO: DON'T USE - we'd like to move away from storing space information inside the agent, planning on removing this soon! + * + * @param {Ucanto.Delegation[]} delegations + */ + async _addSpacesFromDelegations(delegations) { + if (delegations.length > 0) { + const allows = ucanto.Delegation.allows( + delegations[0], + ...delegations.slice(1) + ) + for (const [did, value] of Object.entries(allows)) { + // TODO I don't think this should be `store/*` but this works for today + if (value['store/*']) { + this.#data.addSpace(/** @type {Ucanto.DID} */ (did), { + isRegistered: true, + }) + } + } + } + } + /** * @param {Ucanto.DID<'key'>} space * @param {Ucanto.Principal>} account From 1e9e70f5edd3400d9d76febb1475e7219fd13cbd Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 17:25:06 -0700 Subject: [PATCH 38/54] fix: 433 avoid new agent method (#559) --- .../test/access-client-agent.test.js | 9 +++++ packages/access-client/src/agent-data.js | 2 ++ packages/access-client/src/agent-use-cases.js | 36 +++++++++++++++++-- packages/access-client/src/agent.js | 28 +++------------ packages/access-client/src/types.ts | 2 ++ 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 99af8e0fd..b929da267 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -7,6 +7,7 @@ import { import * as principal from '@ucanto/principal' import { addProvider, + addSpacesFromDelegations, Agent as AccessAgent, claimDelegations, createDidMailtoFromEmail, @@ -369,6 +370,14 @@ for (const accessApiVariant of /** @type {const} */ ([ assert.deepEqual(spaceInfoResult.did, spaceCreation.did) }) }) + + it('can addSpacesFromDelegations', async () => { + const { connection } = await accessApiVariant.create() + const accessAgent = await AccessAgent.create(undefined, { + connection, + }) + await addSpacesFromDelegations(accessAgent, []) + }) } /** diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 6a94438c4..2259a70c2 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -110,6 +110,7 @@ export class AgentData { } /** + * @deprecated * @param {import('@ucanto/interface').DID} did * @param {import('./types').SpaceMeta} meta * @param {import('@ucanto/interface').Delegation} [proof] @@ -120,6 +121,7 @@ export class AgentData { } /** + * @deprecated * @param {import('@ucanto/interface').DID<'key'>} did */ async setCurrentSpace(did) { diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 00626ce84..55a11f622 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -1,8 +1,9 @@ -import { Agent as AccessAgent } from './agent.js' +import { Agent as AccessAgent, agentToData } from './agent.js' import * as Ucanto from '@ucanto/interface' import * as Access from '@web3-storage/capabilities/access' import { bytesToDelegations } from './encoding.js' import { Provider } from '@web3-storage/capabilities' +import { Delegation } from '@ucanto/core' /** * Request authorization of a session allowing this agent to issue UCANs @@ -52,12 +53,41 @@ export async function claimDelegations( ) if (addProofs) for (const d of delegations) access.addProof(d) - // TODO get rid of this - we'd like to move responsibility for storing space metadata out of agent-data soon - if (addProofs) access._addSpacesFromDelegations(delegations) + if (addProofs) { + await addSpacesFromDelegations(access, delegations) + } return delegations } +/** + * Given a list of delegations, add to agent data spaces list. + * + * TODO: DON'T USE - we'd like to move away from storing space information inside the agent, planning on removing this soon! + * + * @param {AccessAgent} access + * @param {Ucanto.Delegation[]} delegations + */ +export async function addSpacesFromDelegations(access, delegations) { + const data = agentToData.get(access) + if (!data) { + throw Object.assign(new Error(`cannot determine AgentData for Agent`), { + agent: access, + }) + } + if (delegations.length > 0) { + const allows = Delegation.allows(delegations[0], ...delegations.slice(1)) + for (const [did, value] of Object.entries(allows)) { + // TODO I don't think this should be `store/*` but this works for today + if (value['store/*']) { + data.addSpace(/** @type {Ucanto.DID} */ (did), { + isRegistered: true, + }) + } + } + } +} + /** * @param {AccessAgent} access * @param {Ucanto.DID<'key'>} space diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 57a05f7a2..be56941f8 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -38,6 +38,9 @@ export * from './agent-use-cases.js' const HOST = 'https://access.web3.storage' const PRINCIPAL = DID.parse('did:web:web3.storage') +/** @type {WeakMap} */ +export const agentToData = new WeakMap() + /** * @param {Ucanto.Signer>} issuer * @param {Ucanto.DID} space @@ -152,6 +155,7 @@ export class Agent { url: this.url, }) this.#data = data + agentToData.set(this, this.#data) } /** @@ -516,30 +520,6 @@ export class Agent { await claimDelegations(this, account.did(), { addProofs: true }) } - /** - * Given a list of delegations, add to agent data spaces list. - * - * TODO: DON'T USE - we'd like to move away from storing space information inside the agent, planning on removing this soon! - * - * @param {Ucanto.Delegation[]} delegations - */ - async _addSpacesFromDelegations(delegations) { - if (delegations.length > 0) { - const allows = ucanto.Delegation.allows( - delegations[0], - ...delegations.slice(1) - ) - for (const [did, value] of Object.entries(allows)) { - // TODO I don't think this should be `store/*` but this works for today - if (value['store/*']) { - this.#data.addSpace(/** @type {Ucanto.DID} */ (did), { - isRegistered: true, - }) - } - } - } - } - /** * @param {Ucanto.DID<'key'>} space * @param {Ucanto.Principal>} account diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index 7342e7edc..8777f8998 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -170,7 +170,9 @@ export type CIDString = string export interface AgentDataModel { meta: AgentMeta principal: Signer> + /** @deprecated */ currentSpace?: DID<'key'> + /** @deprecated */ spaces: Map delegations: Map } From 04323099e28bcf283a4d7871ef2e83dbfb6d5a17 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 17:41:46 -0700 Subject: [PATCH 39/54] start skipped test invoking authorize method --- .../test/access-client-agent.test.js | 24 ++++++++++++++++++- packages/access-client/src/agent-use-cases.js | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index b929da267..f32bad988 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -378,6 +378,28 @@ for (const accessApiVariant of /** @type {const} */ ([ }) await addSpacesFromDelegations(accessAgent, []) }) + + it.skip('multi device authorize method', async () => { + const abort = new AbortController() + after(() => abort.abort()) + const account = { + email: /** @type {const} */ ('example@dag.house'), + did: thisEmailDidMailto, + } + const { connection, emails } = await accessApiVariant.create() + const deviceA = await AccessAgent.create(undefined, { + connection, + }) + + const authorize = () => deviceA.authorize(account.email) + const clickNextConfirmationLink = () => + watchForEmail(emails, 100, abort.signal).then((email) => { + return confirmConfirmationUrl(deviceA.connection, email) + }) + // authorize() will hang because it tries to connect ws to localhost:8787 + // which gets ECONNREFUSED, but ../src/ws.js swallws error + await Promise.all([authorize(), clickNextConfirmationLink()]) + }) } /** @@ -417,7 +439,7 @@ async function extractConfirmInvocation(confirmationUrl) { /** * @param {Array<{ url: string }>} emails - * @param {number} [retryAfter] + * @param {number} retryAfter * @param {AbortSignal} [abort] * @returns {Promise<{ url: string }>} latest email, once received */ diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 55a11f622..9c0496367 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -61,6 +61,7 @@ export async function claimDelegations( } /** + * @private * Given a list of delegations, add to agent data spaces list. * * TODO: DON'T USE - we'd like to move away from storing space information inside the agent, planning on removing this soon! From 97669c5b0080aafa4968c86c6c3ae5b79dc5b1a4 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 18:10:59 -0700 Subject: [PATCH 40/54] test can poll access/claim to find out about confirmation --- .../test/access-client-agent.test.js | 34 +++++++++++++++ packages/access-client/src/agent-use-cases.js | 41 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index f32bad988..7fd9a9c3d 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -11,6 +11,7 @@ import { Agent as AccessAgent, claimDelegations, createDidMailtoFromEmail, + expectNewClaimableDelegations, requestAuthorization, } from '@web3-storage/access/agent' import * as w3caps from '@web3-storage/capabilities' @@ -400,6 +401,39 @@ for (const accessApiVariant of /** @type {const} */ ([ // which gets ECONNREFUSED, but ../src/ws.js swallws error await Promise.all([authorize(), clickNextConfirmationLink()]) }) + + it('can poll access/claim to know when confirmation happened', async () => { + const abort = new AbortController() + after(() => abort.abort()) + const account = { + email: /** @type {const} */ ('example@dag.house'), + did: thisEmailDidMailto, + } + const { connection, emails } = await accessApiVariant.create() + const deviceA = await AccessAgent.create(undefined, { + connection, + }) + + const authorize = async () => { + // fire off request + await requestAuthorization(deviceA, account, [{ can: '*' }]) + const claimed = await expectNewClaimableDelegations( + deviceA, + deviceA.issuer.did(), + { abort: abort.signal } + ) + return claimed + } + const clickNextConfirmationLink = () => + watchForEmail(emails, 100, abort.signal).then((email) => { + return confirmConfirmationUrl(deviceA.connection, email) + }) + const [claimed] = await Promise.all([ + authorize(), + clickNextConfirmationLink(), + ]) + assert.equal(claimed.length, 2) + }) } /** diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 9c0496367..4eecae4dd 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -4,6 +4,7 @@ import * as Access from '@web3-storage/capabilities/access' import { bytesToDelegations } from './encoding.js' import { Provider } from '@web3-storage/capabilities' import { Delegation } from '@ucanto/core' +import * as w3caps from '@web3-storage/capabilities' /** * Request authorization of a session allowing this agent to issue UCANs @@ -108,3 +109,43 @@ export async function addProvider(access, space, account, provider) { throw new Error(`error adding provider`, { cause: result }) } } + +/** + * @param {AccessAgent} access + * @param {Ucanto.DID} delegee + * @param {object} [options] + * @param {number} [options.interval] + * @param {AbortSignal} [options.abort] + */ +export async function expectNewClaimableDelegations(access, delegee, options) { + const interval = options?.interval || 250 + const claim = () => claimDelegations(access, delegee) + const initialClaimResult = await claim() + const claimed = await new Promise((resolve, reject) => { + options?.abort?.addEventListener('abort', reject) + poll(interval) + /** + * @param {number} retryAfter + */ + async function poll(retryAfter) { + const pollClaimResult = await access.invokeAndExecute( + w3caps.Access.claim, + { with: delegee } + ) + if (pollClaimResult.error) { + return reject(pollClaimResult) + } + // got a response. If it contains same amount of delegations as initialClaimResult, + // user has not clicked confirm + const claimedDelegations = Object.values( + pollClaimResult.delegations + ).flatMap((d) => bytesToDelegations(d)) + if (claimedDelegations.length > initialClaimResult.length) { + resolve(claimedDelegations) + } else { + setTimeout(() => poll(retryAfter), retryAfter) + } + } + }) + return claimed +} From 0b2e268036fac2d8aaec37302851ee62adf92884 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Thu, 16 Mar 2023 20:16:16 -0700 Subject: [PATCH 41/54] fix: Agent#authorize races websocket and polling access/claim (#560) TIL: * the websocket definitely doesn't really work in access-api tests, because access-ws is an entirely different cf worker! it's not running in those access-api tests * so 100% of the time the websocket immediately terminates with ECONNREFUSED, but afaict that error isn't really bubbled up when Agent calls `await ws.open`. This still doesn't bubble up that error, but does allow for that open to be abortable * and then authorize() races that websocket and polling access claim. If access/claim polling wins, it aborts the websocket, and everything kinda maybe works End result * this re-enables a test against Agent#authorize, which passes due to the polling on access/claim * in the wild maybe the websocket will win the race sometimes (so it can be used in tests) --- .../test/access-client-agent.test.js | 10 ++- packages/access-client/src/agent-data.js | 2 +- packages/access-client/src/agent-use-cases.js | 10 ++- packages/access-client/src/agent.js | 75 ++++++++++++++++--- packages/access-client/src/utils/ws.js | 12 ++- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index 7fd9a9c3d..b3b24254b 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -380,7 +380,7 @@ for (const accessApiVariant of /** @type {const} */ ([ await addSpacesFromDelegations(accessAgent, []) }) - it.skip('multi device authorize method', async () => { + it('multi device authorize method', async () => { const abort = new AbortController() after(() => abort.abort()) const account = { @@ -388,6 +388,7 @@ for (const accessApiVariant of /** @type {const} */ ([ did: thisEmailDidMailto, } const { connection, emails } = await accessApiVariant.create() + const provider = /** @type {Ucanto.DID<'web'>} */ (connection.id.did()) const deviceA = await AccessAgent.create(undefined, { connection, }) @@ -397,9 +398,10 @@ for (const accessApiVariant of /** @type {const} */ ([ watchForEmail(emails, 100, abort.signal).then((email) => { return confirmConfirmationUrl(deviceA.connection, email) }) - // authorize() will hang because it tries to connect ws to localhost:8787 - // which gets ECONNREFUSED, but ../src/ws.js swallws error await Promise.all([authorize(), clickNextConfirmationLink()]) + + const space = await deviceA.createSpace() + await addProvider(deviceA, space.did, account, provider) }) it('can poll access/claim to know when confirmation happened', async () => { @@ -432,7 +434,7 @@ for (const accessApiVariant of /** @type {const} */ ([ authorize(), clickNextConfirmationLink(), ]) - assert.equal(claimed.length, 2) + assert.equal([...claimed].length, 2) }) } diff --git a/packages/access-client/src/agent-data.js b/packages/access-client/src/agent-data.js index 2259a70c2..23a726792 100644 --- a/packages/access-client/src/agent-data.js +++ b/packages/access-client/src/agent-data.js @@ -164,7 +164,7 @@ const isSessionCapability = (cap) => cap.can === Access.session.can * @param {Ucanto.Delegation} delegation * @returns {delegation is Ucanto.Delegation<[import('./types').AccessSession]>} */ -const isSessionProof = (delegation) => +export const isSessionProof = (delegation) => delegation.capabilities.some((cap) => isSessionCapability(cap)) /** diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 4eecae4dd..6ff703eaa 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -52,7 +52,10 @@ export async function claimDelegations( const delegations = Object.values(res.delegations).flatMap((bytes) => bytesToDelegations(bytes) ) - if (addProofs) for (const d of delegations) access.addProof(d) + if (addProofs) + for (const d of delegations) { + await access.addProof(d) + } if (addProofs) { await addSpacesFromDelegations(access, delegations) @@ -116,13 +119,16 @@ export async function addProvider(access, space, account, provider) { * @param {object} [options] * @param {number} [options.interval] * @param {AbortSignal} [options.abort] + * @returns {Promise>} */ export async function expectNewClaimableDelegations(access, delegee, options) { const interval = options?.interval || 250 const claim = () => claimDelegations(access, delegee) const initialClaimResult = await claim() const claimed = await new Promise((resolve, reject) => { - options?.abort?.addEventListener('abort', reject) + options?.abort?.addEventListener('abort', (e) => { + reject(new Error('expectNewClaimableDelegations aborted', { cause: e })) + }) poll(interval) /** * @param {number} retryAfter diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index be56941f8..3accce653 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -24,12 +24,13 @@ import { validate, canDelegateCapability, } from './delegations.js' -import { AgentData, getSessionProofs } from './agent-data.js' +import { AgentData, getSessionProofs, isSessionProof } from './agent-data.js' import { createDidMailtoFromEmail } from './utils/did-mailto.js' import { addProvider, claimDelegations, requestAuthorization, + expectNewClaimableDelegations, } from './agent-use-cases.js' export { AgentData, createDidMailtoFromEmail } @@ -205,7 +206,6 @@ export class Agent { */ async addProof(delegation) { validate(delegation, { - checkAudience: this.issuer, checkIsExpired: true, }) await this.#data.addDelegation(delegation, { audience: this.meta }) @@ -508,16 +508,66 @@ export class Agent { ] ) - const sessionDelegation = - /** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */ - (await this.#waitForDelegation(opts)) + const raceAbort = new AbortController() + opts?.signal?.addEventListener('abort', () => raceAbort.abort()) + + let winner + const sessionDelegationFromSocket = + /** @type {Promise<[Ucanto.Delegation<[import('./types').AccessSession]>]>} */ + ( + this.#waitForDelegation({ + ...opts, + signal: raceAbort.signal, + }).then((d) => { + raceAbort.abort() + return [d] + }) + ) - await this.addProof(sessionDelegation) + const sessionDelegationFromClaim = expectNewClaimableDelegations( + this, + this.issuer.did(), + { abort: raceAbort.signal } + ).then((claimed) => { + if (![...claimed].some((d) => isSessionProof(d))) { + throw new Error( + `claimed new delegations, but none were a session proof` + ) + } + return [...claimed] + }) + + const sessionDelegations = await Promise.race([ + sessionDelegationFromSocket.then((d) => { + winner = sessionDelegationFromSocket + return d + }), + sessionDelegationFromClaim.then((d) => { + winner = sessionDelegationFromClaim + return d + }), + ]).then((d) => { + raceAbort.abort() + return d + }) + await Promise.all(sessionDelegations.map(async (d) => this.addProof(d))) // claim delegations here because we will need an ucan/attest from the service to // pair with the session delegation we just claimed to make it work - await claimDelegations(this, this.issuer.did(), { addProofs: true }) - await claimDelegations(this, account.did(), { addProofs: true }) + if (winner !== sessionDelegationFromClaim) + await claimDelegations(this, this.issuer.did(), { addProofs: true }) + // see if we just claimed delegations that will allow us to claim as account + const claimAsAccountProofs = this.proofs([ + { can: 'access/claim', with: account.did() }, + ]) + if (claimAsAccountProofs.length > 0) { + await claimDelegations(this, account.did(), { addProofs: true }) + } else { + // eslint-disable-next-line no-console + console.warn( + `authorize() skipped claiming delegations as ${account.did()}` + ) + } } /** @@ -704,7 +754,7 @@ export class Agent { */ async #waitForDelegation(opts) { const ws = new Websocket(this.url, 'validate-ws') - await ws.open() + await ws.open(opts) ws.send({ did: this.did(), @@ -714,20 +764,21 @@ export class Agent { const msg = await ws.awaitMsg(opts) if (msg.type === 'timeout') { - await ws.close() + await ws.close(1000, 'agent got timeout waiting for validation') throw new Error('Email validation timed out.') } if (msg.type === 'delegation') { const delegation = stringToDelegation(msg.delegation) - ws.close() + await ws.close(1000, 'received delegation, agent is done with ws') return delegation } } catch (error) { if (error instanceof AbortError) { - await ws.close() + await ws.close(1000, 'AbortError: agent failed to get delegation') throw new TypeError('Failed to get delegation', { cause: error }) } + throw error } throw new TypeError('Failed to get delegation') } diff --git a/packages/access-client/src/utils/ws.js b/packages/access-client/src/utils/ws.js index be8bdbf2b..127e89382 100644 --- a/packages/access-client/src/utils/ws.js +++ b/packages/access-client/src/utils/ws.js @@ -44,9 +44,13 @@ export class Websocket { this.forceClose = false } - async open() { + /** + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + */ + async open(opts) { this.ws = this.connect() - await pWaitFor(() => this.ws?.readyState === 1) + await pWaitFor(() => opts?.signal?.aborted || this.ws?.readyState === 1) return this } @@ -59,6 +63,10 @@ export class Websocket { this.ws = ws ws.addEventListener('close', (event) => { + if (event.code === 1006) { + this.close(event.code, event.reason) + return + } if (!this.forceClose && !this.timeout) { this.timeout = setTimeout(() => { this.attemps++ From 00d125cb5b1a75f2d476d9dee0892fdaf4ec760d Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Thu, 16 Mar 2023 17:09:13 -0700 Subject: [PATCH 42/54] be sure to request authorizzation to space/* --- packages/access-client/src/agent.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 3accce653..ce1526e5c 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -502,6 +502,7 @@ export class Agent { this, account, opts?.capabilities || [ + { can: 'space/*' }, { can: 'store/*' }, { can: 'provider/add' }, { can: 'upload/*' }, From 5d5f57778174c58f42d544e37ab0d66e71bece41 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 17 Mar 2023 10:50:09 -0700 Subject: [PATCH 43/54] fix: access-client authorize and waitForDelegation use cases (#563) Motivation: * avoid adding new public method to Agent that can be a function that operates on the existing public api --- .../test/access-client-agent.test.js | 5 +- packages/access-client/src/agent-use-cases.js | 127 +++++++++++++++++- packages/access-client/src/agent.js | 123 +---------------- 3 files changed, 131 insertions(+), 124 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index b3b24254b..c20b28420 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -9,6 +9,7 @@ import { addProvider, addSpacesFromDelegations, Agent as AccessAgent, + authorize, claimDelegations, createDidMailtoFromEmail, expectNewClaimableDelegations, @@ -393,12 +394,12 @@ for (const accessApiVariant of /** @type {const} */ ([ connection, }) - const authorize = () => deviceA.authorize(account.email) + const doAuthorize = () => authorize(deviceA, account.email) const clickNextConfirmationLink = () => watchForEmail(emails, 100, abort.signal).then((email) => { return confirmConfirmationUrl(deviceA.connection, email) }) - await Promise.all([authorize(), clickNextConfirmationLink()]) + await Promise.all([doAuthorize(), clickNextConfirmationLink()]) const space = await deviceA.createSpace() await addProvider(deviceA, space.did, account, provider) diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 6ff703eaa..7f5aac5c6 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -1,10 +1,16 @@ -import { Agent as AccessAgent, agentToData } from './agent.js' +import { + Agent as AccessAgent, + agentToData, + createDidMailtoFromEmail, +} from './agent.js' import * as Ucanto from '@ucanto/interface' import * as Access from '@web3-storage/capabilities/access' -import { bytesToDelegations } from './encoding.js' +import { bytesToDelegations, stringToDelegation } from './encoding.js' import { Provider } from '@web3-storage/capabilities' import { Delegation } from '@ucanto/core' import * as w3caps from '@web3-storage/capabilities' +import { Websocket, AbortError } from './utils/ws.js' +import { isSessionProof } from './agent-data.js' /** * Request authorization of a session allowing this agent to issue UCANs @@ -155,3 +161,120 @@ export async function expectNewClaimableDelegations(access, delegee, options) { }) return claimed } + +/** + * @param {AccessAgent} access + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + */ +export async function waitForDelegation(access, opts) { + const ws = new Websocket(access.url, 'validate-ws') + await ws.open(opts) + + ws.send({ + did: access.did(), + }) + + try { + const msg = await ws.awaitMsg(opts) + + if (msg.type === 'timeout') { + await ws.close(1000, 'agent got timeout waiting for validation') + throw new Error('Email validation timed out.') + } + + if (msg.type === 'delegation') { + const delegation = stringToDelegation(msg.delegation) + await ws.close(1000, 'received delegation, agent is done with ws') + return delegation + } + } catch (error) { + if (error instanceof AbortError) { + await ws.close(1000, 'AbortError: agent failed to get delegation') + throw new TypeError('Failed to get delegation', { cause: error }) + } + throw error + } + throw new TypeError('Failed to get delegation') +} + +/** + * Request authorization of a session allowing this agent to issue UCANs + * signed by the passed email address. + * + * @param {AccessAgent} access + * @param {`${string}@${string}`} email + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] + */ +export async function authorize(access, email, opts) { + const account = { did: () => createDidMailtoFromEmail(email) } + await requestAuthorization( + access, + account, + opts?.capabilities || [ + { can: 'space/*' }, + { can: 'store/*' }, + { can: 'provider/add' }, + { can: 'upload/*' }, + ] + ) + + const raceAbort = new AbortController() + opts?.signal?.addEventListener('abort', () => raceAbort.abort()) + + let winner + const sessionDelegationFromSocket = + /** @type {Promise<[Ucanto.Delegation<[import('./types').AccessSession]>]>} */ + ( + waitForDelegation(access, { + ...opts, + signal: raceAbort.signal, + }).then((d) => { + raceAbort.abort() + return [d] + }) + ) + + const sessionDelegationFromClaim = expectNewClaimableDelegations( + access, + access.issuer.did(), + { abort: raceAbort.signal } + ).then((claimed) => { + if (![...claimed].some((d) => isSessionProof(d))) { + throw new Error(`claimed new delegations, but none were a session proof`) + } + return [...claimed] + }) + + const sessionDelegations = await Promise.race([ + sessionDelegationFromSocket.then((d) => { + winner = sessionDelegationFromSocket + return d + }), + sessionDelegationFromClaim.then((d) => { + winner = sessionDelegationFromClaim + return d + }), + ]).then((d) => { + raceAbort.abort() + return d + }) + await Promise.all(sessionDelegations.map(async (d) => access.addProof(d))) + + // claim delegations here because we will need an ucan/attest from the service to + // pair with the session delegation we just claimed to make it work + if (winner !== sessionDelegationFromClaim) + await claimDelegations(access, access.issuer.did(), { addProofs: true }) + // see if we just claimed delegations that will allow us to claim as account + const claimAsAccountProofs = access.proofs([ + { can: 'access/claim', with: account.did() }, + ]) + if (claimAsAccountProofs.length > 0) { + await claimDelegations(access, account.did(), { addProofs: true }) + } else { + // eslint-disable-next-line no-console + console.warn(`authorize() skipped claiming delegations as ${account.did()}`) + } +} diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index ce1526e5c..6869f3ef8 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -14,7 +14,6 @@ import * as Voucher from '@web3-storage/capabilities/voucher' import * as Access from '@web3-storage/capabilities/access' import { stringToDelegation } from './encoding.js' -import { Websocket, AbortError } from './utils/ws.js' import { Signer } from '@ucanto/principal/ed25519' import { Verifier } from '@ucanto/principal' import { invoke, delegate, DID } from '@ucanto/core' @@ -24,14 +23,9 @@ import { validate, canDelegateCapability, } from './delegations.js' -import { AgentData, getSessionProofs, isSessionProof } from './agent-data.js' +import { AgentData, getSessionProofs } from './agent-data.js' import { createDidMailtoFromEmail } from './utils/did-mailto.js' -import { - addProvider, - claimDelegations, - requestAuthorization, - expectNewClaimableDelegations, -} from './agent-use-cases.js' +import { addProvider, waitForDelegation } from './agent-use-cases.js' export { AgentData, createDidMailtoFromEmail } export * from './agent-use-cases.js' @@ -487,90 +481,6 @@ export class Agent { } } - /** - * Request authorization of a session allowing this agent to issue UCANs - * signed by the passed email address. - * - * @param {`${string}@${string}`} email - * @param {object} [opts] - * @param {AbortSignal} [opts.signal] - * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] - */ - async authorize(email, opts) { - const account = { did: () => createDidMailtoFromEmail(email) } - await requestAuthorization( - this, - account, - opts?.capabilities || [ - { can: 'space/*' }, - { can: 'store/*' }, - { can: 'provider/add' }, - { can: 'upload/*' }, - ] - ) - - const raceAbort = new AbortController() - opts?.signal?.addEventListener('abort', () => raceAbort.abort()) - - let winner - const sessionDelegationFromSocket = - /** @type {Promise<[Ucanto.Delegation<[import('./types').AccessSession]>]>} */ - ( - this.#waitForDelegation({ - ...opts, - signal: raceAbort.signal, - }).then((d) => { - raceAbort.abort() - return [d] - }) - ) - - const sessionDelegationFromClaim = expectNewClaimableDelegations( - this, - this.issuer.did(), - { abort: raceAbort.signal } - ).then((claimed) => { - if (![...claimed].some((d) => isSessionProof(d))) { - throw new Error( - `claimed new delegations, but none were a session proof` - ) - } - return [...claimed] - }) - - const sessionDelegations = await Promise.race([ - sessionDelegationFromSocket.then((d) => { - winner = sessionDelegationFromSocket - return d - }), - sessionDelegationFromClaim.then((d) => { - winner = sessionDelegationFromClaim - return d - }), - ]).then((d) => { - raceAbort.abort() - return d - }) - await Promise.all(sessionDelegations.map(async (d) => this.addProof(d))) - - // claim delegations here because we will need an ucan/attest from the service to - // pair with the session delegation we just claimed to make it work - if (winner !== sessionDelegationFromClaim) - await claimDelegations(this, this.issuer.did(), { addProofs: true }) - // see if we just claimed delegations that will allow us to claim as account - const claimAsAccountProofs = this.proofs([ - { can: 'access/claim', with: account.did() }, - ]) - if (claimAsAccountProofs.length > 0) { - await claimDelegations(this, account.did(), { addProofs: true }) - } else { - // eslint-disable-next-line no-console - console.warn( - `authorize() skipped claiming delegations as ${account.did()}` - ) - } - } - /** * @param {Ucanto.DID<'key'>} space * @param {Ucanto.Principal>} account @@ -754,34 +664,7 @@ export class Agent { * @param {AbortSignal} [opts.signal] */ async #waitForDelegation(opts) { - const ws = new Websocket(this.url, 'validate-ws') - await ws.open(opts) - - ws.send({ - did: this.did(), - }) - - try { - const msg = await ws.awaitMsg(opts) - - if (msg.type === 'timeout') { - await ws.close(1000, 'agent got timeout waiting for validation') - throw new Error('Email validation timed out.') - } - - if (msg.type === 'delegation') { - const delegation = stringToDelegation(msg.delegation) - await ws.close(1000, 'received delegation, agent is done with ws') - return delegation - } - } catch (error) { - if (error instanceof AbortError) { - await ws.close(1000, 'AbortError: agent failed to get delegation') - throw new TypeError('Failed to get delegation', { cause: error }) - } - throw error - } - throw new TypeError('Failed to get delegation') + return waitForDelegation(this, opts) } /** From 8ebf0b98fcde8967f3c38a971cccf07a5e9024a0 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 17 Mar 2023 10:51:56 -0700 Subject: [PATCH 44/54] remove unnecessary second claim in authorize --- packages/access-client/src/agent-use-cases.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 7f5aac5c6..bd6ed88af 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -267,14 +267,4 @@ export async function authorize(access, email, opts) { // pair with the session delegation we just claimed to make it work if (winner !== sessionDelegationFromClaim) await claimDelegations(access, access.issuer.did(), { addProofs: true }) - // see if we just claimed delegations that will allow us to claim as account - const claimAsAccountProofs = access.proofs([ - { can: 'access/claim', with: account.did() }, - ]) - if (claimAsAccountProofs.length > 0) { - await claimDelegations(access, account.did(), { addProofs: true }) - } else { - // eslint-disable-next-line no-console - console.warn(`authorize() skipped claiming delegations as ${account.did()}`) - } } From c0e1bb5da3146cd658481a97b5b9fdea03037c82 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:33:05 -0700 Subject: [PATCH 45/54] feat: get rid of Promise.race in authorize. (#565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose authorizeWithSocket and ……authorizeWithPollClain and authorizeAndWait --- .../test/access-client-agent.test.js | 48 ++++++++- packages/access-client/src/agent-use-cases.js | 100 +++++++++++------- packages/access-client/src/agent.js | 4 +- 3 files changed, 108 insertions(+), 44 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index c20b28420..fc6e232d6 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -9,7 +9,8 @@ import { addProvider, addSpacesFromDelegations, Agent as AccessAgent, - authorize, + authorizeAndWait, + authorizeWithPollClaim, claimDelegations, createDidMailtoFromEmail, expectNewClaimableDelegations, @@ -381,7 +382,7 @@ for (const accessApiVariant of /** @type {const} */ ([ await addSpacesFromDelegations(accessAgent, []) }) - it('multi device authorize method', async () => { + it('authorizeAndWait', async () => { const abort = new AbortController() after(() => abort.abort()) const account = { @@ -393,16 +394,55 @@ for (const accessApiVariant of /** @type {const} */ ([ const deviceA = await AccessAgent.create(undefined, { connection, }) + const expectAuthorization = () => + expectNewClaimableDelegations(deviceA, deviceA.issuer.did(), { + abort: abort.signal, + }) + const authorize = () => + authorizeAndWait(deviceA, account.email, { + signal: abort.signal, + expectAuthorization, + }) + const clickNextConfirmationLink = () => + watchForEmail(emails, 100, abort.signal).then((email) => { + return confirmConfirmationUrl(deviceA.connection, email) + }) + await Promise.all([authorize(), clickNextConfirmationLink()]) - const doAuthorize = () => authorize(deviceA, account.email) + const space = await deviceA.createSpace() + await addProvider(deviceA, space.did, account, provider) + const spaceInfoResult = await deviceA.invokeAndExecute(w3caps.Space.info, { + with: space.did, + }) + assertNotError(spaceInfoResult) + }) + + it('authorizeWithPollClaim', async () => { + const abort = new AbortController() + after(() => abort.abort()) + const account = { + email: /** @type {const} */ ('example@dag.house'), + did: thisEmailDidMailto, + } + const { connection, emails } = await accessApiVariant.create() + const provider = /** @type {Ucanto.DID<'web'>} */ (connection.id.did()) + const deviceA = await AccessAgent.create(undefined, { + connection, + }) + const authorize = () => + authorizeWithPollClaim(deviceA, account.email, { signal: abort.signal }) const clickNextConfirmationLink = () => watchForEmail(emails, 100, abort.signal).then((email) => { return confirmConfirmationUrl(deviceA.connection, email) }) - await Promise.all([doAuthorize(), clickNextConfirmationLink()]) + await Promise.all([authorize(), clickNextConfirmationLink()]) const space = await deviceA.createSpace() await addProvider(deviceA, space.did, account, provider) + const spaceInfoResult = await deviceA.invokeAndExecute(w3caps.Space.info, { + with: space.did, + }) + assertNotError(spaceInfoResult) }) it('can poll access/claim to know when confirmation happened', async () => { diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index bd6ed88af..77f79a19a 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -167,7 +167,7 @@ export async function expectNewClaimableDelegations(access, delegee, options) { * @param {object} [opts] * @param {AbortSignal} [opts.signal] */ -export async function waitForDelegation(access, opts) { +export async function waitForDelegationOnSocket(access, opts) { const ws = new Websocket(access.url, 'validate-ws') await ws.open(opts) @@ -206,9 +206,18 @@ export async function waitForDelegation(access, opts) { * @param {`${string}@${string}`} email * @param {object} [opts] * @param {AbortSignal} [opts.signal] + * @param {boolean} [opts.dontAddProofs] - whether to skip adding proofs to the agent * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] + * @param {() => Promise>} [opts.expectAuthorization] - function that will resolve once account has confirmed the authorization request */ -export async function authorize(access, email, opts) { +export async function authorizeAndWait(access, email, opts) { + const expectAuthorization = + opts?.expectAuthorization || + function () { + return expectNewClaimableDelegations(access, access.issuer.did(), { + abort: opts?.signal, + }) + } const account = { did: () => createDidMailtoFromEmail(email) } await requestAuthorization( access, @@ -220,51 +229,66 @@ export async function authorize(access, email, opts) { { can: 'upload/*' }, ] ) + const sessionDelegations = [...(await expectAuthorization())] + if (!opts?.dontAddProofs) { + await Promise.all(sessionDelegations.map(async (d) => access.addProof(d))) + } +} - const raceAbort = new AbortController() - opts?.signal?.addEventListener('abort', () => raceAbort.abort()) - - let winner - const sessionDelegationFromSocket = +/** + * Request authorization of a session allowing this agent to issue UCANs + * signed by the passed email address. + * + * @param {AccessAgent} access + * @param {`${string}@${string}`} email + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] + */ +export async function authorizeWithSocket(access, email, opts) { + const expectAuthorization = () => /** @type {Promise<[Ucanto.Delegation<[import('./types').AccessSession]>]>} */ ( - waitForDelegation(access, { + waitForDelegationOnSocket(access, { ...opts, - signal: raceAbort.signal, + signal: opts?.signal, }).then((d) => { - raceAbort.abort() return [d] }) ) - - const sessionDelegationFromClaim = expectNewClaimableDelegations( - access, - access.issuer.did(), - { abort: raceAbort.signal } - ).then((claimed) => { - if (![...claimed].some((d) => isSessionProof(d))) { - throw new Error(`claimed new delegations, but none were a session proof`) - } - return [...claimed] + await authorizeAndWait(access, email, { + ...opts, + expectAuthorization, }) - - const sessionDelegations = await Promise.race([ - sessionDelegationFromSocket.then((d) => { - winner = sessionDelegationFromSocket - return d - }), - sessionDelegationFromClaim.then((d) => { - winner = sessionDelegationFromClaim - return d - }), - ]).then((d) => { - raceAbort.abort() - return d - }) - await Promise.all(sessionDelegations.map(async (d) => access.addProof(d))) - // claim delegations here because we will need an ucan/attest from the service to // pair with the session delegation we just claimed to make it work - if (winner !== sessionDelegationFromClaim) - await claimDelegations(access, access.issuer.did(), { addProofs: true }) + await claimDelegations(access, access.issuer.did(), { addProofs: true }) +} + +/** + * Request authorization of a session allowing this agent to issue UCANs + * signed by the passed email address. + * + * @param {AccessAgent} access + * @param {`${string}@${string}`} email + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + * @param {Iterable<{ can: Ucanto.Ability }>} [opts.capabilities] + */ +export async function authorizeWithPollClaim(access, email, opts) { + const expectAuthorization = () => + expectNewClaimableDelegations(access, access.issuer.did(), { + abort: opts?.signal, + }).then((claimed) => { + if (![...claimed].some((d) => isSessionProof(d))) { + throw new Error( + `claimed new delegations, but none were a session proof` + ) + } + return [...claimed] + }) + await authorizeAndWait(access, email, { + ...opts, + expectAuthorization, + }) } diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 6869f3ef8..fde8f1875 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -25,7 +25,7 @@ import { } from './delegations.js' import { AgentData, getSessionProofs } from './agent-data.js' import { createDidMailtoFromEmail } from './utils/did-mailto.js' -import { addProvider, waitForDelegation } from './agent-use-cases.js' +import { addProvider, waitForDelegationOnSocket } from './agent-use-cases.js' export { AgentData, createDidMailtoFromEmail } export * from './agent-use-cases.js' @@ -664,7 +664,7 @@ export class Agent { * @param {AbortSignal} [opts.signal] */ async #waitForDelegation(opts) { - return waitForDelegation(this, opts) + return waitForDelegationOnSocket(this, opts) } /** From 0d8dd301a46ef8afe7ccbf38f6600eafbc6bf23e Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:43:21 -0700 Subject: [PATCH 46/54] remove unnecessary proofs --- packages/access-client/src/agent.js | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index fde8f1875..b8d48fab3 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -64,28 +64,6 @@ async function createIssuerSaysAccountCanAdminSpace( }) } -/** - * @param {Ucanto.Signer>} issuer - * @param {Ucanto.DID} space - * @param {Ucanto.Principal>} device - */ -async function createIssuerSaysDeviceCanAccessDelegateWithSpace( - issuer, - space, - device -) { - return ucanto.delegate({ - issuer, - audience: device, - capabilities: [ - { - can: 'access/delegate', - with: space, - }, - ], - }) -} - /** * @typedef {import('./types').Service} Service */ @@ -515,11 +493,6 @@ export class Agent { }, }, proofs: [ - await createIssuerSaysDeviceCanAccessDelegateWithSpace( - this.issuer, - space, - this.issuer - ), // must be embedded here because it's referenced by cid in .nb.delegations issuerSaysAccountCanAdminSpace, ], From 4e7467490901758a055d2c3ace42ec9a3d9093d7 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Fri, 17 Mar 2023 12:01:17 -0700 Subject: [PATCH 47/54] fix: don't let space delegation proofs expire (#564) otherwise the account will eventually lose access to the space --- packages/access-client/src/agent.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index b8d48fab3..8e16d0ad8 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -42,6 +42,7 @@ export const agentToData = new WeakMap() * @param {Ucanto.Principal>} account * @param {Ucanto.Capabilities} capabilities * @param {Ucanto.Delegation[]} proofs + * @param {number} expiration * @returns */ async function createIssuerSaysAccountCanAdminSpace( @@ -54,13 +55,15 @@ async function createIssuerSaysAccountCanAdminSpace( with: space, }, ], - proofs = [] + proofs = [], + expiration ) { return ucanto.delegate({ issuer, audience: account, capabilities, proofs, + expiration, }) } @@ -480,7 +483,9 @@ export class Agent { space, account, undefined, - this.proofs([{ with: space, can: '*' }]) + this.proofs([{ with: space, can: '*' }]), + // we want to sign over control of this space forever + Infinity ) return this.invokeAndExecute(Access.delegate, { audience: this.connection.id, From 3af8816107f3a9f5c7135f5191665ef18950b8c9 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:46:04 -0700 Subject: [PATCH 48/54] Update packages/access-client/src/agent-use-cases.js Co-authored-by: Irakli Gozalishvili --- packages/access-client/src/agent-use-cases.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 77f79a19a..7becc73f8 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -58,12 +58,11 @@ export async function claimDelegations( const delegations = Object.values(res.delegations).flatMap((bytes) => bytesToDelegations(bytes) ) - if (addProofs) + if (addProofs) { for (const d of delegations) { await access.addProof(d) } - if (addProofs) { await addSpacesFromDelegations(access, delegations) } From 2658b44b5574d09447f29277e664858e16d9c7c2 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Fri, 17 Mar 2023 13:20:43 -0700 Subject: [PATCH 49/54] fix: add checkAudience back to addProof we had a theory this was the problem with uploads (ie, we weren't saving enough proofs) but I just confirmed that's not the case --- packages/access-client/src/agent.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 8e16d0ad8..88de51656 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -181,6 +181,7 @@ export class Agent { */ async addProof(delegation) { validate(delegation, { + checkAudience: this.issuer, checkIsExpired: true, }) await this.#data.addDelegation(delegation, { audience: this.meta }) From 9ca0df7c02991680b5d1ab0fc1962b3f93f6225f Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Fri, 17 Mar 2023 13:55:04 -0700 Subject: [PATCH 50/54] Revert "fix: add checkAudience back to addProof" This reverts commit 2658b44b5574d09447f29277e664858e16d9c7c2. --- packages/access-client/src/agent.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 88de51656..8e16d0ad8 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -181,7 +181,6 @@ export class Agent { */ async addProof(delegation) { validate(delegation, { - checkAudience: this.issuer, checkIsExpired: true, }) await this.#data.addDelegation(delegation, { audience: this.meta }) From 571713d5fbbfa228b1c65de1f53d7eb6e4e318f1 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Fri, 17 Mar 2023 14:03:07 -0700 Subject: [PATCH 51/54] simpler change to proofs logic, thx @gozala --- packages/access-client/src/agent.js | 26 +++++++++++------------ packages/access-client/test/agent.test.js | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 8e16d0ad8..62e7e28b7 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -773,21 +773,19 @@ export class Agent { ) } - const extraProofs = options.proofs || [] - const proofs = this.proofs([ - { - with: space, - can: cap.can, - }, - ]) + const proofs = [ + ...(options.proofs || []), + ...this.proofs([ + { + with: space, + can: cap.can, + }, + ]), + ] - if ( - proofs.length === 0 && - options.with !== this.did() && - extraProofs.length === 0 - ) { + if (proofs.length === 0 && options.with !== this.did()) { throw new Error( - `no proofs available for resource ${space} and ability ${cap.can} and no extra proofs were provided` + `no proofs available for resource ${space} and ability ${cap.can}` ) } const inv = invoke({ @@ -799,7 +797,7 @@ export class Agent { nb: options.nb, }), issuer: this.issuer, - proofs: [...proofs, ...extraProofs], + proofs: [...proofs], }) return /** @type {Ucanto.IssuedInvocationView>} */ ( diff --git a/packages/access-client/test/agent.test.js b/packages/access-client/test/agent.test.js index 9e8c3dbdc..a447d5c9a 100644 --- a/packages/access-client/test/agent.test.js +++ b/packages/access-client/test/agent.test.js @@ -129,7 +129,7 @@ describe('Agent', function () { name: 'Error', message: `no proofs available for resource ${URI.from( fixtures.alice.did() - )} and ability space/info and no extra proofs were provided`, + )} and ability space/info`, } ) }) From d1ea1b003b3a743dfabfad0c19d618e82b99c8ae Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 17 Mar 2023 14:45:32 -0700 Subject: [PATCH 52/54] chore: 433 bengo review comments (#567) Motivation: * review comments on https://github.com/web3-storage/w3protocol/pull/433 --- .../access-api/src/routes/validate-email.js | 15 -- .../src/service/access-authorize.js | 6 +- .../access-api/src/service/access-confirm.js | 38 ++-- .../test/access-client-agent.test.js | 58 +++--- packages/access-client/src/agent-use-cases.js | 192 +++++++++++++----- packages/access-client/src/agent.js | 176 +++++----------- packages/access-client/src/utils/ws.js | 4 - 7 files changed, 247 insertions(+), 242 deletions(-) diff --git a/packages/access-api/src/routes/validate-email.js b/packages/access-api/src/routes/validate-email.js index 44a523858..f4bc8c234 100644 --- a/packages/access-api/src/routes/validate-email.js +++ b/packages/access-api/src/routes/validate-email.js @@ -11,7 +11,6 @@ import { ValidateEmailError, PendingValidateEmail, } from '../utils/html.js' -import * as validator from '@ucanto/validator' import { Verifier } from '@ucanto/principal' import * as delegationsResponse from '../utils/delegations-response.js' import * as accessConfirm from '../service/access-confirm.js' @@ -144,18 +143,6 @@ async function authorize(req, env) { */ const request = stringToDelegation(req.query.ucan) - const confirmation = await validator.access(request, { - capability: Access.confirm, - principal: Verifier, - authority: env.signer, - }) - - if (confirmation.error) { - throw new Error(`unable to validate access session: ${confirmation}`, { - cause: confirmation, - }) - } - const confirm = provide( Access.confirm, async ({ capability, invocation }) => { @@ -193,8 +180,6 @@ async function authorize(req, env) { ) ) } catch (error) { - // eslint-disable-next-line no-console - console.warn('error in validate-email', error) const err = /** @type {Error} */ (error) env.log.error(err) return new HtmlResponse( diff --git a/packages/access-api/src/service/access-authorize.js b/packages/access-api/src/service/access-authorize.js index 454219404..4732be9b8 100644 --- a/packages/access-api/src/service/access-authorize.js +++ b/packages/access-api/src/service/access-authorize.js @@ -24,6 +24,9 @@ export function accessAuthorizeProvider(ctx) { const confirmation = await Access.confirm .invoke({ issuer: ctx.signer, + // audience same as issuer because this is a service invocation + // that will get handled by access/confirm handler + // but only if the receiver of this email wants it to be audience: ctx.signer, // Because with is set to our DID no other actor will be able to issue // this delegation without our private key. @@ -34,9 +37,8 @@ export function accessAuthorizeProvider(ctx) { nb: { // we copy request details and set the `aud` field to the agent DID // that requested the authorization. - iss: capability.nb.iss, + ...capability.nb, aud: capability.with, - att: capability.nb.att, }, }) .delegate() diff --git a/packages/access-api/src/service/access-confirm.js b/packages/access-api/src/service/access-confirm.js index 9b04088b1..977662db6 100644 --- a/packages/access-api/src/service/access-confirm.js +++ b/packages/access-api/src/service/access-confirm.js @@ -1,11 +1,10 @@ import * as Ucanto from '@ucanto/interface' import * as ucanto from '@ucanto/core' -import { Absentee } from '@ucanto/principal' +import { Absentee, Verifier } from '@ucanto/principal' import { collect } from 'streaming-iterables' import * as Access from '@web3-storage/capabilities/access' import { delegationsToString } from '@web3-storage/access/encoding' import * as delegationsResponse from '../utils/delegations-response.js' -import * as validator from '@ucanto/validator' /** * @typedef {import('@web3-storage/capabilities/types').AccessConfirmSuccess} AccessConfirmSuccess @@ -19,9 +18,7 @@ export function parse(invocation) { const capability = invocation.capabilities[0] // Create a absentee signer for the account that authorized the delegation const account = Absentee.from({ id: capability.nb.iss }) - const agent = { - did: () => validator.DID.match({ method: 'key' }).from(capability.nb.aud), - } + const agent = Verifier.parse(capability.nb.aud) return { account, agent, @@ -53,8 +50,8 @@ export async function handleAccessConfirm(invocation, ctx) { })) ) - const [delegation, attestation] = await createSessionProofs( - ctx.signer, + const [delegation, attestation] = await createSessionProofs({ + service: ctx.signer, account, agent, capabilities, @@ -63,11 +60,11 @@ export async function handleAccessConfirm(invocation, ctx) { // We should actually filter out only delegations that support delegated // capabilities, but for now we just include all of them since we only // implement sudo access anyway. - ctx.models.delegations.find({ + delegationProofs: ctx.models.delegations.find({ audience: account.did(), }), - Infinity - ) + expiration: Infinity, + }) // Store the delegations so that they can be pulled with access/claim // The fact that we're storing proofs chains that we pulled from the @@ -84,22 +81,25 @@ export async function handleAccessConfirm(invocation, ctx) { } /** - * @param {Ucanto.Signer} service - * @param {Ucanto.Principal>} account - * @param {Ucanto.Principal>} agent - * @param {Ucanto.Capabilities} capabilities - * @param {AsyncIterable} delegationProofs - * @param {number} expiration + * @param {object} opts + * @param {Ucanto.Signer} opts.service + * @param {Ucanto.Principal>} opts.account + * @param {Ucanto.Principal} opts.agent + * @param {Ucanto.Capabilities} opts.capabilities + * @param {AsyncIterable} opts.delegationProofs + * @param {number} opts.expiration * @returns {Promise<[delegation: Ucanto.Delegation, attestation: Ucanto.Delegation]>} */ -export async function createSessionProofs( +export async function createSessionProofs({ service, account, agent, capabilities, delegationProofs, - expiration -) { + // default to Infinity is reasonable here because + // account consented to this. + expiration = Infinity, +}) { // create an delegation on behalf of the account with an absent signature. const delegation = await ucanto.delegate({ issuer: Absentee.from({ id: account.did() }), diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index fc6e232d6..ba6b11df1 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -11,10 +11,10 @@ import { Agent as AccessAgent, authorizeAndWait, authorizeWithPollClaim, - claimDelegations, + claimAccess, createDidMailtoFromEmail, expectNewClaimableDelegations, - requestAuthorization, + requestAccess, } from '@web3-storage/access/agent' import * as w3caps from '@web3-storage/capabilities' import * as assert from 'assert' @@ -81,7 +81,7 @@ for (const accessApiVariant of /** @type {const} */ ([ const account = { did: () => createDidMailtoFromEmail('example@dag.house'), } - await requestAuthorization(accessAgent, account, [{ can: '*' }]) + await requestAccess(accessAgent, account, [{ can: '*' }]) assert.deepEqual(emails.length, emailCount + 1) }) @@ -105,7 +105,7 @@ for (const accessApiVariant of /** @type {const} */ ([ /** @type {Ucanto.Principal>} */ const account = { did: () => 'did:mailto:dag.house:example' } const accessAgent = await AccessAgent.create(undefined, { connection }) - await requestAuthorization(accessAgent, account, [{ can: '*' }]) + await requestAccess(accessAgent, account, [{ can: '*' }]) }) it('can authorize session with account and use', async () => { @@ -118,7 +118,7 @@ for (const accessApiVariant of /** @type {const} */ ([ // request that account authorizes accessAgent // this should result in sending a confirmation email - const requestAllAbilities = requestAuthorization(accessAgent, account, [ + const requestAllAbilities = requestAccess(accessAgent, account, [ { can: '*' }, ]) @@ -146,7 +146,7 @@ for (const accessApiVariant of /** @type {const} */ ([ ) // these are delegations with audience=accessAgent.issuer - const claimedAsAgent = await claimDelegations( + const claimedAsAgent = await claimAccess( accessAgent, accessAgent.issuer.did(), { addProofs: true } @@ -192,12 +192,12 @@ for (const accessApiVariant of /** @type {const} */ ([ after(() => abort.abort()) // request agent authorization from account - requestAuthorization(accessAgent, account, [{ can: '*' }]) + requestAccess(accessAgent, account, [{ can: '*' }]) // confirm authorization const confirmationEmail = await watchForEmail(emails, 100, abort.signal) await confirmConfirmationUrl(accessAgent.connection, confirmationEmail) // claim delegations after confirmation - await claimDelegations(accessAgent, accessAgent.issuer.did(), { + await claimAccess(accessAgent, accessAgent.issuer.did(), { addProofs: true, }) @@ -238,12 +238,12 @@ for (const accessApiVariant of /** @type {const} */ ([ let expectedDataDelegations = 0 for (const account of accounts) { // request agent authorization from account - await requestAuthorization(accessAgent, account, [{ can: '*' }]) + await requestAccess(accessAgent, account, [{ can: '*' }]) // confirm authorization const confirmationEmail = await watchForEmail(emails, 100, abort.signal) await confirmConfirmationUrl(accessAgent.connection, confirmationEmail) // claim delegations after confirmation - await claimDelegations(accessAgent, accessAgent.issuer.did(), { + await claimAccess(accessAgent, accessAgent.issuer.did(), { addProofs: true, }) // expect two new delegations, [delegationFromAccount, attestationFromService] @@ -272,7 +272,12 @@ for (const accessApiVariant of /** @type {const} */ ([ accessAgent.connection.id.did() ) for (const account of accounts) { - await addProvider(accessAgent, spaceCreation.did, account, provider) + await addProvider({ + access: accessAgent, + space: spaceCreation.did, + account, + provider, + }) } }) @@ -293,12 +298,12 @@ for (const accessApiVariant of /** @type {const} */ ([ }) // deviceA authorization - await requestAuthorization(deviceA, account, [{ can: '*' }]) + await requestAccess(deviceA, account, [{ can: '*' }]) await confirmConfirmationUrl( deviceA.connection, await watchForEmail(emails, 100, abort.signal) ) - await claimDelegations(deviceA, deviceA.issuer.did(), { + await claimAccess(deviceA, deviceA.issuer.did(), { addProofs: true, }) @@ -321,13 +326,13 @@ for (const accessApiVariant of /** @type {const} */ ([ connection, }) // authorize deviceB - await requestAuthorization(deviceB, account, [{ can: '*' }]) + await requestAccess(deviceB, account, [{ can: '*' }]) await confirmConfirmationUrl( deviceB.connection, await watchForEmail(emails, 100, abort.signal) ) // claim delegations aud=deviceB.issuer - const deviceBIssuerClaimed = await claimDelegations( + const deviceBIssuerClaimed = await claimAccess( deviceB, deviceB.issuer.did(), { @@ -340,13 +345,9 @@ for (const accessApiVariant of /** @type {const} */ ([ 'deviceBIssuerClaimed delegations' ) // claim delegations aud=account - const deviceBAccountClaimed = await claimDelegations( - deviceB, - account.did(), - { - addProofs: true, - } - ) + const deviceBAccountClaimed = await claimAccess(deviceB, account.did(), { + addProofs: true, + }) assert.equal( deviceBAccountClaimed.length, 1, @@ -354,7 +355,12 @@ for (const accessApiVariant of /** @type {const} */ ([ ) // try to addProvider - await addProvider(deviceB, spaceCreation.did, account, provider) + await addProvider({ + access: deviceB, + space: spaceCreation.did, + account, + provider, + }) // issuer + account proofs should authorize deviceB to invoke space/info const spaceInfoResult = await deviceB.invokeAndExecute( @@ -410,7 +416,7 @@ for (const accessApiVariant of /** @type {const} */ ([ await Promise.all([authorize(), clickNextConfirmationLink()]) const space = await deviceA.createSpace() - await addProvider(deviceA, space.did, account, provider) + await addProvider({ access: deviceA, space: space.did, account, provider }) const spaceInfoResult = await deviceA.invokeAndExecute(w3caps.Space.info, { with: space.did, }) @@ -438,7 +444,7 @@ for (const accessApiVariant of /** @type {const} */ ([ await Promise.all([authorize(), clickNextConfirmationLink()]) const space = await deviceA.createSpace() - await addProvider(deviceA, space.did, account, provider) + await addProvider({ access: deviceA, space: space.did, account, provider }) const spaceInfoResult = await deviceA.invokeAndExecute(w3caps.Space.info, { with: space.did, }) @@ -459,7 +465,7 @@ for (const accessApiVariant of /** @type {const} */ ([ const authorize = async () => { // fire off request - await requestAuthorization(deviceA, account, [{ can: '*' }]) + await requestAccess(deviceA, account, [{ can: '*' }]) const claimed = await expectNewClaimableDelegations( deviceA, deviceA.issuer.did(), diff --git a/packages/access-client/src/agent-use-cases.js b/packages/access-client/src/agent-use-cases.js index 7becc73f8..132876460 100644 --- a/packages/access-client/src/agent-use-cases.js +++ b/packages/access-client/src/agent-use-cases.js @@ -1,26 +1,27 @@ import { + addSpacesFromDelegations, Agent as AccessAgent, - agentToData, createDidMailtoFromEmail, } from './agent.js' import * as Ucanto from '@ucanto/interface' import * as Access from '@web3-storage/capabilities/access' import { bytesToDelegations, stringToDelegation } from './encoding.js' import { Provider } from '@web3-storage/capabilities' -import { Delegation } from '@ucanto/core' import * as w3caps from '@web3-storage/capabilities' import { Websocket, AbortError } from './utils/ws.js' -import { isSessionProof } from './agent-data.js' +import { AgentData, isSessionProof } from './agent-data.js' +import * as ucanto from '@ucanto/core' +import { DID as DIDValidator } from '@ucanto/validator' /** - * Request authorization of a session allowing this agent to issue UCANs - * signed by the passed email address. + * Request access by a session allowing this agent to issue UCANs + * signed by the account. * * @param {AccessAgent} access * @param {Ucanto.Principal>} account * @param {Iterable<{ can: Ucanto.Ability }>} capabilities */ -export async function requestAuthorization(access, account, capabilities) { +export async function requestAccess(access, account, capabilities) { const res = await access.invokeAndExecute(Access.authorize, { audience: access.connection.id, with: access.issuer.did(), @@ -30,7 +31,7 @@ export async function requestAuthorization(access, account, capabilities) { }, }) if (res?.error) { - throw new Error('failed to authorize session', { cause: res }) + throw res } } @@ -38,22 +39,22 @@ export async function requestAuthorization(access, account, capabilities) { * claim delegations delegated to an audience * * @param {AccessAgent} access - * @param {Ucanto.DID} [delegee] - audience of claimed delegations. defaults to access.connection.id.did() + * @param {Ucanto.DID} [audienceOfClaimedDelegations] - audience of claimed delegations. defaults to access.connection.id.did() * @param {object} options * @param {boolean} [options.addProofs] - whether to addProof to access agent * @returns */ -export async function claimDelegations( +export async function claimAccess( access, - delegee = access.connection.id.did(), + audienceOfClaimedDelegations = access.connection.id.did(), { addProofs = false } = {} ) { const res = await access.invokeAndExecute(Access.claim, { audience: access.connection.id, - with: delegee, + with: audienceOfClaimedDelegations, }) if (res.error) { - throw new Error('error claiming delegations') + throw res } const delegations = Object.values(res.delegations).flatMap((bytes) => bytesToDelegations(bytes) @@ -70,41 +71,13 @@ export async function claimDelegations( } /** - * @private - * Given a list of delegations, add to agent data spaces list. - * - * TODO: DON'T USE - we'd like to move away from storing space information inside the agent, planning on removing this soon! - * - * @param {AccessAgent} access - * @param {Ucanto.Delegation[]} delegations - */ -export async function addSpacesFromDelegations(access, delegations) { - const data = agentToData.get(access) - if (!data) { - throw Object.assign(new Error(`cannot determine AgentData for Agent`), { - agent: access, - }) - } - if (delegations.length > 0) { - const allows = Delegation.allows(delegations[0], ...delegations.slice(1)) - for (const [did, value] of Object.entries(allows)) { - // TODO I don't think this should be `store/*` but this works for today - if (value['store/*']) { - data.addSpace(/** @type {Ucanto.DID} */ (did), { - isRegistered: true, - }) - } - } - } -} - -/** - * @param {AccessAgent} access - * @param {Ucanto.DID<'key'>} space - * @param {Ucanto.Principal>} account - * @param {Ucanto.DID<'web'>} provider - e.g. 'did:web:staging.web3.storage' + * @param {object} opts + * @param {AccessAgent} opts.access + * @param {Ucanto.DID<'key'>} opts.space + * @param {Ucanto.Principal>} opts.account + * @param {Ucanto.DID<'web'>} opts.provider - e.g. 'did:web:staging.web3.storage' */ -export async function addProvider(access, space, account, provider) { +export async function addProvider({ access, space, account, provider }) { const result = await access.invokeAndExecute(Provider.add, { audience: access.connection.id, with: account.did(), @@ -114,7 +87,7 @@ export async function addProvider(access, space, account, provider) { }, }) if (result.error) { - throw new Error(`error adding provider`, { cause: result }) + throw result } } @@ -128,7 +101,7 @@ export async function addProvider(access, space, account, provider) { */ export async function expectNewClaimableDelegations(access, delegee, options) { const interval = options?.interval || 250 - const claim = () => claimDelegations(access, delegee) + const claim = () => claimAccess(access, delegee) const initialClaimResult = await claim() const claimed = await new Promise((resolve, reject) => { options?.abort?.addEventListener('abort', (e) => { @@ -218,7 +191,7 @@ export async function authorizeAndWait(access, email, opts) { }) } const account = { did: () => createDidMailtoFromEmail(email) } - await requestAuthorization( + await requestAccess( access, account, opts?.capabilities || [ @@ -261,7 +234,7 @@ export async function authorizeWithSocket(access, email, opts) { }) // claim delegations here because we will need an ucan/attest from the service to // pair with the session delegation we just claimed to make it work - await claimDelegations(access, access.issuer.did(), { addProofs: true }) + await claimAccess(access, access.issuer.did(), { addProofs: true }) } /** @@ -291,3 +264,122 @@ export async function authorizeWithPollClaim(access, email, opts) { expectAuthorization, }) } + +/** + * Invokes voucher/redeem for the free tier, wait on the websocket for the voucher/claim and invokes it + * + * It also adds a full space delegation to the service in the voucher/claim invocation to allow for recovery + * + * @param {AccessAgent} access + * @param {AgentData} agentData + * @param {string} email + * @param {object} [opts] + * @param {AbortSignal} [opts.signal] + * @param {Ucanto.DID<'key'>} [opts.space] + * @param {Ucanto.DID<'web'>} [opts.provider] - provider to register - defaults to this.connection.id + */ +export async function addProviderAndDelegateToAccount( + access, + agentData, + email, + opts +) { + const space = opts?.space || access.currentSpace() + const spaceMeta = space ? agentData.spaces.get(space) : undefined + const provider = + opts?.provider || + (() => { + const service = access.connection.id.did() + if (DIDValidator.match({ method: 'web' }).is(service)) { + // connection.id did is a valid provider value. Try using that. + return service + } + throw new Error( + `unable to determine provider to use to addProviderAndDelegateToAccount using access.connection.id did ${service}. expected a did:web:` + ) + })() + + if (!space || !spaceMeta) { + throw new Error('No space selected') + } + + if (spaceMeta && spaceMeta.isRegistered) { + throw new Error('Space already registered with web3.storage.') + } + const account = { did: () => createDidMailtoFromEmail(email) } + await addProvider({ access, space, account, provider }) + const delegateSpaceAccessResult = await delegateSpaceAccessToAccount( + access, + space, + account + ) + if (delegateSpaceAccessResult.error) { + throw delegateSpaceAccessResult + } + spaceMeta.isRegistered = true + await agentData.addSpace(space, spaceMeta) +} + +/** + * @param {AccessAgent} access + * @param {Ucanto.DID<'key'>} space + * @param {Ucanto.Principal>} account + */ +async function delegateSpaceAccessToAccount(access, space, account) { + const issuerSaysAccountCanAdminSpace = + await createIssuerSaysAccountCanAdminSpace( + access.issuer, + space, + account, + undefined, + access.proofs([{ with: space, can: '*' }]), + // we want to sign over control of this space forever + Infinity + ) + return access.invokeAndExecute(Access.delegate, { + audience: access.connection.id, + with: space, + expiration: Infinity, + nb: { + delegations: { + [issuerSaysAccountCanAdminSpace.cid.toString()]: + issuerSaysAccountCanAdminSpace.cid, + }, + }, + proofs: [ + // must be embedded here because it's referenced by cid in .nb.delegations + issuerSaysAccountCanAdminSpace, + ], + }) +} + +/** + * @param {Ucanto.Signer>} issuer + * @param {Ucanto.DID} space + * @param {Ucanto.Principal>} account + * @param {Ucanto.Capabilities} capabilities + * @param {Ucanto.Delegation[]} proofs + * @param {number} expiration + * @returns + */ +async function createIssuerSaysAccountCanAdminSpace( + issuer, + space, + account, + capabilities = [ + { + can: '*', + with: space, + }, + ], + proofs = [], + expiration +) { + return ucanto.delegate({ + issuer, + audience: account, + capabilities, + proofs, + expiration, + }) +} diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 62e7e28b7..84c526df0 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -7,7 +7,7 @@ import * as CAR from '@ucanto/transport/car' import * as CBOR from '@ucanto/transport/cbor' import * as HTTP from '@ucanto/transport/http' import * as ucanto from '@ucanto/core' -import { URI, DID as DIDValidator } from '@ucanto/validator' +import { URI } from '@ucanto/validator' import { Peer } from './awake/peer.js' import * as Space from '@web3-storage/capabilities/space' import * as Voucher from '@web3-storage/capabilities/voucher' @@ -25,7 +25,10 @@ import { } from './delegations.js' import { AgentData, getSessionProofs } from './agent-data.js' import { createDidMailtoFromEmail } from './utils/did-mailto.js' -import { addProvider, waitForDelegationOnSocket } from './agent-use-cases.js' +import { + addProviderAndDelegateToAccount, + waitForDelegationOnSocket, +} from './agent-use-cases.js' export { AgentData, createDidMailtoFromEmail } export * from './agent-use-cases.js' @@ -33,39 +36,15 @@ export * from './agent-use-cases.js' const HOST = 'https://access.web3.storage' const PRINCIPAL = DID.parse('did:web:web3.storage') -/** @type {WeakMap} */ -export const agentToData = new WeakMap() - /** - * @param {Ucanto.Signer>} issuer - * @param {Ucanto.DID} space - * @param {Ucanto.Principal>} account - * @param {Ucanto.Capabilities} capabilities - * @param {Ucanto.Delegation[]} proofs - * @param {number} expiration - * @returns + * Keeps track of AgentData for all Agents constructed. + * Used by + * * addSpacesFromDelegations - so it can only accept Agent as param, but still mutate corresponding AgentData + * + * @deprecated - remove this when deprecated addSpacesFromDelegations is removed */ -async function createIssuerSaysAccountCanAdminSpace( - issuer, - space, - account, - capabilities = [ - { - can: '*', - with: space, - }, - ], - proofs = [], - expiration -) { - return ucanto.delegate({ - issuer, - audience: account, - capabilities, - proofs, - expiration, - }) -} +/** @type {WeakMap} */ +const agentToData = new WeakMap() /** * @typedef {import('./types').Service} Service @@ -462,98 +441,6 @@ export class Agent { } } - /** - * @param {Ucanto.DID<'key'>} space - * @param {Ucanto.Principal>} account - * @param {Ucanto.DID<'web'>} provider - e.g. 'did:web:staging.web3.storage' - */ - async #addProvider(space, account, provider) { - return addProvider(this, space, account, provider) - } - - /** - * - * @param {Ucanto.DID<'key'>} space - * @param {Ucanto.Principal>} account - */ - async #delegateSpaceAccessToAccount(space, account) { - const issuerSaysAccountCanAdminSpace = - await createIssuerSaysAccountCanAdminSpace( - this.issuer, - space, - account, - undefined, - this.proofs([{ with: space, can: '*' }]), - // we want to sign over control of this space forever - Infinity - ) - return this.invokeAndExecute(Access.delegate, { - audience: this.connection.id, - with: space, - expiration: Infinity, - nb: { - delegations: { - [issuerSaysAccountCanAdminSpace.cid.toString()]: - issuerSaysAccountCanAdminSpace.cid, - }, - }, - proofs: [ - // must be embedded here because it's referenced by cid in .nb.delegations - issuerSaysAccountCanAdminSpace, - ], - }) - } - - /** - * Invokes voucher/redeem for the free tier, wait on the websocket for the voucher/claim and invokes it - * - * It also adds a full space delegation to the service in the voucher/claim invocation to allow for recovery - * - * @param {string} email - * @param {object} [opts] - * @param {AbortSignal} [opts.signal] - * @param {Ucanto.DID<'key'>} [opts.space] - * @param {Ucanto.DID<'web'>} [opts.provider] - provider to register - defaults to this.connection.id - */ - async #newRegisterSpace(email, opts) { - const space = opts?.space || this.currentSpace() - const spaceMeta = space ? this.#data.spaces.get(space) : undefined - const provider = - opts?.provider || - (() => { - const service = this.connection.id.did() - if (DIDValidator.match({ method: 'web' }).is(service)) { - // connection.id did is a valid provider value. Try using that. - return service - } - throw new Error( - `unable to determine provider to use to register space. Pass opts.provider` - ) - })() - - if (!space || !spaceMeta) { - throw new Error('No space selected') - } - - if (spaceMeta && spaceMeta.isRegistered) { - throw new Error('Space already registered with web3.storage.') - } - const account = { did: () => createDidMailtoFromEmail(email) } - await this.#addProvider(space, account, provider) - const delegateSpaceAccessResult = await this.#delegateSpaceAccessToAccount( - space, - account - ) - if (delegateSpaceAccessResult.error) { - // @ts-ignore it's very weird that this is throwing an error but line 692 above does not - ignore for now - throw new Error(delegateSpaceAccessResult.message, { - cause: delegateSpaceAccessResult, - }) - } - spaceMeta.isRegistered = true - this.#data.addSpace(space, spaceMeta) - } - /** * Invokes voucher/redeem for the free tier, wait on the websocket for the voucher/claim and invokes it * @@ -568,7 +455,12 @@ export class Agent { async registerSpace(email, opts = {}) { // if the client passes `provider` use the new space registration flow if (opts.provider) { - return this.#newRegisterSpace(email, opts) + return await addProviderAndDelegateToAccount( + this, + this.#data, + email, + opts + ) } const space = opts.space ?? this.currentSpace() @@ -834,3 +726,35 @@ export class Agent { return inv } } + +/** + * Given a list of delegations, add to agent data spaces list. + * + * @deprecated - trying to remove explicit space tracking from Agent/AgentData + * in favor of functions that derive the space set from access.delegations + * + * @param {Agent} access + * @param {Ucanto.Delegation[]} delegations + */ +export async function addSpacesFromDelegations(access, delegations) { + const data = agentToData.get(access) + if (!data) { + throw Object.assign(new Error(`cannot determine AgentData for Agent`), { + agent: access, + }) + } + if (delegations.length > 0) { + const allows = ucanto.Delegation.allows( + delegations[0], + ...delegations.slice(1) + ) + for (const [did, value] of Object.entries(allows)) { + // TODO I don't think this should be `store/*` but this works for today + if (value['store/*']) { + data.addSpace(/** @type {Ucanto.DID} */ (did), { + isRegistered: true, + }) + } + } + } +} diff --git a/packages/access-client/src/utils/ws.js b/packages/access-client/src/utils/ws.js index 127e89382..7658cc0cb 100644 --- a/packages/access-client/src/utils/ws.js +++ b/packages/access-client/src/utils/ws.js @@ -63,10 +63,6 @@ export class Websocket { this.ws = ws ws.addEventListener('close', (event) => { - if (event.code === 1006) { - this.close(event.code, event.reason) - return - } if (!this.forceClose && !this.timeout) { this.timeout = setTimeout(() => { this.attemps++ From 70b663d534edf96959d86fd98f372fcbfc2c4fdc Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 17 Mar 2023 14:58:07 -0700 Subject: [PATCH 53/54] re-introduce checkAudience in agent addProof/validate --- packages/access-api/test/access-client-agent.test.js | 9 --------- packages/access-client/src/agent.js | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/access-api/test/access-client-agent.test.js b/packages/access-api/test/access-client-agent.test.js index ba6b11df1..1df61b5b5 100644 --- a/packages/access-api/test/access-client-agent.test.js +++ b/packages/access-api/test/access-client-agent.test.js @@ -344,15 +344,6 @@ for (const accessApiVariant of /** @type {const} */ ([ 2, 'deviceBIssuerClaimed delegations' ) - // claim delegations aud=account - const deviceBAccountClaimed = await claimAccess(deviceB, account.did(), { - addProofs: true, - }) - assert.equal( - deviceBAccountClaimed.length, - 1, - 'deviceBAccountClaimed delegations' - ) // try to addProvider await addProvider({ diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 84c526df0..e97f411a6 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -160,6 +160,7 @@ export class Agent { */ async addProof(delegation) { validate(delegation, { + checkAudience: this.issuer, checkIsExpired: true, }) await this.#data.addDelegation(delegation, { audience: this.meta }) From 943458dbb7ed2e929c608b331580920e8ca0e021 Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Fri, 17 Mar 2023 16:06:51 -0700 Subject: [PATCH 54/54] fix: only use spaces with key dids (#568) this is not ideal, but I chatted with @gozala and we agreed that this makes sense for now --------- Co-authored-by: Benjamin Goering <171782+gobengo@users.noreply.github.com> --- packages/access-client/src/agent.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index e97f411a6..b4f4dd6cd 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -744,14 +744,16 @@ export async function addSpacesFromDelegations(access, delegations) { agent: access, }) } + // TODO: we need a more robust way to determine which spaces a user has access to + // it may or may not involve look at delegations if (delegations.length > 0) { const allows = ucanto.Delegation.allows( delegations[0], ...delegations.slice(1) ) + for (const [did, value] of Object.entries(allows)) { - // TODO I don't think this should be `store/*` but this works for today - if (value['store/*']) { + if (did.startsWith('did:key') && value['space/*']) { data.addSpace(/** @type {Ucanto.DID} */ (did), { isRegistered: true, })