diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml index 191f0f2f0..000c96d30 100644 --- a/.github/workflows/manual.yml +++ b/.github/workflows/manual.yml @@ -12,6 +12,7 @@ on: - upload-client - access-client - wallet + - docs environment: description: 'Environment to deploy' required: true @@ -96,3 +97,29 @@ jobs: - run: pnpm -r --filter @web3-storage/upload-client publish --tag next --access public env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + deploy-docs: + runs-on: ubuntu-latest + if: github.event.inputs.package == 'docs' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2.2.3 + with: + version: 7 + - uses: actions/setup-node@v3 + with: + node-version: 18 + registry-url: 'https://registry.npmjs.org' + cache: 'pnpm' + - run: pnpm install + - run: pnpm run lint + - run: pnpm run docs + - uses: actions/configure-pages@v2 + - uses: actions/upload-pages-artifact@v1 + with: + path: './docs' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.github/workflows/reuse-deploy-api.yml b/.github/workflows/reuse-deploy-api.yml index 50be27f62..784927adb 100644 --- a/.github/workflows/reuse-deploy-api.yml +++ b/.github/workflows/reuse-deploy-api.yml @@ -41,6 +41,11 @@ jobs: node-version: 18 cache: 'pnpm' - run: pnpm install + # Migration database + - run: pnpm -r --filter @web3-storage/access-api exec wrangler d1 migrations apply __D1_BETA__ --env ${{ inputs.environment }} + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CF_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} # Publish worker to cloudflare - uses: cloudflare/wrangler-action@2.0.0 with: diff --git a/package.json b/package.json index 8adb0001d..4a0b4d15d 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,15 @@ "clean": "rm -rf docs node_modules pnpm-lock.yaml packages/*/{pnpm-lock.yaml,coverage,.nyc_output,dist,node_modules,.next}", "lint": "pnpm -r --if-present run lint", "test": "pnpm -r --if-present run test", - "check": "pnpm -r --if-present run check" + "check": "pnpm -r --if-present run check", + "docs": "typedoc --out docs" }, "devDependencies": { "lint-staged": "^13.0.3", "prettier": "2.7.1", "simple-git-hooks": "^2.8.1", - "typescript": "^4.8.4", - "wrangler": "^2.1.13" + "typescript": "4.8.4", + "wrangler": "^2.4.0" }, "simple-git-hooks": { "pre-commit": "npx lint-staged" @@ -35,5 +36,9 @@ }, "engines": { "node": ">=16" + }, + "dependencies": { + "typedoc": "^0.23.21", + "typedoc-plugin-missing-exports": "^1.0.0" } } diff --git a/packages/access-api/migrations/0000_create_spaces_table.sql b/packages/access-api/migrations/0000_create_spaces_table.sql new file mode 100644 index 000000000..c56fb1cf5 --- /dev/null +++ b/packages/access-api/migrations/0000_create_spaces_table.sql @@ -0,0 +1,11 @@ +-- Migration number: 0000 2022-11-17T15:52:48.968Z +CREATE TABLE + IF NOT EXISTS spaces ( + did TEXT NOT NULL PRIMARY KEY, + product TEXT NOT NULL, + email TEXT NOT NULL, + agent TEXT NOT NULL, + inserted_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), + UNIQUE (did) + ); \ No newline at end of file diff --git a/packages/access-api/package.json b/packages/access-api/package.json index e1f2afdf5..477ae45dc 100644 --- a/packages/access-api/package.json +++ b/packages/access-api/package.json @@ -29,7 +29,7 @@ "multiformats": "^10.0.2", "nanoid": "^4.0.0", "p-retry": "^5.1.1", - "preact": "^10.11.2", + "preact": "^10.11.3", "preact-render-to-string": "^5.2.6", "qrcode": "^1.5.1", "toucan-js": "^2.7.0", @@ -38,30 +38,31 @@ "devDependencies": { "@cloudflare/workers-types": "^3.18.0", "@databases/escape-identifier": "^1.0.3", + "@databases/split-sql-query": "^1.0.3", "@databases/sql": "^3.2.0", - "@sentry/cli": "^2.8.0", - "@sentry/webpack-plugin": "^1.19.1", + "@sentry/cli": "^2.9.0", + "@sentry/webpack-plugin": "^1.20.0", "@types/assert": "^1.5.6", "@types/git-rev-sync": "^2.0.0", - "@types/node": "^18.11.7", + "@types/node": "^18.11.9", "@types/qrcode": "^1.5.0", "assert": "^2.0.0", - "ava": "^5.0.1", + "ava": "^5.1.0", "better-sqlite3": "7.6.2", "buffer": "^6.0.3", "delay": "^5.0.0", "dotenv": "^16.0.3", - "esbuild": "^0.15.12", + "esbuild": "^0.15.14", "execa": "^6.1.0", - "git-rev-sync": "^3.0.1", + "git-rev-sync": "^3.0.2", "hd-scripts": "^3.0.2", - "miniflare": "^2.10.0", + "miniflare": "^2.11.0", "p-wait-for": "^5.0.0", "process": "^0.11.10", - "readable-stream": "^4.1.0", - "sade": "^1.7.4", + "readable-stream": "^4.2.0", + "sade": "^1.8.1", "typescript": "4.8.4", - "wrangler": "^2.1.13" + "wrangler": "^2.4.0" }, "eslintConfig": { "extends": [ @@ -75,7 +76,7 @@ "COMMITHASH": "readonly", "BRANCH": "readonly", "DEBUG": "readonly", - "ACCOUNTS": "writable", + "SPACES": "writable", "VALIDATIONS": "writable", "BUCKET": "writable", "W3ACCESS_METRICS": "writable", diff --git a/packages/access-api/scripts/cli.js b/packages/access-api/scripts/cli.js index 0ed6f832b..db29d7037 100755 --- a/packages/access-api/scripts/cli.js +++ b/packages/access-api/scripts/cli.js @@ -12,7 +12,7 @@ import { Log, LogLevel, Miniflare } from 'miniflare' // @ts-ignore import git from 'git-rev-sync' -import { migrate } from '../sql/migrate.js' +import { migrate } from './migrate.js' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const require = createRequire(__dirname) diff --git a/packages/access-api/scripts/migrate.js b/packages/access-api/scripts/migrate.js new file mode 100644 index 000000000..c4ef54dfe --- /dev/null +++ b/packages/access-api/scripts/migrate.js @@ -0,0 +1,47 @@ +// import { escapeSQLiteIdentifier } from '@databases/escape-identifier' +import split from '@databases/split-sql-query' +import sql from '@databases/sql' +import path from 'path' +import { fileURLToPath } from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +/** @type {import('@databases/sql').FormatConfig} */ +const sqliteFormat = { + // escapeIdentifier: (str) => escapeSQLiteIdentifier(str), + // formatValue: (value) => ({ placeholder: '?', value }), + + escapeIdentifier: (_) => '', + formatValue: (_, __) => ({ placeholder: '', value: '' }), +} +const migrations = [ + sql.file(`${__dirname}/../migrations/0000_create_spaces_table.sql`), +] + +/** + * Migrate from migration files + * + * @param {D1Database} db + */ +export async function migrate(db) { + try { + for (const m of migrations) { + /** @type {import('@databases/sql').SQLQuery[]} */ + // @ts-ignore + const qs = split.default(m) + await db.batch( + qs.map((q) => { + return db.prepare(q.format(sqliteFormat).text.replace(/^--.*$/gm, '')) + }) + ) + } + } catch (error) { + const err = /** @type {Error} */ (error) + // eslint-disable-next-line no-console + console.error('D1 Error', { + message: err.message, + // @ts-ignore + cause: err.cause?.message, + }) + } +} diff --git a/packages/access-api/sql/migrate.js b/packages/access-api/sql/migrate.js deleted file mode 100644 index 43bdc5b2b..000000000 --- a/packages/access-api/sql/migrate.js +++ /dev/null @@ -1,37 +0,0 @@ -import { escapeSQLiteIdentifier } from '@databases/escape-identifier' -import sql from '@databases/sql' -import path from 'path' -import { fileURLToPath } from 'url' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) - -/** @type {import('@databases/sql').FormatConfig} */ -const sqliteFormat = { - escapeIdentifier: (str) => escapeSQLiteIdentifier(str), - formatValue: (value) => ({ placeholder: '?', value }), -} - -const migrations = [sql.file(`${__dirname}/tables.sql`)] - -/** - * Probably should batch queries and use https://www.atdatabases.org/docs/split-sql-query to split inlined queries - * - * @see https://docs.google.com/document/d/1QpUryGBWaGbAIjkw2URwpV6Btp5S-XQVkBJJs85dLRc/edit# - * - * @param {D1Database} db - */ -export async function migrate(db) { - try { - for (const m of migrations) { - await db.exec(m.format(sqliteFormat).text.replace(/\n/g, '')) - } - } catch (error) { - const err = /** @type {Error} */ (error) - // eslint-disable-next-line no-console - console.error('D1 Error', { - message: err.message, - // @ts-ignore - cause: err.cause?.message, - }) - } -} diff --git a/packages/access-api/sql/reset.sql b/packages/access-api/sql/reset.sql deleted file mode 100644 index 7e74df081..000000000 --- a/packages/access-api/sql/reset.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE - IF EXISTS accounts; \ No newline at end of file diff --git a/packages/access-api/sql/tables.sql b/packages/access-api/sql/tables.sql deleted file mode 100644 index 7b1ba2512..000000000 --- a/packages/access-api/sql/tables.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE - IF NOT EXISTS accounts ( - did TEXT NOT NULL PRIMARY KEY, - product TEXT NOT NULL, - email TEXT NOT NULL, - agent TEXT NOT NULL, - inserted_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), - updated_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), - UNIQUE (did) - ) \ No newline at end of file diff --git a/packages/access-api/src/bindings.d.ts b/packages/access-api/src/bindings.d.ts index f4a7781be..5a99c4bb5 100644 --- a/packages/access-api/src/bindings.d.ts +++ b/packages/access-api/src/bindings.d.ts @@ -2,7 +2,7 @@ import type { Logging } from '@web3-storage/worker-utils/logging' import type { Handler as _Handler } from '@web3-storage/worker-utils/router' import type { Signer } from '@ucanto/interface' import { Email } from './utils/email.js' -import { Accounts } from './kvs/accounts.js' +import { Spaces } from './kvs/spaces.js' import { Validations } from './kvs/validations.js' import { D1QB } from 'workers-qb' import { loadConfig } from './config.js' @@ -29,7 +29,7 @@ export interface Env { POSTMARK_TOKEN: string LOGTAIL_TOKEN: string // bindings - ACCOUNTS: KVNamespace + SPACES: KVNamespace VALIDATIONS: KVNamespace W3ACCESS_METRICS: AnalyticsEngine // eslint-disable-next-line @typescript-eslint/naming-convention @@ -43,7 +43,7 @@ export interface RouteContext { url: URL email: Email kvs: { - accounts: Accounts + spaces: Spaces validations: Validations } db: D1QB diff --git a/packages/access-api/src/config.js b/packages/access-api/src/config.js index 7c891ced8..afa519ae2 100644 --- a/packages/access-api/src/config.js +++ b/packages/access-api/src/config.js @@ -54,7 +54,7 @@ export function loadConfig(env) { /** @type {import("./bindings").AnalyticsEngine} */ ( env.W3ACCESS_METRICS ) || createAnalyticsEngine(), - ACCOUNTS: env.ACCOUNTS, + SPACES: env.SPACES, VALIDATIONS: env.VALIDATIONS, DB: /** @type {D1Database} */ (env.__D1_BETA__), } diff --git a/packages/access-api/src/kvs/accounts.js b/packages/access-api/src/kvs/spaces.js similarity index 84% rename from packages/access-api/src/kvs/accounts.js rename to packages/access-api/src/kvs/spaces.js index 466385a92..386ef43db 100644 --- a/packages/access-api/src/kvs/accounts.js +++ b/packages/access-api/src/kvs/spaces.js @@ -4,14 +4,13 @@ import * as Ucanto from '@ucanto/interface' import { delegationToString } from '@web3-storage/access/encoding' /** - * @typedef {{account: string, proof: string}} AccountValue - * @typedef {import('@web3-storage/access/types').Account} Account + * @typedef {import('@web3-storage/access/types').SpaceD1} SpaceD1 */ /** - * Accounts + * Spaces */ -export class Accounts { +export class Spaces { /** * * @param {KVNamespace} kv @@ -28,9 +27,9 @@ export class Accounts { */ async create(capability, invocation) { await this.db.insert({ - tableName: 'accounts', + tableName: 'spaces', data: { - did: capability.nb.account, + did: capability.nb.space, product: capability.nb.product, email: capability.nb.identity.replace('mailto:', ''), agent: invocation.issuer.did(), @@ -39,13 +38,13 @@ export class Accounts { } /** - * Get account by DID + * Get space by DID * * @param {string} did */ async get(did) { const { results } = await this.db.fetchOne({ - tableName: 'accounts', + tableName: 'spaces', fields: '*', where: { conditions: 'did=?1', @@ -57,7 +56,7 @@ export class Accounts { return } - return /** @type {Account} */ ({ + return /** @type {SpaceD1} */ ({ did: results.did, agent: results.agent, email: results.email, @@ -68,7 +67,7 @@ export class Accounts { } /** - * Save account delegation per email + * Save space delegation per email * * @param {`mailto:${string}`} email * @param {Ucanto.Delegation} delegation @@ -111,7 +110,7 @@ export class Accounts { return } - return /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/types').Any]>[]} */ ( + return /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/types').Top]>[]} */ ( r ) } diff --git a/packages/access-api/src/routes/validate-email.js b/packages/access-api/src/routes/validate-email.js index 056659ebd..a3e54257d 100644 --- a/packages/access-api/src/routes/validate-email.js +++ b/packages/access-api/src/routes/validate-email.js @@ -63,7 +63,7 @@ export async function validateEmail(req, env) { async function recover(req, env) { try { const delegation = await env.kvs.validations.put( - /** @type {import('@web3-storage/access/src/types.js').EncodedDelegation<[import('@web3-storage/access/src/types.js').AccountRecover]>} */ ( + /** @type {import('@web3-storage/access/src/types.js').EncodedDelegation<[import('@web3-storage/access/src/types.js').SpaceRecover]>} */ ( req.query.ucan ) ) diff --git a/packages/access-api/src/service/index.js b/packages/access-api/src/service/index.js index 9d602f376..94ee126e8 100644 --- a/packages/access-api/src/service/index.js +++ b/packages/access-api/src/service/index.js @@ -1,6 +1,6 @@ import * as Server from '@ucanto/server' import { Failure } from '@ucanto/server' -import * as Account from '@web3-storage/access/capabilities/account' +import * as Space from '@web3-storage/access/capabilities/space' import { voucherClaimProvider } from './voucher-claim.js' import { voucherRedeemProvider } from './voucher-redeem.js' import * as DID from '@ipld/dag-ucan/did' @@ -8,7 +8,7 @@ import { delegationToString, stringToDelegation, } from '@web3-storage/access/encoding' -import { any } from '@web3-storage/access/capabilities/any' +import { top } from '@web3-storage/access/capabilities/top' /** * @param {import('../bindings').RouteContext} ctx @@ -21,16 +21,16 @@ export function service(ctx) { redeem: voucherRedeemProvider(ctx), }, - account: { - info: Server.provide(Account.info, async ({ capability, invocation }) => { - const results = await ctx.kvs.accounts.get(capability.with) + space: { + info: Server.provide(Space.info, async ({ capability, invocation }) => { + const results = await ctx.kvs.spaces.get(capability.with) if (!results) { - return new Failure('Account not found.') + return new Failure('Space not found.') } return results }), recover: Server.provide( - Account.recover, + Space.recover, async ({ capability, invocation }) => { if (capability.with !== ctx.signer.did()) { return new Failure( @@ -40,7 +40,7 @@ export function service(ctx) { ) } - const encoded = await ctx.kvs.accounts.getDelegations( + const encoded = await ctx.kvs.spaces.getDelegations( capability.nb.identity ) if (!encoded) { @@ -52,7 +52,7 @@ export function service(ctx) { const results = [] for (const e of encoded) { const proof = await stringToDelegation(e) - const del = await any.delegate({ + const del = await top.delegate({ audience: invocation.issuer, issuer: ctx.signer, with: proof.capabilities[0].with, @@ -68,20 +68,20 @@ export function service(ctx) { ), 'recover-validation': Server.provide( - Account.recoverValidation, + Space.recoverValidation, async ({ capability }) => { // check if we have delegations in the KV for the email - // if yes send email with account/login - // if not error "no accounts for email X" + // if yes send email with space/recover + // if not error "no spaces for email X" const email = capability.nb.identity - if (!(await ctx.kvs.accounts.hasDelegations(email))) { + if (!(await ctx.kvs.spaces.hasDelegations(email))) { return new Failure( - `No accounts found for email: ${email.replace('mailto:', '')}.` + `No spaces found for email: ${email.replace('mailto:', '')}.` ) } - const inv = await Account.recover + const inv = await Space.recover .invoke({ issuer: ctx.signer, audience: DID.parse(capability.with), @@ -91,7 +91,7 @@ export function service(ctx) { identity: email, }, proofs: [ - await Account.recover.delegate({ + await Space.recover.delegate({ audience: ctx.signer, issuer: ctx.signer, expiration: Infinity, diff --git a/packages/access-api/src/service/voucher-claim.js b/packages/access-api/src/service/voucher-claim.js index 2859c3ce1..8a210512b 100644 --- a/packages/access-api/src/service/voucher-claim.js +++ b/packages/access-api/src/service/voucher-claim.js @@ -15,7 +15,7 @@ export function voucherClaimProvider(ctx) { nb: { product: 'product:*', identity: 'mailto:*', - account: 'did:*', + space: 'did:*', }, }) @@ -26,7 +26,7 @@ export function voucherClaimProvider(ctx) { with: ctx.signer.did(), lifetimeInSeconds: 60 * 10, // 10 mins nb: { - account: capability.with, + space: capability.with, identity: capability.nb.identity, product: capability.nb.product, }, diff --git a/packages/access-api/src/service/voucher-redeem.js b/packages/access-api/src/service/voucher-redeem.js index d86eee05e..ae1fdc332 100644 --- a/packages/access-api/src/service/voucher-redeem.js +++ b/packages/access-api/src/service/voucher-redeem.js @@ -13,7 +13,7 @@ export function voucherRedeemProvider(ctx) { ) } // @ts-ignore - TODO fix this - await ctx.kvs.accounts.create(capability, invocation) + await ctx.kvs.spaces.create(capability, invocation) // We should only save delegation for email identities if (capability.nb.identity.startsWith('mailto:')) { @@ -22,22 +22,22 @@ export function voucherRedeemProvider(ctx) { Delegation.isDelegation(p) && p.audience.did() === ctx.signer.did() ) { - await ctx.kvs.accounts.saveDelegation(capability.nb.identity, p) + await ctx.kvs.spaces.saveDelegation(capability.nb.identity, p) } } } ctx.config.METRICS.writeDataPoint({ - blobs: [ctx.config.ENV, 'new_account_v1'], + blobs: [ctx.config.ENV, 'new_space_v1'], doubles: [1], }) if (ctx.config.ENV === 'production') { ctx.email.send({ to: 'david@dag.house,jchris@dag.house', - subject: 'New w3account Created', - textBody: `New account v1 registered for ${ - capability.nb.account + subject: 'New Space Created', + textBody: `New space v1 registered for ${ + capability.nb.space } with email ${capability.nb.identity.replace('mailto:', '')}`, }) } diff --git a/packages/access-api/src/utils/context.js b/packages/access-api/src/utils/context.js index 7a931c0e4..235ea2068 100644 --- a/packages/access-api/src/utils/context.js +++ b/packages/access-api/src/utils/context.js @@ -3,7 +3,7 @@ import { Logging } from '@web3-storage/worker-utils/logging' import Toucan from 'toucan-js' import pkg from '../../package.json' import { loadConfig } from '../config.js' -import { Accounts } from '../kvs/accounts.js' +import { Spaces } from '../kvs/spaces.js' import { Validations } from '../kvs/validations.js' import { Email } from './email.js' import { D1QB } from 'workers-qb' @@ -51,7 +51,7 @@ export function getContext(request, env, ctx) { config, url, kvs: { - accounts: new Accounts(config.ACCOUNTS, db), + spaces: new Spaces(config.SPACES, db), validations: new Validations(config.VALIDATIONS), }, email: new Email({ token: config.POSTMARK_TOKEN }), diff --git a/packages/access-api/src/utils/html.js b/packages/access-api/src/utils/html.js index 301a1d8e5..083987196 100644 --- a/packages/access-api/src/utils/html.js +++ b/packages/access-api/src/utils/html.js @@ -72,7 +72,7 @@ export class HtmlResponse extends Response { /** * * @param {object} param0 - * @param {Ucanto.Delegation<[import('@web3-storage/access/capabilities/types').VoucherClaim]> | Ucanto.Delegation<[import('@web3-storage/access/capabilities/types').AccountRecover]>} param0.delegation + * @param {Ucanto.Delegation<[import('@web3-storage/access/capabilities/types').VoucherClaim]> | Ucanto.Delegation<[import('@web3-storage/access/capabilities/types').SpaceRecover]>} param0.delegation * @param {string} param0.ucan * @param {string} param0.qrcode */ diff --git a/packages/access-api/test/account-recover.test.js b/packages/access-api/test/account-recover.test.js index 3b90b9680..32140b1f9 100644 --- a/packages/access-api/test/account-recover.test.js +++ b/packages/access-api/test/account-recover.test.js @@ -1,19 +1,19 @@ -import * as Account from '@web3-storage/access/capabilities/account' +import * as Space from '@web3-storage/access/capabilities/space' import { stringToDelegation } from '@web3-storage/access/encoding' import pWaitFor from 'p-wait-for' import { context, test } from './helpers/context.js' import { Validations } from '../src/kvs/validations.js' -import { createAccount } from './helpers/utils.js' +import { createSpace } from './helpers/utils.js' test.beforeEach(async (t) => { t.context = await context() }) -test('should fail before registering account', async (t) => { +test('should fail before registering space', async (t) => { const { issuer, service, conn } = t.context - const inv = await Account.recoverValidation + const inv = await Space.recoverValidation .invoke({ issuer, audience: service, @@ -25,24 +25,24 @@ test('should fail before registering account', async (t) => { .execute(conn) if (inv?.error) { - t.deepEqual(inv.message, `No accounts found for email: hello@dag.house.`) + t.deepEqual(inv.message, `No spaces found for email: hello@dag.house.`) } else { return t.fail() } }) -test('should return account/recover', async (t) => { +test('should return space/recover', async (t) => { const { issuer, service, conn, mf } = t.context - await createAccount(issuer, service, conn, 'account-recover@dag.house') + await createSpace(issuer, service, conn, 'space-recover@dag.house') - const inv = await Account.recoverValidation + const inv = await Space.recoverValidation .invoke({ issuer, audience: service, with: issuer.did(), nb: { - identity: 'mailto:account-recover@dag.house', + identity: 'mailto:space-recover@dag.house', }, }) .execute(conn) @@ -53,7 +53,7 @@ test('should return account/recover', async (t) => { const url = new URL(inv) const encoded = - /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').AccountRecover]>} */ ( + /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').SpaceRecover]>} */ ( url.searchParams.get('ucan') ) @@ -61,7 +61,7 @@ test('should return account/recover', async (t) => { t.deepEqual(del.audience.did(), issuer.did()) t.deepEqual(del.issuer.did(), service.did()) - t.deepEqual(del.capabilities[0].can, 'account/recover') + t.deepEqual(del.capabilities[0].can, 'space/recover') const rsp = await mf.dispatchFetch(url) const html = await rsp.text() @@ -69,7 +69,7 @@ test('should return account/recover', async (t) => { const validations = new Validations(await mf.getKVNamespace('VALIDATIONS')) const recoverEncoded = - /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').AccountRecover]>} */ ( + /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').SpaceRecover]>} */ ( await validations.get(issuer.did()) ) @@ -77,7 +77,7 @@ test('should return account/recover', async (t) => { const recover = await stringToDelegation(recoverEncoded) t.deepEqual(recover.audience.did(), issuer.did()) t.deepEqual(recover.issuer.did(), service.did()) - t.deepEqual(recover.capabilities[0].can, 'account/recover') + t.deepEqual(recover.capabilities[0].can, 'space/recover') // ws const res = await mf.dispatchFetch('http://localhost:8787/validate-ws', { @@ -93,7 +93,7 @@ test('should return account/recover', async (t) => { const data = JSON.parse(event.data) const encoded = - /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').AccountRecover]>} */ ( + /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').SpaceRecover]>} */ ( data.delegation ) @@ -101,7 +101,7 @@ test('should return account/recover', async (t) => { const recover = await stringToDelegation(encoded) t.deepEqual(recover.audience.did(), issuer.did()) t.deepEqual(recover.issuer.did(), service.did()) - t.deepEqual(recover.capabilities[0].can, 'account/recover') + t.deepEqual(recover.capabilities[0].can, 'space/recover') done = true }) @@ -117,12 +117,12 @@ test('should return account/recover', async (t) => { } }) -test('should invoke account/recover and get account delegation', async (t) => { +test('should invoke space/recover and get space delegation', async (t) => { const { issuer, service, conn } = t.context - const email = 'account-recover@dag.house' - const { account } = await createAccount(issuer, service, conn, email) + const email = 'space-recover@dag.house' + const { space } = await createSpace(issuer, service, conn, email) - const inv = await Account.recoverValidation + const inv = await Space.recoverValidation .invoke({ issuer, audience: service, @@ -140,7 +140,7 @@ test('should invoke account/recover and get account delegation', async (t) => { const url = new URL(inv) const encoded = - /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').AccountRecover]>} */ ( + /** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/access/capabilities/types').SpaceRecover]>} */ ( url.searchParams.get('ucan') ) @@ -148,9 +148,9 @@ test('should invoke account/recover and get account delegation', async (t) => { t.deepEqual(del.audience.did(), issuer.did()) t.deepEqual(del.issuer.did(), service.did()) - t.deepEqual(del.capabilities[0].can, 'account/recover') + t.deepEqual(del.capabilities[0].can, 'space/recover') - const inv2 = await Account.recover + const inv2 = await Space.recover .invoke({ issuer, audience: service, @@ -166,23 +166,23 @@ test('should invoke account/recover and get account delegation', async (t) => { return t.fail('failed to recover') } - const accountDelegation = await stringToDelegation(inv2[0]) - t.deepEqual(accountDelegation.audience.did(), issuer.did()) - t.deepEqual(accountDelegation.capabilities[0].can, '*') - t.deepEqual(accountDelegation.capabilities[0].with, account.did()) + const spaceDelegation = await stringToDelegation(inv2[0]) + t.deepEqual(spaceDelegation.audience.did(), issuer.did()) + t.deepEqual(spaceDelegation.capabilities[0].can, '*') + t.deepEqual(spaceDelegation.capabilities[0].with, space.did()) - const accountInfo = await Account.info + const spaceInfo = await Space.info .invoke({ issuer, audience: service, - with: account.did(), - proofs: [accountDelegation], + with: space.did(), + proofs: [spaceDelegation], }) .execute(conn) - if (!accountInfo || accountInfo.error) { - return t.fail('failed to get account info') + if (!spaceInfo || spaceInfo.error) { + return t.fail('failed to get space info') } - t.deepEqual(accountInfo.did, account.did()) + t.deepEqual(spaceInfo.did, space.did()) }) diff --git a/packages/access-api/test/helpers/context.js b/packages/access-api/test/helpers/context.js index 3f3a5bf5d..479cb4ab9 100644 --- a/packages/access-api/test/helpers/context.js +++ b/packages/access-api/test/helpers/context.js @@ -6,7 +6,7 @@ import dotenv from 'dotenv' import { Miniflare } from 'miniflare' import path from 'path' import { fileURLToPath } from 'url' -import { migrate } from '../../sql/migrate.js' +import { migrate } from '../../scripts/migrate.js' import { D1QB } from 'workers-qb' const __dirname = path.dirname(fileURLToPath(import.meta.url)) diff --git a/packages/access-api/test/helpers/utils.js b/packages/access-api/test/helpers/utils.js index c73726c2c..7299d6c49 100644 --- a/packages/access-api/test/helpers/utils.js +++ b/packages/access-api/test/helpers/utils.js @@ -2,10 +2,10 @@ import * as UCAN from '@ipld/dag-ucan' // eslint-disable-next-line no-unused-vars import * as Types from '@ucanto/interface' -import { StoreMemory } from '@web3-storage/access/stores/store-memory' -import * as Any from '@web3-storage/access/capabilities/any' +import * as Top from '@web3-storage/access/capabilities/top' import * as Voucher from '@web3-storage/access/capabilities/voucher' import { stringToDelegation } from '@web3-storage/access/encoding' +import { Signer } from '@ucanto/principal/ed25519' /** * @param {Types.UCAN.View} ucan @@ -26,14 +26,13 @@ export async function send(ucan, mf) { * @param {Types.ConnectionView} conn * @param {string} email */ -export async function createAccount(issuer, service, conn, email) { - const store = new StoreMemory() - const account = await store.createAccount() +export async function createSpace(issuer, service, conn, email) { + const space = await Signer.generate() const claim = await Voucher.claim .invoke({ issuer, audience: service, - with: account.did(), + with: space.did(), nb: { // @ts-ignore identity: `mailto:${email}`, @@ -41,17 +40,17 @@ export async function createAccount(issuer, service, conn, email) { service: service.did(), }, proofs: [ - await Any.any.delegate({ - issuer: account, + await Top.top.delegate({ + issuer: space, audience: issuer, - with: account.did(), + with: space.did(), expiration: Infinity, }), ], }) .execute(conn) if (!claim || claim.error) { - throw new Error('failed to create account') + throw new Error('failed to create space') } const delegation = await stringToDelegation(claim) @@ -62,16 +61,16 @@ export async function createAccount(issuer, service, conn, email) { audience: service, with: service.did(), nb: { - account: account.did(), + space: space.did(), identity: delegation.capabilities[0].nb.identity, product: delegation.capabilities[0].nb.product, }, proofs: [ delegation, - await Any.any.delegate({ - issuer: account, + await Top.top.delegate({ + issuer: space, audience: service, - with: account.did(), + with: space.did(), expiration: Infinity, }), ], @@ -85,6 +84,6 @@ export async function createAccount(issuer, service, conn, email) { } return { - account, + space, } } diff --git a/packages/access-api/test/voucher-claim.test.js b/packages/access-api/test/voucher-claim.test.js index ea5364c31..738779aca 100644 --- a/packages/access-api/test/voucher-claim.test.js +++ b/packages/access-api/test/voucher-claim.test.js @@ -34,7 +34,7 @@ test('should voucher/claim', async (t) => { t.deepEqual(delegation.issuer.did(), service.did()) t.deepEqual(delegation.audience.did(), issuer.did()) - t.deepEqual(delegation.capabilities[0].nb.account, issuer.did()) + t.deepEqual(delegation.capabilities[0].nb.space, issuer.did()) t.deepEqual(delegation.capabilities[0].nb.product, 'product:free') t.deepEqual(delegation.capabilities[0].nb.identity, 'mailto:email@dag.house') @@ -45,7 +45,7 @@ test('should voucher/claim', async (t) => { with: service.did(), can: 'voucher/redeem', nb: { - account: 'did:*', + space: 'did:*', identity: 'mailto:*', product: 'product:*', }, diff --git a/packages/access-api/test/voucher-redeem.test.js b/packages/access-api/test/voucher-redeem.test.js index 1ad8e9bc8..b0276803d 100644 --- a/packages/access-api/test/voucher-redeem.test.js +++ b/packages/access-api/test/voucher-redeem.test.js @@ -1,36 +1,35 @@ /* eslint-disable unicorn/prefer-number-properties */ -import * as Any from '@web3-storage/access/capabilities/any' +import * as Top from '@web3-storage/access/capabilities/top' import * as Voucher from '@web3-storage/access/capabilities/voucher' import { stringToDelegation } from '@web3-storage/access/encoding' -import { StoreMemory } from '@web3-storage/access/stores/store-memory' import { context, test } from './helpers/context.js' -import { createAccount } from './helpers/utils.js' -import { Accounts } from '../src/kvs/accounts.js' +import { createSpace } from './helpers/utils.js' +import { Spaces } from '../src/kvs/spaces.js' +import { Signer } from '@ucanto/principal/ed25519' test.beforeEach(async (t) => { t.context = await context() }) -test('should return account/redeem', async (t) => { +test('should return voucher/redeem', async (t) => { const { issuer, service, conn, mf, db } = t.context - const store = new StoreMemory() - const account = await store.createAccount() + const space = await Signer.generate() const claim = await Voucher.claim .invoke({ issuer, audience: service, - with: account.did(), + with: space.did(), nb: { identity: 'mailto:email@dag.house', product: 'product:free', service: service.did(), }, proofs: [ - await Any.any.delegate({ - issuer: account, + await Top.top.delegate({ + issuer: space, audience: issuer, - with: account.did(), + with: space.did(), expiration: Infinity, }), ], @@ -52,16 +51,16 @@ test('should return account/redeem', async (t) => { audience: service, with: service.did(), nb: { - account: account.did(), + space: space.did(), identity: delegation.capabilities[0].nb.identity, product: delegation.capabilities[0].nb.product, }, proofs: [ delegation, - await Any.any.delegate({ - issuer: account, + await Top.top.delegate({ + issuer: space, audience: service, - with: account.did(), + with: space.did(), expiration: Infinity, }), ], @@ -73,18 +72,18 @@ test('should return account/redeem', async (t) => { return t.fail() } - const accounts = new Accounts(await mf.getKVNamespace('ACCOUNTS'), db) + const spaces = new Spaces(await mf.getKVNamespace('SPACES'), db) - // check db for account - t.like(await accounts.get(account.did()), { - did: account.did(), + // check db for space + t.like(await spaces.get(space.did()), { + did: space.did(), product: 'product:free', email: 'email@dag.house', agent: issuer.did(), }) - // check account delegations - const delegations = await accounts.getDelegations('mailto:email@dag.house') + // check space delegations + const delegations = await spaces.getDelegations('mailto:email@dag.house') if (!delegations) { return t.fail('no delegation for email') @@ -94,17 +93,17 @@ test('should return account/redeem', async (t) => { t.deepEqual(del.audience.did(), service.did()) t.deepEqual(del.capabilities[0].can, '*') - t.deepEqual(del.capabilities[0].with, account.did()) + t.deepEqual(del.capabilities[0].with, space.did()) }) -test('should save first account delegation', async (t) => { +test('should save first space delegation', async (t) => { const { issuer, service, conn, mf } = t.context - await createAccount(issuer, service, conn, 'first@dag.house') + await createSpace(issuer, service, conn, 'first@dag.house') - const accounts = await mf.getKVNamespace('ACCOUNTS') + const spaces = await mf.getKVNamespace('SPACES') - const delEncoded = await accounts.get('mailto:first@dag.house', { + const delEncoded = await spaces.get('mailto:first@dag.house', { type: 'json', }) @@ -112,15 +111,15 @@ test('should save first account delegation', async (t) => { t.assert(delEncoded.length === 1) }) -test('should save multiple account delegation', async (t) => { +test('should save multiple space delegation', async (t) => { const { issuer, service, conn, mf } = t.context - await createAccount(issuer, service, conn, 'multiple@dag.house') - await createAccount(issuer, service, conn, 'multiple@dag.house') + await createSpace(issuer, service, conn, 'multiple@dag.house') + await createSpace(issuer, service, conn, 'multiple@dag.house') - const accounts = await mf.getKVNamespace('ACCOUNTS') + const spaces = await mf.getKVNamespace('SPACES') - const delEncoded = await accounts.get('mailto:multiple@dag.house', { + const delEncoded = await spaces.get('mailto:multiple@dag.house', { type: 'json', }) @@ -137,7 +136,7 @@ test('should fail with wrong resource', async (t) => { audience: service, with: issuer.did(), nb: { - account: issuer.did(), + space: issuer.did(), identity: 'mailto:email@dag.house', product: 'product:free', }, diff --git a/packages/access-api/wrangler.toml b/packages/access-api/wrangler.toml index 321e74164..0aff12a5e 100644 --- a/packages/access-api/wrangler.toml +++ b/packages/access-api/wrangler.toml @@ -11,7 +11,7 @@ compatibility_flags = ["url_standard"] no_bundle = false [[kv_namespaces]] -binding = "ACCOUNTS" +binding = "SPACES" id = "e9fad7e04b254bf49206e08e50074387" preview_id = "e9fad7e04b254bf49206e08e50074387" @@ -21,8 +21,8 @@ id = "62e57652625c44a3b1fef2f840ffc882" preview_id = "62e57652625c44a3b1fef2f840ffc882" [[d1_databases]] -binding = "__D1_BETA__" # i.e. available in your Worker on env.DB -database_name = "accounts-dev" +binding = "__D1_BETA__" +database_name = "spaces-dev" database_id = "7c676e0c-b9e7-4711-97c8-7b1c8eb229ae" [vars] @@ -44,11 +44,11 @@ workers_dev = true vars = { ENV = "dev", DEBUG = "false" } build = { command = "scripts/cli.js build --env dev", watch_dir = "src" } kv_namespaces = [ - { binding = "ACCOUNTS", id = "5697e95e1aaa436788e6d697fd3350be" }, + { binding = "SPACES", id = "5697e95e1aaa436788e6d697fd3350be" }, { binding = "VALIDATIONS", id = "ea17f472b37a43d29c1faf7af9512e03" }, ] d1_databases = [ - { binding = "__D1_BETA__", database_name = "accounts-dev", database_id = "7c676e0c-b9e7-4711-97c8-7b1c8eb229ae" }, + { binding = "__D1_BETA__", database_name = "access-dev", database_id = "4145a261-e54c-411d-a001-050fc30e4678" }, ] unsafe = { bindings = [ { type = "analytics_engine", dataset = "W3ACCESS_METRICS", name = "W3ACCESS_METRICS" }, @@ -62,11 +62,11 @@ workers_dev = true vars = { ENV = "staging", DEBUG = "false" } build = { command = "scripts/cli.js build --env staging", watch_dir = "src" } kv_namespaces = [ - { binding = "ACCOUNTS", id = "b0e5ca990dda4e3784a1741dfa28a52e" }, + { binding = "SPACES", id = "b0e5ca990dda4e3784a1741dfa28a52e" }, { binding = "VALIDATIONS", id = "b13f07c88fe848db9ccf651a0fea3fb6" }, ] d1_databases = [ - { binding = "__D1_BETA__", database_name = "accounts-staging", database_id = "4c94bac0-76a7-4ce3-8f24-90ce50590d00" }, + { binding = "__D1_BETA__", database_name = "access-staging", database_id = "16f0660e-2a8e-4e13-9f05-0a2a594d91ed" }, ] unsafe = { bindings = [ { type = "analytics_engine", dataset = "W3ACCESS_METRICS", name = "W3ACCESS_METRICS" }, @@ -80,11 +80,11 @@ routes = [{ pattern = "access.web3.storage", custom_domain = true }] vars = { ENV = "production", DEBUG = "false" } build = { command = "scripts/cli.js build --env production", watch_dir = "src" } kv_namespaces = [ - { binding = "ACCOUNTS", id = "5437954e8cfd4f7d98557132b0a2e93f" }, + { binding = "SPACES", id = "5437954e8cfd4f7d98557132b0a2e93f" }, { binding = "VALIDATIONS", id = "fb7cf10c725f45948321e88b8cb168ad" }, ] d1_databases = [ - { binding = "__D1_BETA__", database_name = "accounts", database_id = "5a016afe-2c65-4e10-9e8d-bced1b59814a" }, + { binding = "__D1_BETA__", database_name = "access", database_id = "d9e83dff-d023-4206-9cf9-d11135bbe0b7" }, ] unsafe = { bindings = [ { type = "analytics_engine", dataset = "W3ACCESS_METRICS", name = "W3ACCESS_METRICS" }, diff --git a/packages/access-client/package.json b/packages/access-client/package.json index 86334eb52..7e21838fc 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -29,15 +29,11 @@ ".": "./src/index.js", "./capabilities/*": "./src/capabilities/*.js", "./stores/*": "./src/stores/*.js", - "./connection": "./src/connection.js", "./types": "./src/types.js", "./encoding": "./src/encoding.js" }, "typesVersions": { "*": { - "connection": [ - "dist/src/connection" - ], "types": [ "dist/src/types" ], @@ -58,10 +54,9 @@ "dist/src/**/*.d.ts.map" ], "dependencies": { - "@ipld/car": "^5.0.0", + "@ipld/car": "^5.0.1", "@ipld/dag-ucan": "^2.0.1", "@noble/ed25519": "^1.7.1", - "@types/ws": "^8.5.3", "@ucanto/client": "^3.0.2", "@ucanto/core": "^3.0.2", "@ucanto/interface": "^3.0.1", @@ -71,7 +66,7 @@ "@ucanto/validator": "^3.0.4", "@web-std/fetch": "^4.1.0", "bigint-mod-arith": "^3.1.2", - "conf": "^10.1.2", + "conf": "^10.2.0", "inquirer": "^9.1.4", "isomorphic-ws": "^5.0.0", "multiformats": "^10.0.2", @@ -79,11 +74,9 @@ "one-webcrypto": "^1.0.3", "ora": "^6.1.2", "p-defer": "^4.0.0", - "p-queue": "^7.3.0", - "p-retry": "^5.1.1", "p-wait-for": "^5.0.0", + "type-fest": "^3.2.0", "uint8arrays": "^4.0.2", - "undici": "^5.12.0", "ws": "^8.11.0", "zod": "^3.19.1" }, @@ -91,16 +84,18 @@ "@types/assert": "^1.5.6", "@types/inquirer": "^9.0.3", "@types/mocha": "^10.0.0", - "@types/node": "^18.11.7", + "@types/node": "^18.11.9", + "@types/ws": "^8.5.3", "@web-std/fetch": "^4.1.0", "assert": "^2.0.0", "delay": "^5.0.0", "dotenv": "^16.0.3", "hd-scripts": "^3.0.2", - "miniflare": "^2.9.0", + "miniflare": "^2.11.0", "mocha": "^10.1.0", + "p-queue": "^7.3.0", "playwright-test": "^8.1.1", - "sade": "^1.7.4", + "sade": "^1.8.1", "typescript": "4.8.4", "watch": "^1.0.2" }, @@ -116,7 +111,8 @@ }, "rules": { "unicorn/prefer-number-properties": "off", - "unicorn/prefer-export-from": "off" + "unicorn/prefer-export-from": "off", + "unicorn/no-array-reduce": "off" }, "env": { "mocha": true diff --git a/packages/access-client/readme.md b/packages/access-client/readme.md new file mode 100644 index 000000000..9cd140ad5 --- /dev/null +++ b/packages/access-client/readme.md @@ -0,0 +1,10 @@ +


web3.storage

+

The access client for https://web3.storage

+ +## Install + +Install the package using npm: + +```bash +npm install @web3-storage/access +``` diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index ed359eb57..535644640 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -1,3 +1,4 @@ +/* eslint-disable max-depth */ import * as DID from '@ipld/dag-ucan/did' import * as Client from '@ucanto/client' // @ts-ignore @@ -8,31 +9,19 @@ import * as CBOR from '@ucanto/transport/cbor' import * as HTTP from '@ucanto/transport/http' import { URI } from '@ucanto/validator' import { Peer } from './awake/peer.js' -import * as Account from './capabilities/account.js' +import * as Space from './capabilities/space.js' import * as Voucher from './capabilities/voucher.js' -import { any as Any } from './capabilities/any.js' +import { top as Top } from './capabilities/top.js' import { stringToDelegation } from './encoding.js' import { Websocket, AbortError } from './utils/ws.js' - -/** - * @template T - * @typedef {{ - * store: import('./stores/types').Store - * connection: Ucanto.ConnectionView, - * url?: URL, - * fetch: typeof fetch - * data: import('./stores/types').StoreData - * }} AgentOptions - */ - -/** - * @template T - * @typedef {{ - * store: import('./stores/types').Store - * url?: URL, - * fetch?: typeof fetch - * }} AgentCreateOptions - */ +import { Signer } from '@ucanto/principal/ed25519' +import { invoke, delegate } from '@ucanto/core' +import { + isExpired, + isTooEarly, + validate, + canDelegateCapability, +} from './delegations.js' const HOST = 'https://access.web3.storage' @@ -41,18 +30,22 @@ const HOST = 'https://access.web3.storage' * @param {Ucanto.Principal} principal * @param {typeof fetch} _fetch * @param {URL} url - * @returns { Promise>} + * @param {Ucanto.Transport.Channel} [channel] + * @returns {Promise>} */ -export async function connection(principal, _fetch, url) { +export async function connection(principal, _fetch, url, channel) { + const _channel = + channel || + HTTP.open({ + url, + method: 'POST', + fetch: _fetch, + }) const connection = Client.connect({ id: principal, encoder: CAR, decoder: CBOR, - channel: HTTP.open({ - url, - method: 'POST', - fetch: _fetch, - }), + channel: _channel, }) return connection @@ -70,7 +63,7 @@ export class Agent { #fetch /** - * @param {AgentOptions} opts + * @param {import('./types').AgentOptions} opts */ constructor(opts) { this.url = opts.url || new URL(HOST) @@ -86,7 +79,7 @@ export class Agent { /** * @template {Ucanto.Signer} T - * @param {AgentCreateOptions} opts + * @param {import('./types').AgentCreateOptions} opts */ static async create(opts) { let _fetch = opts.fetch @@ -103,9 +96,12 @@ export class Agent { } } + if (!(await opts.store.exists())) { + throw new Error('Store is not initialized, run "Store.init()" first.') + } const data = await opts.store.load() return new Agent({ - connection: await connection(data.principal, _fetch, url), + connection: await connection(data.principal, _fetch, url, opts.channel), fetch: _fetch, url, store: opts.store, @@ -128,67 +124,243 @@ export class Agent { } /** + * Add a proof to the agent store + * + * A proof is a delegation with an audience matching agent DID + * + * @param {Ucanto.Delegation} delegation + */ + async addProof(delegation) { + validate(delegation, { + checkAudience: this.issuer, + checkIsExpired: true, + }) + + this.data.delegations.set(delegation.cid.toString(), { + delegation, + }) + + await this.store.save(this.data) + } + + /** + * Query the delegations store for all the delegations matching the capabilities provided. + * + * @param {import('@ucanto/interface').Capability[]} [caps] + */ + async *#delegations(caps) { + const _caps = new Set(caps) + for (const [key, value] of this.data.delegations) { + // check expiration + if (!isExpired(value.delegation)) { + // check if delegation can be used + if (!isTooEarly(value.delegation)) { + // check if we need to filter for caps + if (Array.isArray(caps) && caps.length > 0) { + for (const cap of _caps) { + if (canDelegateCapability(value.delegation, cap)) { + _caps.delete(cap) + yield value + } + } + } else { + yield value + } + } + } else { + // delete any expired delegation + this.data.delegations.delete(key) + } + } + + await this.store.save(this.data) + } + + /** + * Get all the proofs matching the capabilities + * + * Proofs are delegations with an audience matching agent DID. + * + * @param {import('@ucanto/interface').Capability[]} [caps] - Capabilities to filter by. Empty or undefined caps with return all the proofs. + */ + async proofs(caps) { + const arr = [] + for await (const value of this.#delegations(caps)) { + if (value.delegation.audience.did() === this.issuer.did()) { + arr.push(value.delegation) + } + } + + return arr + } + + /** + * Get delegations created by the agent for others. + * + * @param {import('@ucanto/interface').Capability[]} [caps] - Capabilities to filter by. Empty or undefined caps with return all the delegations. + */ + async *delegations(caps) { + for await (const { delegation } of this.delegationsWithMeta(caps)) { + yield delegation + } + } + + /** + * Get delegations created by the agent for others and their metadata. + * + * @param {import('@ucanto/interface').Capability[]} [caps] - Capabilities to filter by. Empty or undefined caps with return all the delegations. + */ + async *delegationsWithMeta(caps) { + for await (const value of this.#delegations(caps)) { + if (value.delegation.audience.did() !== this.issuer.did()) { + yield value + } + } + } + + /** + * Creates a space signer and a delegation to the agent + * + * @param {string} name + */ + async createSpace(name) { + const signer = await Signer.generate() + const proof = await Top.delegate({ + issuer: signer, + audience: this.issuer, + with: signer.did(), + expiration: Infinity, + }) + + this.data.spaces.set(signer.did(), { + name, + isRegistered: false, + }) + + await this.addProof(proof) + + return { + did: signer.did(), + proof, + } + } + + /** + * Sets the current selected space + * + * Other methods will default to use the current space if no resource is defined + * + * @param {Ucanto.DID} space + */ + async setCurrentSpace(space) { + const proofs = await this.proofs([ + { + can: 'space/info', + with: space, + }, + ]) + + if (proofs.length === 0) { + throw new Error(`Agent has no proofs for ${space}.`) + } + + this.data.currentSpace = space + await this.store.save(this.data) + + return space + } + + /** + * Get current space DID + */ + currentSpace() { + return this.data.currentSpace + } + + /** + * Get current space DID, proofs and abilities + */ + async currentSpaceWithMeta() { + if (!this.data.currentSpace) { + return + } + + // TODO cache these + const proofs = await this.proofs([ + { + can: 'space/info', + with: this.data.currentSpace, + }, + ]) + + const caps = new Set() + for (const p of proofs) { + for (const cap of p.capabilities) { + caps.add(cap.can) + } + } + + return { + did: this.data.currentSpace, + proofs, + capabilities: [...caps], + } + } + + /** + * 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] */ - async createAccount(email, opts) { - const account = await this.store.createAccount() + async registerSpace(email, opts) { + const space = this.currentSpace() const service = await this.service() - const delegationToAgent = await Any.delegate({ - issuer: account, - audience: this.issuer, - with: account.did(), - expiration: Infinity, - }) - const inv = await Voucher.claim - .invoke({ - issuer: this.issuer, - audience: service, - with: account.did(), - nb: { - identity: URI.from(`mailto:${email}`), - product: 'product:free', - service: service.did(), - }, - proofs: [delegationToAgent], - }) - .execute(this.connection) + if (!space) { + throw new Error('No space selected') + } + + 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('Account creation failed', { cause: inv.error }) + throw new Error('Voucher claim failed', { cause: inv }) } const voucherRedeem = await this.#waitForVoucherRedeem(opts) - // TODO save this delegation so we can revoke later - const delegationToService = await Any.delegate({ - issuer: account, + const delegationToService = await this.delegate({ + abilities: ['*'], audience: service, - with: account.did(), - expiration: Infinity, + audienceMeta: { + name: 'w3access', + type: 'service', + }, }) - const accInv = await Voucher.redeem - .invoke({ - issuer: this.data.principal, - audience: service, - with: service.did(), - nb: { - account: account.did(), - identity: voucherRedeem.capabilities[0].nb.identity, - product: voucherRedeem.capabilities[0].nb.product, - }, - proofs: [voucherRedeem, delegationToService], - }) - .execute(this.connection) + 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], + }) if (accInv && accInv.error) { - throw new Error('Account registration failed', { cause: accInv }) + throw new Error('Space registration failed', { cause: accInv }) } - this.data.delegations.addMany([voucherRedeem, delegationToAgent]) - this.data.accounts.push(account) - this.store.save(this.data) + + await this.addProof(voucherRedeem) } /** @@ -232,28 +404,161 @@ export class Agent { /** * - * @param {Ucanto.Principal} audience - * @param {import('@ipld/dag-ucan').Capabilities} capabilities - * @param {number} [lifetimeInSeconds] + * @param {import('./types').DelegationOptions} options */ - async delegate(audience, capabilities, lifetimeInSeconds) { - const delegation = await this.data.delegations.delegate( - audience, - capabilities, - lifetimeInSeconds + async delegate(options) { + const space = await this.currentSpaceWithMeta() + if (!space) { + throw new Error('there no space selected.') + } + + const caps = /** @type {Ucanto.Capabilities} */ ( + options.abilities.map((a) => { + return { + with: space.did, + can: a, + } + }) ) + const delegation = await delegate({ + issuer: this.issuer, + capabilities: caps, + proofs: await this.proofs(caps), + ...options, + }) + + this.data.delegations.set(delegation.cid.toString(), { + delegation, + meta: { + audience: options.audienceMeta, + }, + }) await this.store.save(this.data) return delegation } /** + * Invoke and execute the given capability on the Access service connection + * + * Sugar for : + * + * ```js + * + * await agent.invokeAndExecute(Space.recover, { + * nb: { + * identity: 'mailto: email@gmail.com', + * }, + * }) + * + * // sugar for + * const recoverInvocation = await agent.invoke(Space.recover, { + * nb: { + * identity: 'mailto: email@gmail.com', + * }, + * }) + * + * await recoverInvocation.execute(agent.connection) + * ``` + * + * @template {Ucanto.Ability} A + * @template {Ucanto.URI} R + * @template {Ucanto.TheCapabilityParser>} CAP + * @template {Ucanto.Caveats} [C={}] + * @param {CAP} cap + * @param {import('./types').InvokeOptions} options + */ + async invokeAndExecute(cap, options) { + const inv = await this.invoke(cap, options) + + // @ts-ignore + const out = inv.execute(this.connection) + + return /** @type {Promise, import('./types').Service>>} */ ( + out + ) + } + + /** + * Execute invocations on the agent's connection + * + * @example + * ```js + * const i1 = await agent.invoke(Space.info, {}) + * const i2 = await agent.invoke(Space.recover, { + * nb: { + * identity: 'mailto:hello@web3.storage', + * }, + * }) + * + * const results = await agent.execute2(i1, i2) * - * @param {import('@ucanto/interface').Delegation} delegation + * ``` + * @template {Ucanto.Capability} C + * @template {Ucanto.Tuple>} I + * @param {I} invocations */ - async addDelegation(delegation) { - await this.data.delegations.add(delegation) - await this.store.save(this.data) + execute(...invocations) { + return this.connection.execute(...invocations) + } + + /** + * Creates an invocation for the given capability with Agent's proofs, service, issuer and space. + * + * @example + * ```js + * const recoverInvocation = await agent.invoke(Space.recover, { + * nb: { + * identity: 'mailto: email@gmail.com', + * }, + * }) + * + * await recoverInvocation.execute(agent.connection) + * // or + * await agent.execute(recoverInvocation) + * ``` + * + * @template {Ucanto.Ability} A + * @template {Ucanto.URI} R + * @template {Ucanto.TheCapabilityParser>} CAP + * @template {Ucanto.Caveats} [C={}] + * @param {CAP} cap + * @param {import('./types').InvokeOptions} options + */ + async invoke(cap, options) { + const space = options.with || this.currentSpace() + if (!space) { + throw new Error('No space selected, you need pass a resource.') + } + + const proofs = await this.proofs([ + { + with: space, + can: cap.can, + }, + ]) + + if (proofs.length === 0) { + throw new Error( + `no proofs available for resource ${space} and ability ${cap.can}` + ) + } + + const extraProofs = options.proofs || [] + const inv = invoke({ + audience: options.audience || (await this.service()), + // @ts-ignore + capability: cap.create({ + with: space, + nb: options.nb, + }), + issuer: this.issuer, + proofs: [...proofs, ...extraProofs], + }) + + return /** @type {Ucanto.IssuedInvocationView>} */ ( + inv + ) } /** @@ -265,22 +570,18 @@ export class Agent { } /** - * @param {Ucanto.URI<"did:">} account + * Get Space information from Access service + * + * @param {Ucanto.URI<"did:">} [space] */ - async getAccountInfo(account) { - const proofs = isEmpty(this.data.delegations.getByResource(account)) - if (!proofs) { - throw new TypeError('No proofs for "account/info".') + async getSpaceInfo(space) { + const _space = space || this.currentSpace() + if (!_space) { + throw new Error('No space selected, you need pass a resource.') } - - const inv = await Account.info - .invoke({ - issuer: this.issuer, - audience: await this.service(), - with: account, - proofs, - }) - .execute(this.connection) + const inv = await this.invokeAndExecute(Space.info, { + with: _space, + }) if (inv.error) { throw inv @@ -289,15 +590,3 @@ export class Agent { return inv } } - -/** - * @template T - * @param { Array | undefined} arr - */ -function isEmpty(arr) { - if (!Array.isArray(arr) || arr.length === 0) { - return - } - - return /** @type {T[]} */ (arr) -} diff --git a/packages/access-client/src/awake/messages.js b/packages/access-client/src/awake/messages.js index a45c9d41d..948382c70 100644 --- a/packages/access-client/src/awake/messages.js +++ b/packages/access-client/src/awake/messages.js @@ -1,3 +1,8 @@ +/** + * @module awake-messages + * @packageDocumentation + * @ignore + */ import { z } from 'zod' export const MessageType = z.enum(['awake/init', 'awake/res', 'awake/msg']) diff --git a/packages/access-client/src/awake/peer.js b/packages/access-client/src/awake/peer.js index 9c4fc149d..7f7153805 100644 --- a/packages/access-client/src/awake/peer.js +++ b/packages/access-client/src/awake/peer.js @@ -146,7 +146,7 @@ export class Peer { await this.channel.sendFin(this.nextdid) - await this.agent.addDelegation(delegations[0]) + await this.agent.addProof(delegations[0]) return { delegation: delegations[0], meta: capsRsp.msg.meta } } @@ -158,12 +158,12 @@ export class Peer { // request caps /** @type {import('./types').LinkRequest} */ const reqCap = await this.channel.awaitMsg(this.nextdid) - const d = await this.agent.delegate( - this.audience, - [{ with: this.agent.did(), can: reqCap.msg.caps[0].can }], - 8_600_000 - ) - + const d = await this.agent.delegate({ + abilities: ['*'], // TODO should be derived from reqCap + audience: this.audience, + expiration: Infinity, + audienceMeta: reqCap.msg.meta, + }) this.channel.subscribe('awake/msg', (msg) => { this.channel.close() }) diff --git a/packages/access-client/src/capabilities/any.js b/packages/access-client/src/capabilities/any.js deleted file mode 100644 index 3be16db3c..000000000 --- a/packages/access-client/src/capabilities/any.js +++ /dev/null @@ -1,12 +0,0 @@ -import { capability, URI } from '@ucanto/validator' -import { equalWith } from './utils.js' - -/** - * Represents `{ can: '*', with: 'did:key:zAlice' }` capability, which we often - * also call account linking. - */ -export const any = capability({ - can: '*', - with: URI.match({ protocol: 'did:' }), - derives: equalWith, -}) diff --git a/packages/access-client/src/capabilities/account.js b/packages/access-client/src/capabilities/space.js similarity index 70% rename from packages/access-client/src/capabilities/account.js rename to packages/access-client/src/capabilities/space.js index bbcbd4399..f45237367 100644 --- a/packages/access-client/src/capabilities/account.js +++ b/packages/access-client/src/capabilities/space.js @@ -1,27 +1,38 @@ -import { capability, URI } from '@ucanto/server' -import { any } from './any.js' +/** + * Space Capabilities + * + * These can be imported directly with: + * ```js + * import * as Space from '@web3-storage/access/capabilities/space' + * ``` + * + * @module + */ + +import { top } from './top.js' import { store } from './store.js' +import { capability, URI } from '@ucanto/validator' import { canDelegateURI, equalWith, fail } from './utils.js' -export const account = any.derive({ +export const space = top.derive({ to: capability({ - can: 'account/*', + can: 'space/*', with: URI.match({ protocol: 'did:' }), derives: equalWith, }), derives: equalWith, }) -const base = any.or(account) +const base = top.or(space) /** - * `account/info` can be derived from any of the `store/*` + * `space/info` can be derived from any of the `store/*` * capability that has matching `with`. This allows store service * to identify account based on any user request. */ export const info = base.or(store).derive({ to: capability({ - can: 'account/info', + can: 'space/info', with: URI.match({ protocol: 'did:' }), derives: equalWith, }), @@ -30,7 +41,7 @@ export const info = base.or(store).derive({ export const recoverValidation = base.derive({ to: capability({ - can: 'account/recover-validation', + can: 'space/recover-validation', with: URI.match({ protocol: 'did:' }), nb: { identity: URI.match({ protocol: 'mailto:' }), @@ -42,7 +53,7 @@ export const recoverValidation = base.derive({ export const recover = base.derive({ to: capability({ - can: 'account/recover', + can: 'space/recover', with: URI.match({ protocol: 'did:' }), nb: { identity: URI.match({ protocol: 'mailto:' }), diff --git a/packages/access-client/src/capabilities/store.js b/packages/access-client/src/capabilities/store.js index 2c7153cdf..934cbf666 100644 --- a/packages/access-client/src/capabilities/store.js +++ b/packages/access-client/src/capabilities/store.js @@ -1,13 +1,23 @@ +/** + * Store Capabilities + * + * These can be imported directly with: + * ```js + * import * as Account from '@web3-storage/access/capabilities/store' + * ``` + * + * @module + */ import { capability, Failure, Link, URI, Schema } from '@ucanto/validator' import { equalLink, equalWith } from './utils.js' -import { any } from './any.js' +import { top } from './top.js' /** * Capability can only be delegated (but not invoked) allowing audience to * derived any `store/` prefixed capability for the (memory) space identified * by did:key in the `with` field. */ -export const store = any.derive({ +export const store = top.derive({ to: capability({ can: 'store/*', /** @@ -29,7 +39,7 @@ export const store = any.derive({ // derived from `store/*`. As a workaround we just define base capability // here so all store capabilities could be derived from either `*` or // `store/*`. -const base = any.or(store) +const base = top.or(store) /** * `store/add` capability allows agent to store a CAR file into a (memory) space diff --git a/packages/access-client/src/capabilities/top.js b/packages/access-client/src/capabilities/top.js new file mode 100644 index 000000000..3e3f7ac7c --- /dev/null +++ b/packages/access-client/src/capabilities/top.js @@ -0,0 +1,25 @@ +/** + * Top Capabilities + * + * These can be imported directly with: + * ```js + * import * as Account from '@web3-storage/access/capabilities/top' + * ``` + * + * @module + */ + +import { capability, URI } from '@ucanto/validator' +import { equalWith } from './utils.js' + +/** + * Represents the top `{ can: '*', with: 'did:key:zAlice' }` capability, which we often + * also call account linking. + * + * @see {@link https://github.com/ucan-wg/spec#52-top} + */ +export const top = capability({ + can: '*', + with: URI.match({ protocol: 'did:' }), + derives: equalWith, +}) diff --git a/packages/access-client/src/capabilities/types.ts b/packages/access-client/src/capabilities/types.ts index ce5d033eb..3db23a629 100644 --- a/packages/access-client/src/capabilities/types.ts +++ b/packages/access-client/src/capabilities/types.ts @@ -1,17 +1,18 @@ import { InferInvokedCapability } from '@ucanto/interface' -import { account, info, recover, recoverValidation } from './account.js' -import { any } from './any.js' -import { add, list, remove } from './store.js' +import { space, info, recover, recoverValidation } from './space.js' +import { top } from './top.js' +import { add, list, remove, store } from './store.js' import * as UploadCaps from './upload.js' import { claim, redeem } from './voucher.js' -// Account -export type Account = InferInvokedCapability -export type AccountInfo = InferInvokedCapability -export type AccountRecoverValidation = InferInvokedCapability< +// Space +export type Space = InferInvokedCapability +export type SpaceInfo = InferInvokedCapability +export type SpaceRecoverValidation = InferInvokedCapability< typeof recoverValidation > -export type AccountRecover = InferInvokedCapability +export type SpaceRecover = InferInvokedCapability + // Voucher Protocol export type VoucherRedeem = InferInvokedCapability export type VoucherClaim = InferInvokedCapability @@ -21,8 +22,26 @@ export type UploadAdd = InferInvokedCapability export type UploadRemove = InferInvokedCapability export type UploadList = InferInvokedCapability // Store +export type Store = InferInvokedCapability export type StoreAdd = InferInvokedCapability export type StoreRemove = InferInvokedCapability export type StoreList = InferInvokedCapability -// Any -export type Any = InferInvokedCapability +// Top +export type Top = InferInvokedCapability + +export type Abilities = + | Space['can'] + | SpaceInfo['can'] + | SpaceRecover['can'] + | SpaceRecoverValidation['can'] + | VoucherClaim['can'] + | VoucherRedeem['can'] + | Upload['can'] + | UploadAdd['can'] + | UploadRemove['can'] + | UploadList['can'] + | Store['can'] + | StoreAdd['can'] + | StoreRemove['can'] + | StoreList['can'] + | Top['can'] diff --git a/packages/access-client/src/capabilities/upload.js b/packages/access-client/src/capabilities/upload.js index 5f589bb9c..eb1f25250 100644 --- a/packages/access-client/src/capabilities/upload.js +++ b/packages/access-client/src/capabilities/upload.js @@ -1,14 +1,24 @@ +/** + * Upload Capabilities + * + * These can be imported directly with: + * ```js + * import * as Account from '@web3-storage/access/capabilities/upload' + * ``` + * + * @module + */ import { capability, Link, URI } from '@ucanto/validator' import { codec as CAR } from '@ucanto/transport/car' import { equalWith, fail, equal } from './utils.js' -import { any } from './any.js' +import { top } from './top.js' /** * Capability can only be delegated (but not invoked) allowing audience to * derived any `upload/` prefixed capability for the (memory) space identified * by did:key in the `with` field. */ -export const upload = any.derive({ +export const upload = top.derive({ to: capability({ can: 'upload/*', /** @@ -30,7 +40,7 @@ export const upload = any.derive({ // derived from `upload/*`. As a workaround we just define base capability // here so all store capabilities could be derived from either `*` or // `upload/*`. -const base = any.or(upload) +const base = top.or(upload) /** * Schema representing a link (a.k.a CID) to a CAR file. Enforces CAR codec code and CID v1. diff --git a/packages/access-client/src/capabilities/utils.js b/packages/access-client/src/capabilities/utils.js index 731cfda1f..2b984be01 100644 --- a/packages/access-client/src/capabilities/utils.js +++ b/packages/access-client/src/capabilities/utils.js @@ -12,7 +12,6 @@ export function canDelegateURI(child, parent) { if (parent === undefined) { return true } - if (child !== undefined && parent.endsWith('*')) { return child.startsWith(parent.slice(0, -1)) ? true @@ -89,3 +88,55 @@ export const equalLink = (claimed, delegated) => { export function fail(value) { return value === true ? undefined : value } + +/** + * + * @param {import('@ucanto/interface').Ability} ability + */ +function parseAbility(ability) { + const [namespace, ...segments] = ability.split('/') + return { namespace, segments } +} + +/** + * + * TODO: needs to account for caps derived from diferent namespaces like 'account/info' can be derived from 'store/add' + * + * @param {import('@ucanto/interface').Ability} parent + * @param {import('@ucanto/interface').Ability} child + */ +export function canDelegateAbility(parent, child) { + const parsedParent = parseAbility(parent) + const parsedChild = parseAbility(child) + + // Parent is wildcard + if (parsedParent.namespace === '*' && parsedParent.segments.length === 0) { + return true + } + + // Child is wild card so it can not be delegated from anything + if (parsedChild.namespace === '*' && parsedChild.segments.length === 0) { + return false + } + + // namespaces dont match + if (parsedParent.namespace !== parsedChild.namespace) { + return false + } + + // given that namespaces match and parent first segment is wildcard + if (parsedParent.segments[0] === '*') { + return true + } + + // Array equality + if (parsedParent.segments.length !== parsedChild.segments.length) { + return false + } + + // all segments must match + return parsedParent.segments.reduce( + (acc, v, i) => acc && parsedChild.segments[i] === v, + true + ) +} diff --git a/packages/access-client/src/capabilities/voucher.js b/packages/access-client/src/capabilities/voucher.js index 26d668cd9..786ae9cf2 100644 --- a/packages/access-client/src/capabilities/voucher.js +++ b/packages/access-client/src/capabilities/voucher.js @@ -1,6 +1,19 @@ +/** + * Voucher Capabilities + * + * These can be imported directly with: + * ```js + * import * as Account from '@web3-storage/access/capabilities/voucher' + * ``` + * + * @module + */ import { capability, URI, DID } from '@ucanto/validator' -import { equalWith, equal, fail } from './utils.js' -import { any } from './any.js' +// @ts-ignore +// eslint-disable-next-line no-unused-vars +import * as Types from '@ucanto/interface' +import { equalWith, fail, equal } from './utils.js' +import { top } from './top.js' /** * Products are identified by the CID of the DAG that describes them. @@ -26,7 +39,7 @@ export const Service = DID.match({ method: 'key' }) * Currently DID in the `with` field will always be web3.storage DID since we * do not support other types of vouchers yet. */ -export const voucher = any.derive({ +export const voucher = top.derive({ to: capability({ can: 'voucher/*', with: URI.match({ protocol: 'did:' }), @@ -35,7 +48,7 @@ export const voucher = any.derive({ derives: equalWith, }) -const base = any.or(voucher) +const base = top.or(voucher) /** * Capability can be invoked by an agent to claim a voucher for a specific @@ -99,16 +112,16 @@ export const redeem = voucher.derive({ /** * Space identifier where voucher can be redeemed. When service delegates * `voucher/redeem` to the user agent it may omit this field to allow - * account to choose account. + * agent to choose space. */ - account: URI.match({ protocol: 'did:' }), + space: URI.match({ protocol: 'did:' }), }, derives: (child, parent) => { return ( fail(equalWith(child, parent)) || fail(equal(child.nb.product, parent.nb.product, 'product')) || fail(equal(child.nb.identity, parent.nb.identity, 'identity')) || - fail(equal(child.nb.account, parent.nb.account, 'account')) || + fail(equal(child.nb.space, parent.nb.space, 'account')) || true ) }, diff --git a/packages/access-client/src/cli/cmd-create-account.js b/packages/access-client/src/cli/cmd-create-account.js index 0fa30d63a..763c11a18 100644 --- a/packages/access-client/src/cli/cmd-create-account.js +++ b/packages/access-client/src/cli/cmd-create-account.js @@ -29,7 +29,7 @@ export async function cmdCreateAccount(opts) { }) spinner.start('Waiting for email validation...') try { - await agent.createAccount(email) + await agent.registerSpace(email) spinner.succeed('Account has been created and register with the service.') } catch (error) { console.error(error) diff --git a/packages/access-client/src/cli/cmd-whoami.js b/packages/access-client/src/cli/cmd-whoami.js index 2526a34c4..f3c5c3a14 100644 --- a/packages/access-client/src/cli/cmd-whoami.js +++ b/packages/access-client/src/cli/cmd-whoami.js @@ -1,33 +1,31 @@ /* eslint-disable no-console */ -import { StoreConf } from '../stores/store-conf.js' -import { NAME } from './config.js' +// import { StoreConf } from '../stores/store-conf.js' +// import { NAME } from './config.js' /** * @param {{ profile: any; env : string }} opts */ export async function cmdWhoami(opts) { - const store = new StoreConf({ profile: opts.profile }) - if (await store.exists()) { - const { delegations, meta, accounts, principal } = await store.load() - - console.log('Agent', principal.did(), meta) - console.log('Accounts:') - for (const acc of accounts) { - console.log(acc.did()) - } - - console.log('Delegations created:') - for (const created of delegations.created) { - console.log(created) - } - console.log('Delegations received:') - for (const [key, value] of delegations.receivedByResource) { - console.log( - `Resource: ${key}`, - value.map((cap) => cap.cap.can) - ) - } - } else { - console.error(`Run "${NAME} setup" first`) - } + // const store = new StoreConf({ profile: opts.profile }) + // if (await store.exists()) { + // const { delegations, meta, accounts, principal } = await store.load() + // console.log('Agent', principal.did(), meta) + // console.log('Accounts:') + // for (const acc of accounts) { + // console.log(acc.did()) + // } + // console.log('Delegations created:') + // for (const created of delegations.created) { + // console.log(created) + // } + // console.log('Delegations received:') + // for (const [key, value] of delegations.receivedByResource) { + // console.log( + // `Resource: ${key}`, + // value.map((cap) => cap.cap.can) + // ) + // } + // } else { + // console.error(`Run "${NAME} setup" first`) + // } } diff --git a/packages/access-client/src/cli/index.js b/packages/access-client/src/cli/index.js index 68cddc656..878c8db56 100755 --- a/packages/access-client/src/cli/index.js +++ b/packages/access-client/src/cli/index.js @@ -1,12 +1,8 @@ #!/usr/bin/env node /* eslint-disable no-console */ import fs from 'fs' -import ora from 'ora' -import path from 'path' import sade from 'sade' -import { Transform } from 'stream' -import undici from 'undici' -import { getConfig, NAME, pkg } from './config.js' +import { NAME, pkg } from './config.js' import { getService } from './utils.js' // @ts-ignore // eslint-disable-next-line no-unused-vars @@ -16,9 +12,10 @@ import { cmdSetup } from './cmd-setup.js' import { cmdWhoami } from './cmd-whoami.js' import { StoreConf } from '../stores/store-conf.js' import { Agent } from '../agent.js' -import inquirer from 'inquirer' -import { Verifier } from '@ucanto/principal/ed25519' -import { delegationToString, stringToDelegation } from '../encoding.js' +import { stringToDelegation } from '../encoding.js' +// import inquirer from 'inquirer' +// import { Verifier } from '@ucanto/principal/ed25519' +// import { delegationToString, stringToDelegation } from '../encoding.js' const prog = sade(NAME) prog @@ -26,45 +23,6 @@ prog .option('-p, --profile', 'Select the config profile to use.', 'main') .option('--env', 'Env', 'production') -prog - .command('upload ') - .describe("Register with the service using config's keypair.") - .action(async (file, opts) => { - const config = getConfig(opts.profile) - const { url } = await getService(opts.env) - const spinner = ora('Registering with the service').start() - try { - if (!config.get('private-key')) { - spinner.fail( - `You dont have a private key saved yet, run "${NAME} init"` - ) - process.exit(1) - } - - const stream = fs.createReadStream(path.resolve(file)) - const checkStream = new Transform({ - transform(chunk, encoding, callback) { - console.log(chunk.length) - callback(undefined, chunk) - }, - }) - - const rsp = await undici.fetch(`${url}upload`, { - body: stream.pipe(checkStream), - method: 'POST', - }) - - console.log(await rsp.text()) - - spinner.succeed('Registration done.') - } catch (error) { - console.error(error) - // @ts-ignore - spinner.fail(error.message) - process.exit(1) - } - }) - prog.command('link [channel]').describe('Link.').action(cmdLink) prog .command('setup') @@ -81,111 +39,103 @@ prog .command('account') .describe('Account info.') .action(async (opts) => { - const store = new StoreConf({ profile: opts.profile }) - const { url } = await getService(opts.env) - if (await store.exists()) { - const agent = await Agent.create({ - store, - url, - }) - - const choices = [] - for (const [key, value] of agent.data.delegations.receivedByResource) { - for (const d of value) { - if (d.cap.can === 'account/info' || d.cap.can === 'account/*') { - choices.push({ name: key }) - } - } - } - - const { account } = await inquirer.prompt([ - { - type: 'list', - name: 'account', - default: 'device', - choices, - message: 'Select account:', - }, - ]) - try { - const result = await agent.getAccountInfo(account) - console.log(result) - } catch (error_) { - const error = /** @type {Error} */ (error_) - console.log(error.message) - } - } else { - console.error(`Run "${NAME} setup" first`) - } + // const store = new StoreConf({ profile: opts.profile }) + // const { url } = await getService(opts.env) + // if (await store.exists()) { + // const agent = await Agent.create({ + // store, + // url, + // }) + // const choices = [] + // for (const [key, value] of agent.data.delegations.receivedByResource) { + // for (const d of value) { + // if (d.cap.can === 'account/info' || d.cap.can === 'account/*') { + // choices.push({ name: key }) + // } + // } + // } + // const { account } = await inquirer.prompt([ + // { + // type: 'list', + // name: 'account', + // default: 'device', + // choices, + // message: 'Select account:', + // }, + // ]) + // try { + // const result = await agent.getAccountInfo(account) + // console.log(result) + // } catch (error_) { + // const error = /** @type {Error} */ (error_) + // console.log(error.message) + // } + // } else { + // console.error(`Run "${NAME} setup" first`) + // } }) prog .command('delegate') .describe('Delegation capabilities.') .action(async (opts) => { - const store = new StoreConf({ profile: opts.profile }) - const { url } = await getService(opts.env) - if (await store.exists()) { - const agent = await Agent.create({ - store, - url, - }) - - const accountDids = agent.data.accounts.map((acc) => { - return { name: acc.did() } - }) - const { account } = await inquirer.prompt([ - { - type: 'list', - name: 'account', - choices: accountDids, - message: 'Select account:', - }, - ]) - - const abilities = [] - for (const [key, values] of agent.data.delegations.receivedByResource) { - if (key === account) { - for (const cap of values) { - abilities.push({ name: cap.cap.can }) - } - } - } - const { ability } = await inquirer.prompt([ - { - type: 'list', - name: 'ability', - choices: abilities, - message: 'Select ability:', - }, - ]) - - const { audience } = await inquirer.prompt([ - { - type: 'input', - name: 'audience', - choices: abilities, - message: 'Input audience:', - }, - ]) - - console.log(account, ability) - - const delegation = await agent.delegate( - Verifier.parse(audience), - [ - { - can: ability, - with: account, - }, - ], - 800_000 - ) - - console.log(await delegationToString(delegation)) - } else { - console.error(`Run "${NAME} setup" first`) - } + // const store = new StoreConf({ profile: opts.profile }) + // const { url } = await getService(opts.env) + // if (await store.exists()) { + // const agent = await Agent.create({ + // store, + // url, + // }) + // const accountDids = agent.data.accounts.map((acc) => { + // return { name: acc.did() } + // }) + // const { account } = await inquirer.prompt([ + // { + // type: 'list', + // name: 'account', + // choices: accountDids, + // message: 'Select account:', + // }, + // ]) + // const abilities = [] + // for (const [key, values] of agent.data.delegations.receivedByResource) { + // if (key === account) { + // for (const cap of values) { + // abilities.push({ name: cap.cap.can }) + // } + // } + // } + // const { ability } = await inquirer.prompt([ + // { + // type: 'list', + // name: 'ability', + // choices: abilities, + // message: 'Select ability:', + // }, + // ]) + // const { audience } = await inquirer.prompt([ + // { + // type: 'input', + // name: 'audience', + // choices: abilities, + // message: 'Input audience:', + // }, + // ]) + // console.log(account, ability) + // const delegation = await agent.delegate( + // Verifier.parse(audience), + // [ + // { + // can: ability, + // with: account, + // }, + // ], + // 800_000 + // ) + // console.log(await delegationToString(delegation)) + // } else { + // console.error(`Run "${NAME} setup" first`) + // } }) prog @@ -203,7 +153,7 @@ prog const del = fs.readFileSync('./delegation', { encoding: 'utf8' }) - await agent.addDelegation(await stringToDelegation(del)) + await agent.addProof(await stringToDelegation(del)) } else { console.error(`Run "${NAME} setup" first`) } diff --git a/packages/access-client/src/cli/utils.js b/packages/access-client/src/cli/utils.js index 329f6cb52..8f67174d1 100644 --- a/packages/access-client/src/cli/utils.js +++ b/packages/access-client/src/cli/utils.js @@ -1,4 +1,3 @@ -import undici from 'undici' import { Verifier } from '@ucanto/principal/ed25519' /** @type {Record} */ @@ -22,7 +21,7 @@ export async function getService(env) { if (audience) { return { url, did: audience } } else { - const rsp = await undici.fetch(url + 'version') + const rsp = await fetch(url + 'version') // @ts-ignore const { did } = await rsp.json() diff --git a/packages/access-client/src/delegations.js b/packages/access-client/src/delegations.js index 526f898e1..551d5cc63 100644 --- a/packages/access-client/src/delegations.js +++ b/packages/access-client/src/delegations.js @@ -1,103 +1,74 @@ -import { delegate } from '@ucanto/core' // @ts-ignore // eslint-disable-next-line no-unused-vars import * as Ucanto from '@ucanto/interface' +import { canDelegateAbility } from './capabilities/utils.js' /** - * TODO: clear expired delegations + * + * @param {Ucanto.Delegation} delegation */ -export class Delegations { - /** - * @param {{ - * principal: Ucanto.Signer; - * received?: Ucanto.Delegation[] - * created?: Ucanto.Delegation[] - * meta?: import('./awake/types').MetaMap - * }} opts - */ - constructor(opts) { - this.principal = opts.principal - - /** @type {Ucanto.Delegation[]} */ - this.received = opts.received || [] - - /** @type {Ucanto.Delegation[]} */ - this.created = opts.created || [] - - /** @type {import('./awake/types').MetaMap} */ - this.meta = new Map() - - /** - * @type {Map} - */ - this.receivedByResource = new Map() - /** - * @type {Map} - */ - this.receivedMap = new Map() +export function isExpired(delegation) { + if ( + delegation.expiration === undefined || + delegation.expiration <= Math.floor(Date.now() / 1000) + ) { + return true } + return false +} - /** - * - * @param {Ucanto.Delegation} delegation - */ - async add(delegation) { - const cid = delegation.cid.toString() - - for (const cap of delegation.capabilities) { - const byResource = this.receivedByResource.get(cap.with) ?? [] - - byResource.push({ cid: delegation.cid.toString(), cap }) - this.receivedByResource.set(cap.with, byResource) - } - this.received.push(delegation) - - this.receivedMap.set(cid, delegation) +/** + * + * @param {Ucanto.Delegation} delegation + */ +export function isTooEarly(delegation) { + if (!delegation.notBefore) { + return false } + return delegation.notBefore > Math.floor(Date.now() / 1000) +} - /** - * @param {string} resource - */ - getByResource(resource) { - const byResource = this.receivedByResource.get(resource) - if (!byResource) { - return - } +/** + * + * @param {Ucanto.Delegation} delegation + * @param {object} [opts] + * @param {Ucanto.Principal} [opts.checkAudience] + * @param {boolean} [opts.checkIsExpired] + * @param {boolean} [opts.checkIsTooEarly] + */ +export function validate(delegation, opts) { + const { + checkAudience, + checkIsExpired = true, + checkIsTooEarly = true, + } = opts ?? {} - return byResource.map((r) => { - return this.receivedMap.get(r.cid) - }) + if (checkAudience && delegation.audience.did() !== checkAudience.did()) { + throw new Error(`Delegation audience does not match required DID.`) } - /** - * Add multiple received delegations - * - * @param {Ucanto.Delegation[]} delegations - */ - async addMany(delegations) { - for (const d of delegations) { - this.add(d) - } + if (checkIsExpired && isExpired(delegation)) { + throw new Error(`Delegation expired.`) } - /** - * - * @param {import('@ucanto/interface').Principal} audience - * @param {import('@ipld/dag-ucan').Capabilities} capabilities - * @param {number} [lifetimeInSeconds] - */ - async delegate(audience, capabilities, lifetimeInSeconds) { - const delegation = await delegate({ - issuer: this.principal, - // @ts-ignore - audience, - capabilities, - lifetimeInSeconds, - // be smarter about picking only the needs delegations - proofs: [...this.receivedMap.values()], - }) + if (checkIsTooEarly && isTooEarly(delegation)) { + throw new Error(`Delegation is not active yet (too early).`) + } +} - this.created.push(delegation) - return delegation +/** + * + * @param {import('@ucanto/interface').Delegation} delegation + * @param {import('@ucanto/interface').Capability} child + */ +export function canDelegateCapability(delegation, child) { + for (const parent of delegation.capabilities) { + if ( + parent.with === child.with && + canDelegateAbility(parent.can, child.can) + ) { + return true + } } + return false } diff --git a/packages/access-client/src/encoding.js b/packages/access-client/src/encoding.js index fa8812525..84109c343 100644 --- a/packages/access-client/src/encoding.js +++ b/packages/access-client/src/encoding.js @@ -1,3 +1,17 @@ +/** + * Encoding utilities + * + * These can be imported directly with: + * ```js + * import * as Encoding from '@web3-storage/access/encoding' + * + * // or + * + * import { encodeDelegations } from '@web3-storage/access/encoding' + * ``` + * + * @module + */ /* eslint-disable unicorn/prefer-spread */ import { CarReader } from '@ipld/car/reader' import { CarWriter } from '@ipld/car/writer' diff --git a/packages/access-client/src/index.js b/packages/access-client/src/index.js index a858dcaa6..99fb59e81 100644 --- a/packages/access-client/src/index.js +++ b/packages/access-client/src/index.js @@ -1 +1,12 @@ +/* eslint-disable jsdoc/check-tag-names */ export * from './agent.js' + +// Workaround for typedoc until 0.24 support export maps +export * as Space from './capabilities/space.js' +export * as Top from './capabilities/top.js' +export * as Store from './capabilities/store.js' +export * as Upload from './capabilities/upload.js' +export * as Voucher from './capabilities/voucher.js' +export * as Encoding from './encoding.js' +export { StoreConf } from './stores/store-conf.js' +export { StoreIndexedDB } from './stores/store-indexeddb.js' diff --git a/packages/access-client/src/stores/store-conf.js b/packages/access-client/src/stores/store-conf.js index 6782a0ef9..e66f10f03 100644 --- a/packages/access-client/src/stores/store-conf.js +++ b/packages/access-client/src/stores/store-conf.js @@ -1,12 +1,23 @@ +/* eslint-disable jsdoc/check-indentation */ import Conf from 'conf' import { Signer } from '@ucanto/principal/ed25519' -import { Delegations } from '../delegations.js' -import { decodeDelegations, encodeDelegations } from '../encoding.js' +import { delegationToString, stringToDelegation } from '../encoding.js' +// eslint-disable-next-line no-unused-vars +import * as Ucanto from '@ucanto/interface' /** - * @typedef {import('./types').DelegationsAsJSON} DelegationsAsJSON - * @typedef {import('./types').StoreDataKeyEd} StoreData - * @typedef {import('./types').StoreKeyEd} Store + * @typedef {import('../types').AgentData} StoreData + * @typedef {import('./types').IStore} Store + * @typedef {{ + * meta: import('../types.js').AgentMeta + * principal: string + * currentSpace?: Ucanto.DID + * spaces: Array<[Ucanto.DID, import('../types').SpaceMeta]> + * delegations: Array<[import('../types').CIDString, { + * meta?: import('../types').DelegationMeta, + * delegation: import('../types.js').EncodedDelegation + * }]> + * }} Data */ /** @@ -15,6 +26,9 @@ import { decodeDelegations, encodeDelegations } from '../encoding.js' * @implements {Store} */ export class StoreConf { + /** + * @type {Conf} + */ #config /** * @@ -31,6 +45,10 @@ export class StoreConf { this.path = this.#config.path } + /** + * + * @returns {Promise} + */ async open() { return this } @@ -48,17 +66,14 @@ export class StoreConf { /** @type {Store['init']} */ async init(data) { const principal = data.principal || (await Signer.generate()) - const delegations = - data.delegations || - new Delegations({ - principal, - }) + /** @type {StoreData} */ const storeData = { - accounts: data.accounts || [], meta: data.meta || { name: 'agent', type: 'device' }, + spaces: data.spaces || new Map(), + delegations: data.delegations || new Map(), principal, - delegations, + currentSpace: data.currentSpace, } await this.save(storeData) @@ -68,119 +83,55 @@ export class StoreConf { /** * * @param {StoreData} data + * @returns {Promise} */ async save(data) { - this.setAccounts(data.accounts) - this.setDelegations(data.delegations) - this.setMeta(data.meta) - this.setPrincipal(data.principal) - return this - } - - /** @type {Store['load']} */ - async load() { - /** @type {StoreData} */ - return { - accounts: await this.getAccounts(), - meta: await this.getMeta(), - principal: await this.getPrincipal(), - delegations: await this.getDelegations(), + /** @type {Data['delegations']} */ + const dels = [] + + for (const [key, value] of data.delegations) { + dels.push([ + key, + { + meta: value.meta, + delegation: await delegationToString(value.delegation), + }, + ]) } - } - - async createAccount() { - return await Signer.generate() - } - - /** - * - * @param {import('../types').AgentMeta} meta - */ - async setMeta(meta) { - this.#config.set('meta', meta) - return meta - } - - /** - * @param {Signer.EdSigner} [principal] - */ - async setPrincipal(principal) { - let signer = principal - if (!signer) { - signer = await Signer.generate() - } - this.#config.set('principal', Signer.format(signer)) - return signer - } - - async getPrincipal() { - const raw = this.#config.get('principal') - return Signer.parse(/** @type {string} */ (raw)) - } - - async getMeta() { - const raw = this.#config.get('meta') - - return /** @type {import('../types').AgentMeta} */ (raw) - } - - /** - * @param {Delegations} delegations - */ - async setDelegations(delegations) { - const data = { - created: await encodeDelegations(delegations.created), - received: await encodeDelegations(delegations.received), - meta: [...delegations.meta.entries()], + /** @type {Data} */ + const encodedData = { + currentSpace: data.currentSpace, + spaces: [...data.spaces.entries()], + meta: data.meta, + principal: Signer.format(data.principal), + delegations: dels, } - this.#config.set('delegations', data) - - return delegations - } + this.#config.set(encodedData) - async getDelegations() { - const data = /** @type {DelegationsAsJSON} */ ( - this.#config.get('delegations') - ) - const delegations = new Delegations({ - principal: await this.getPrincipal(), - created: await decodeDelegations(data.created || ''), - meta: new Map(data.meta), - }) - - await delegations.addMany(await decodeDelegations(data.received || '')) - - return delegations + return this } - /** - * - * @param {Signer.EdSigner[]} accounts - */ - async setAccounts(accounts) { - const encoded = [] - for (const acc of accounts) { - encoded.push(Signer.format(acc)) - } - - this.#config.set('accounts', encoded) - - return accounts - } + /** @type {Store['load']} */ + async load() { + const data = this.#config.store - async getAccounts() { - const encoded = /** @type {string[]} */ (this.#config.get('accounts')) - /** @type {Signer.EdSigner[]} */ - const accounts = [] + /** @type {StoreData['delegations']} */ + const dels = new Map() - if (!Array.isArray(encoded)) { - return accounts + for (const [key, value] of data.delegations) { + dels.set(key, { + delegation: await stringToDelegation(value.delegation), + meta: value.meta, + }) } - for (const acc of encoded) { - accounts.push(Signer.parse(acc)) + /** @type {StoreData} */ + return { + principal: Signer.parse(data.principal), + currentSpace: data.currentSpace, + meta: data.meta, + spaces: new Map(data.spaces), + delegations: dels, } - - return accounts } } diff --git a/packages/access-client/src/stores/store-indexeddb.js b/packages/access-client/src/stores/store-indexeddb.js index 0dff6ee04..5e227ca81 100644 --- a/packages/access-client/src/stores/store-indexeddb.js +++ b/packages/access-client/src/stores/store-indexeddb.js @@ -1,11 +1,10 @@ import { importDAG } from '@ucanto/core/delegation' import * as Signer from '@ucanto/principal/rsa' import defer from 'p-defer' -import { Delegations } from '../delegations.js' /** - * @typedef {import('./types').StoreDataKeyRSA} StoreData - * @typedef {import('./types').StoreKeyRSA} Store + * @typedef {import('../types').AgentData} StoreData + * @typedef {import('./types').IStore} Store */ const STORE_NAME = 'AccessStore' @@ -41,6 +40,10 @@ export class StoreIndexedDB { this.#dbStoreName = options.dbStoreName ?? STORE_NAME } + /** + * + * @returns {Promise} + */ async open() { /** @type {import('p-defer').DeferredPromise} */ const { resolve, reject, promise } = defer() @@ -102,6 +105,7 @@ export class StoreIndexedDB { * @param {object} [options] * @param {number} [options.dbVersion] * @param {string} [options.dbStoreName] + * @returns {Promise} */ static async create(dbName, options) { const store = new StoreIndexedDB(dbName, options) @@ -112,21 +116,24 @@ export class StoreIndexedDB { /** @type {Store['init']} */ async init(data) { - const principal = - data.principal || (await Signer.generate({ extractable: false })) - const delegations = data.delegations || new Delegations({ principal }) + /** @type {StoreData} */ const storeData = { - accounts: data.accounts || [], meta: data.meta || { name: 'agent', type: 'device' }, - principal, - delegations, + principal: + data.principal || (await Signer.generate({ extractable: false })), + spaces: data.spaces || new Map(), + delegations: data.delegations || new Map(), + currentSpace: data.currentSpace, } await this.save(storeData) return storeData } - /** @param {StoreData} data */ + /** + * @param {StoreData} data + * @returns {Promise} + */ async save(data) { const db = this.#db if (!db) throw new Error('Store is not open') @@ -139,16 +146,24 @@ export class StoreIndexedDB { /** @type {import('p-defer').DeferredPromise} */ const { resolve, reject, promise } = defer() + const dels = [] + + for (const [key, value] of data.delegations) { + dels.push([ + key, + { + meta: value.meta, + delegation: [...value.delegation.export()], + }, + ]) + } const putReq = store.put({ id: DATA_ID, - accounts: data.accounts.map((a) => a.toArchive()), - delegations: { - created: data.delegations.created.map((d) => [...d.export()]), - received: data.delegations.received.map((d) => [...d.export()]), - meta: [...data.delegations.meta.entries()], - }, meta: data.meta, principal: data.principal.toArchive(), + currentSpace: data.currentSpace, + spaces: data.spaces, + delegations: dels, }) putReq.addEventListener('success', () => resolve(this)) putReq.addEventListener('error', () => @@ -178,25 +193,27 @@ export class StoreIndexedDB { const getReq = store.get(DATA_ID) getReq.addEventListener('success', () => { try { - /** @type {import('./types').IDBStoreData} */ + /** @type {import('./types').StoreDataIDB} */ const raw = getReq.result if (!raw) throw new Error('Store is not initialized') - const principal = Signer.from(raw.principal) + /** @type {StoreData['delegations']} */ + const dels = new Map() + + for (const [key, value] of raw.delegations) { + dels.set(key, { + delegation: importDAG(value.delegation), + meta: value.meta, + }) + } + + /** @type {StoreData} */ const data = { - accounts: raw.accounts.map((a) => Signer.from(a)), - delegations: new Delegations({ - principal, - received: raw.delegations.received.map((blocks) => - importDAG(blocks) - ), - created: raw.delegations.created.map((blocks) => - importDAG(blocks) - ), - meta: new Map(raw.delegations.meta), - }), meta: raw.meta, - principal, + principal: Signer.from(raw.principal), + currentSpace: raw.currentSpace, + spaces: raw.spaces, + delegations: dels, } resolve(data) } catch (error) { @@ -214,10 +231,6 @@ export class StoreIndexedDB { return await getData() } - async createAccount() { - return await Signer.generate({ extractable: false }) - } - async reset() { if (this.#db) { withObjectStore(this.#db, 'readwrite', this.#dbStoreName, (s) => { diff --git a/packages/access-client/src/stores/store-memory.js b/packages/access-client/src/stores/store-memory.js index e391dff9e..1109a4625 100644 --- a/packages/access-client/src/stores/store-memory.js +++ b/packages/access-client/src/stores/store-memory.js @@ -2,12 +2,10 @@ import { Signer } from '@ucanto/principal/ed25519' // @ts-ignore // eslint-disable-next-line no-unused-vars import * as Types from '@ucanto/interface' -import { Delegations } from '../delegations.js' /** - * @typedef {import('./types').DelegationsAsJSON} DelegationsAsJSON - * @typedef {import('./types').StoreDataKeyEd} StoreData - * @typedef {import('./types').StoreKeyEd} Store + * @typedef {import('../types').AgentData} StoreData + * @typedef {import('./types').IStore} Store */ /** @@ -37,9 +35,14 @@ export class StoreMemory { return this.data.meta !== undefined && this.data.principal !== undefined } - static async create() { + /** + * + * @param {Partial} [data] + * @returns {Promise} + */ + static async create(data = {}) { const store = new StoreMemory() - await store.init({}) + await store.init(data) return store } @@ -47,17 +50,13 @@ export class StoreMemory { /** @type {Store['init']} */ async init(data) { const principal = data.principal || (await Signer.generate()) - const delegations = - data.delegations || - new Delegations({ - principal, - }) /** @type {StoreData} */ const storeData = { - accounts: data.accounts || [], meta: data.meta || { name: 'agent', type: 'device' }, principal, - delegations, + spaces: data.spaces || new Map(), + delegations: data.delegations || new Map(), + currentSpace: data.currentSpace, } await this.save(storeData) @@ -67,6 +66,7 @@ export class StoreMemory { /** * * @param {StoreData} data + * @returns {Promise} */ async save(data) { this.data = { @@ -80,8 +80,4 @@ export class StoreMemory { /** @type {StoreData} */ return this.data } - - async createAccount() { - return await Signer.generate() - } } diff --git a/packages/access-client/src/stores/types.ts b/packages/access-client/src/stores/types.ts index 757ed7e08..7fea75846 100644 --- a/packages/access-client/src/stores/types.ts +++ b/packages/access-client/src/stores/types.ts @@ -1,47 +1,63 @@ -import { AgentMeta } from '../types.js' -import { Delegations } from '../delegations.js' -import ed25519 from '@ucanto/principal/ed25519' +import { + AgentData, + AgentMeta, + CIDString, + DelegationMeta, + SpaceMeta, +} from '../types.js' import { RSASigner } from '@ucanto/principal/rsa' -import { SignerArchive } from '@ucanto/interface' +import { SignerArchive, DID, Block } from '@ucanto/interface' -export interface DelegationsAsJSON { - created: string - received: string - meta: Array<[string, AgentMeta]> -} - -export interface StoreData { - accounts: T[] - meta: AgentMeta - principal: T - delegations: Delegations -} - -export interface Store { - open: () => Promise> +/** + * Store interface that all stores need to implement + */ +export interface IStore { + /** + * Open store + */ + open: () => Promise> + /** + * Clean up and close store + */ close: () => Promise + /** + * Check if store exists and is initialized + */ exists: () => Promise - init: (data: Partial>) => Promise> - save: (data: StoreData) => Promise> - load: () => Promise> - createAccount: () => Promise + /** + * Initilize store with data + * + * @param data + */ + init: (data: Partial>) => Promise> + /** + * Persist data to the store's backend + * + * @param data + */ + save: (data: AgentData) => Promise> + /** + * Loads data from the store's backend + */ + load: () => Promise> + /** + * Clean all the data in the store's backend + */ reset: () => Promise } -export interface StoreKeyEd extends Store {} -export interface StoreDataKeyEd extends StoreData {} - -export interface StoreKeyRSA extends Store {} -export interface StoreDataKeyRSA extends StoreData {} - -export interface IDBStoreData { +// Store IDB +export interface StoreDataIDB { id: number - accounts: Array> - delegations: { - created: Array> - received: Array> - meta: Array<[string, import('../awake/types').PeerMeta]> - } - meta: import('../types').AgentMeta + meta: AgentMeta principal: SignerArchive + currentSpace?: DID + spaces: Map + delegations: Map< + CIDString, + { + meta?: DelegationMeta + delegation: Block[] + } + > } diff --git a/packages/access-client/src/types.ts b/packages/access-client/src/types.ts index 88a21c3b4..204fb1c4c 100644 --- a/packages/access-client/src/types.ts +++ b/packages/access-client/src/types.ts @@ -1,30 +1,45 @@ /* eslint-disable @typescript-eslint/indent */ import type { Capabilities, + ConnectionView, + Fact, Failure, Phantom, + Principal, RequestEncoder, + Resource, ResponseDecoder, ServiceMethod, UCAN, URI, + InferInvokedCapability, + CapabilityParser, + Match, + Ability, + UnknownMatch, + Transport, + Delegation, + DID, } from '@ucanto/interface' import type { - AccountInfo, - AccountRecover, - AccountRecoverValidation, - Any, + Abilities, + SpaceInfo, + SpaceRecover, + SpaceRecoverValidation, + Top, } from './capabilities/types' import { VoucherClaim, VoucherRedeem } from './capabilities/types.js' +import { IStore } from './stores/types.js' +import type { SetRequired } from 'type-fest' +// export other types export * from './capabilities/types.js' +export * from './stores/types.js' -export interface ClientCodec extends RequestEncoder, ResponseDecoder {} - -export type EncodedDelegation = string & - Phantom - +/** + * Access api service definition type + */ export interface Service { voucher: { claim: ServiceMethod< @@ -34,22 +49,82 @@ export interface Service { > redeem: ServiceMethod } - account: { - info: ServiceMethod + space: { + info: ServiceMethod 'recover-validation': ServiceMethod< - AccountRecoverValidation, - EncodedDelegation<[AccountRecover]> | undefined, + SpaceRecoverValidation, + EncodedDelegation<[SpaceRecover]> | undefined, Failure > recover: ServiceMethod< - AccountRecover, - Array>, + SpaceRecover, + Array>, Failure > } } -export interface Account { +/** + * Schema types + * + * Interfaces for data structures used in the client + * + */ + +export type CIDString = string + +/** + * Data schema used by the agent and persisted by stores + */ +export interface AgentData { + meta: AgentMeta + principal: T + currentSpace?: DID + spaces: Map + delegations: Map +} + +/** + * Agent metadata used to describe an agent ("audience") + * with a more human and UI friendly data + */ +export interface AgentMeta { + name: string + description?: string + url?: URL + image?: URL + type: 'device' | 'app' | 'service' +} + +/** + * Delegation metadata + */ +export interface DelegationMeta { + /** + * Audience metadata to be easier to build UIs with human readable data + * Normally used with delegations issued to third parties or other devices. + */ + audience: AgentMeta +} + +/** + * Space metadata + */ +export interface SpaceMeta { + /** + * Human readable name for the space + */ + name: string + /** + * Was this space already registered with the access-api using a voucher ? + */ + isRegistered: boolean +} + +/** + * Space schema in D1 database + */ +export interface SpaceD1 { did: UCAN.DID agent: UCAN.DID email: URI<'mailto:'> @@ -58,10 +133,119 @@ export interface Account { inserted_at: string } -export interface AgentMeta { - name: string - description?: string +/** + * Agent class types + */ + +export interface AgentOptions { + store: IStore + connection: ConnectionView url?: URL - image?: URL - type: 'device' | 'app' | 'service' + fetch: typeof fetch + data: AgentData +} + +export interface AgentCreateOptions { + channel?: Transport.Channel + store: IStore + url?: URL + fetch?: typeof fetch +} + +export type InvokeOptions< + A extends Ability, + R extends Resource, + CAP extends CapabilityParser< + Match<{ can: A; with: R; nb?: {} | undefined }, UnknownMatch> + > +> = UCANBasicOptions & + InferNb['nb']> & { + /** + * Resource for the capability, normally a Space DID + * Defaults to the current selected Space + */ + with?: R + + /** + * Extra proofs to be added to the invocation + */ + proofs?: Delegation[] + } + +export type DelegationOptions = SetRequired & { + /** + * Abilities to delegate + */ + abilities: Abilities[] + /** + * Metadata about the audience + */ + audienceMeta: AgentMeta +} + +/** + * Utility types + */ + +export interface UCANBasicOptions { + /** + * Audience Principal (DID), + * Defaults to "Access service DID" + * + * @see {@link https://github.com/ucan-wg/spec#321-principals Spec} + */ + audience?: Principal + /** + * UCAN lifetime in seconds + */ + lifetimeInSeconds?: number + /** + * Unix timestamp when the UCAN is no longer valid + * + * Expiration overrides `lifetimeInSeconds` + * + * @see {@link https://github.com/ucan-wg/spec#322-time-bounds Spec} + */ + expiration?: number + /** + * Unix timestamp when the UCAN becomas valid + * + * @see {@link https://github.com/ucan-wg/spec#322-time-bounds Spec} + */ + notBefore?: number + /** + * Nonce, a randomly generated string, used to ensure the uniqueness of the UCAN. + * + * @see {@link https://github.com/ucan-wg/spec#323-nonce Spec} + */ + nonce?: string + /** + * Facts, an array of extra facts or information to attach to the UCAN + * + * @see {@link https://github.com/ucan-wg/spec#324-facts Spec} + */ + facts?: Fact[] } + +/** + * Given an inferred capability infers if the nb field is optional or not + */ +export type InferNb = keyof C extends never + ? { + nb?: never + } + : { + /** + * Non-normative fields for the capability + * + * Check the capability definition for more details on the `nb` field. + * + * @see {@link https://github.com/ucan-wg/spec#241-nb-non-normative-fields Spec} + */ + nb: C + } + +export interface ClientCodec extends RequestEncoder, ResponseDecoder {} + +export type EncodedDelegation = string & + Phantom diff --git a/packages/access-client/test/agent.test.js b/packages/access-client/test/agent.test.js new file mode 100644 index 000000000..ebfacdc78 --- /dev/null +++ b/packages/access-client/test/agent.test.js @@ -0,0 +1,231 @@ +import assert from 'assert' +import { URI } from '@ucanto/validator' +import { Agent } from '../src/agent.js' +import { StoreMemory } from '../src/stores/store-memory.js' +import * as Space from '../src/capabilities/space.js' +import { createServer } from './helpers/utils.js' +import * as fixtures from './helpers/fixtures.js' + +describe('Agent', function () { + it('should fail if store is not initialized', async function () { + const store = new StoreMemory() + + return assert.rejects( + Agent.create({ + store, + }), + { + name: 'Error', + message: 'Store is not initialized, run "Store.init()" first.', + } + ) + }) + + it('should return did', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + }) + + assert.ok(agent.did()) + }) + + it('should create space', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + }) + + const space = await agent.createSpace('test-create') + + assert(typeof space.did === 'string') + assert(space.proof) + }) + + it('should add proof when creating acccount', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + }) + + const space = await agent.createSpace('test-add') + + const delegations = await agent.proofs() + + assert.equal(space.proof.cid, delegations[0].cid) + }) + + it('should set current space', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + }) + + const space = await agent.createSpace('test') + + await agent.setCurrentSpace(space.did) + + const accWithMeta = await agent.currentSpaceWithMeta() + if (!accWithMeta) { + assert.fail('should have space') + } + assert.equal(accWithMeta.did, space.did) + assert(accWithMeta.proofs.length === 1) + assert.deepStrictEqual(accWithMeta.capabilities, ['*']) + }) + + it('fails set current space with no proofs', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + }) + + await assert.rejects( + () => { + return agent.setCurrentSpace(fixtures.alice.did()) + }, + { + message: `Agent has no proofs for ${fixtures.alice.did()}.`, + } + ) + }) + + it('should invoke and execute', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + channel: createServer(), + }) + + const space = await agent.createSpace('execute') + await agent.setCurrentSpace(space.did) + + const out = await agent.invokeAndExecute(Space.info, { + audience: fixtures.service, + }) + + assert.deepEqual(out, { + did: 'did:key:sss', + agent: 'did:key:agent', + email: 'mail@mail.com', + product: 'product:free', + updated_at: 'sss', + inserted_at: 'date', + }) + }) + + it('should execute', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + channel: createServer(), + }) + + const space = await agent.createSpace('execute') + await agent.setCurrentSpace(space.did) + + const i1 = await agent.invoke(Space.info, { + audience: fixtures.service, + }) + const i2 = await agent.invoke(Space.recover, { + audience: fixtures.service, + nb: { + identity: 'mailto: email@gmail.com', + }, + }) + + const out = await agent.execute(i1, i2) + + assert.deepStrictEqual(out, [ + { + did: 'did:key:sss', + agent: 'did:key:agent', + email: 'mail@mail.com', + product: 'product:free', + updated_at: 'sss', + inserted_at: 'date', + }, + { + recover: true, + }, + ]) + }) + + it('should fail execute with no proofs', async function () { + const store = await StoreMemory.create() + const agent = await Agent.create({ + store, + channel: createServer(), + }) + + await assert.rejects( + async () => { + await agent.invokeAndExecute(Space.info, { + audience: fixtures.service, + with: URI.from(fixtures.alice.did()), + }) + }, + { + name: 'Error', + message: `no proofs available for resource ${URI.from( + fixtures.alice.did() + )} and ability space/info`, + } + ) + }) + + it('should get space info', async function () { + const store = await StoreMemory.create() + const server = createServer() + const agent = await Agent.create({ + store, + channel: server, + }) + + // mock service + // @ts-ignore + agent.service = async () => server.id + + const space = await agent.createSpace('execute') + await agent.setCurrentSpace(space.did) + + const out = await agent.getSpaceInfo() + assert.deepEqual(out, { + did: 'did:key:sss', + agent: 'did:key:agent', + email: 'mail@mail.com', + product: 'product:free', + updated_at: 'sss', + inserted_at: 'date', + }) + }) + + it('should delegate', async function () { + const store = await StoreMemory.create() + const server = createServer() + const agent = await Agent.create({ + store, + channel: server, + }) + + const space = await agent.createSpace('execute') + await agent.setCurrentSpace(space.did) + + const out = await agent.delegate({ + abilities: ['*'], + audience: fixtures.alice, + audienceMeta: { + name: 'sss', + type: 'app', + }, + }) + + assert(out.audience.did() === fixtures.alice.did()) + assert.deepStrictEqual(out.capabilities, [ + { + can: '*', + with: space.did, + }, + ]) + }) +}) diff --git a/packages/access-client/test/awake-channel.node.test.js b/packages/access-client/test/awake-channel.node.test.js index 3f1889d34..e424d9ff2 100644 --- a/packages/access-client/test/awake-channel.node.test.js +++ b/packages/access-client/test/awake-channel.node.test.js @@ -1,4 +1,4 @@ -import assert from 'assert/strict' +import assert from 'assert' import { Channel } from '../src/awake/channel.js' import { EcdhKeypair } from '../src/crypto/p256-ecdh.js' import { getWebsocketServer } from './helpers/miniflare.js' diff --git a/packages/access-client/test/awake.node.test.js b/packages/access-client/test/awake.node.test.js index 1ec927ee4..5f55a2684 100644 --- a/packages/access-client/test/awake.node.test.js +++ b/packages/access-client/test/awake.node.test.js @@ -8,7 +8,6 @@ import delay from 'delay' import pWaitFor from 'p-wait-for' import { Agent } from '../src/agent.js' import { StoreMemory } from '../src/stores/store-memory.js' -import { fetch } from '@web-std/fetch' describe('awake', function () { const host = new URL('ws://127.0.0.1:8788/connect') @@ -41,12 +40,12 @@ describe('awake', function () { it('should send msgs', async function () { const agent1 = await Agent.create({ store: await StoreMemory.create(), - fetch: globalThis.fetch || fetch, url: new URL('http://127.0.0.1:8787'), }) + const space = await agent1.createSpace('responder') + await agent1.setCurrentSpace(space.did) const agent2 = await Agent.create({ store: await StoreMemory.create(), - fetch: globalThis.fetch || fetch, url: new URL('http://127.0.0.1:8787'), }) const responder = agent1.peer(ws1) @@ -105,8 +104,8 @@ describe('awake', function () { // @ts-ignore if (link) { assert.deepEqual(requestor.did, link.delegation.audience.did()) - assert.deepEqual(responder.did, link.delegation.capabilities[0].with) - assert.deepEqual('identity/*', link.delegation.capabilities[0].can) + assert.deepEqual(space.did, link.delegation.capabilities[0].with) + assert.deepEqual('*', link.delegation.capabilities[0].can) } // they should close channel after link diff --git a/packages/access-client/test/capabilities/store.test.js b/packages/access-client/test/capabilities/store.test.js index 2f45cbc4c..d574be53b 100644 --- a/packages/access-client/test/capabilities/store.test.js +++ b/packages/access-client/test/capabilities/store.test.js @@ -4,7 +4,7 @@ import { access } from '@ucanto/validator' import { Verifier } from '@ucanto/principal' import { delegate, parseLink } from '@ucanto/core' import * as Store from '../../src/capabilities/store.js' -import * as Capability from '../../src/capabilities/any.js' +import * as Capability from '../../src/capabilities/top.js' import { alice, @@ -14,8 +14,8 @@ import { } from '../helpers/fixtures.js' import { createCarCid } from '../helpers/utils.js' -const any = async () => - Capability.any.delegate({ +const top = async () => + Capability.top.delegate({ issuer: account, audience: alice, with: account.did(), @@ -26,7 +26,7 @@ const store = async () => issuer: account, audience: alice, with: account.did(), - proofs: [await any()], + proofs: [await top()], }) describe('store capabilities', function () { @@ -39,7 +39,7 @@ describe('store capabilities', function () { link: parseLink('bafkqaaa'), size: 0, }, - proofs: [await any()], + proofs: [await top()], }) const result = await access(await add.delegate(), { @@ -93,7 +93,7 @@ describe('store capabilities', function () { issuer: alice, audience: bob, with: account.did(), - proofs: [await any()], + proofs: [await top()], }) const add = Store.add.invoke({ @@ -132,7 +132,7 @@ describe('store capabilities', function () { nb: { size: 1024, }, - proofs: [await any()], + proofs: [await top()], }) { @@ -190,7 +190,7 @@ describe('store capabilities', function () { for (const size of fixtures) { const json = JSON.stringify(size) it(`store/add size must be an int not ${json}`, async () => { - const proofs = [await any()] + const proofs = [await top()] assert.throws(() => { Store.add.invoke({ issuer: alice, @@ -220,7 +220,7 @@ describe('store capabilities', function () { }, }, ], - proofs: [await any()], + proofs: [await top()], }) // @ts-expect-error - size type doesnt not match because we are testing fails @@ -235,7 +235,7 @@ describe('store capabilities', function () { } it('store/add size must be an int', async () => { - const proofs = [await any()] + const proofs = [await top()] assert.throws(() => { Store.add.invoke({ issuer: alice, diff --git a/packages/access-client/test/helpers/utils.js b/packages/access-client/test/helpers/utils.js index 291f1a62d..d861d44a1 100644 --- a/packages/access-client/test/helpers/utils.js +++ b/packages/access-client/test/helpers/utils.js @@ -1,8 +1,11 @@ // eslint-disable-next-line no-unused-vars import * as Ucanto from '@ucanto/interface' import { parseLink } from '@ucanto/core' -import { codec as CARCodec } from '@ucanto/transport/car' -import { codec as CBOR } from '@ucanto/transport/cbor' +import * as Server from '@ucanto/server' +import * as Space from '../../src/capabilities/space.js' +import * as CAR from '@ucanto/transport/car' +import * as CBOR from '@ucanto/transport/cbor' +import { service } from './fixtures.js' /** * @param {string} source @@ -15,7 +18,7 @@ export function parseCarLink(source) { * @param {any} data */ export async function createCborCid(data) { - const cbor = await CBOR.write(data) + const cbor = await CBOR.codec.write(data) return cbor.cid } @@ -23,7 +26,41 @@ export async function createCborCid(data) { * @param {string} source */ export async function createCarCid(source) { - const cbor = await CBOR.write({ hello: source }) - const shard = await CARCodec.write({ roots: [cbor] }) + const cbor = await CBOR.codec.write({ hello: source }) + const shard = await CAR.codec.write({ roots: [cbor] }) return shard.cid } + +/** + * + * @returns {Ucanto.ServerView} + */ +export function createServer() { + const server = Server.create({ + id: service, + encoder: CBOR, + decoder: CAR, + service: { + space: { + info: Server.provide(Space.info, async ({ capability }) => { + return { + did: 'did:key:sss', + agent: 'did:key:agent', + email: 'mail@mail.com', + product: 'product:free', + updated_at: 'sss', + inserted_at: 'date', + } + }), + recover: Server.provide(Space.recover, async ({ capability }) => { + return { + recover: true, + } + }), + }, + }, + }) + + // @ts-ignore + return server +} diff --git a/packages/access-client/test/stores/store-indexeddb.browser.test.js b/packages/access-client/test/stores/store-indexeddb.browser.test.js index 9120cd79a..bdd356e9d 100644 --- a/packages/access-client/test/stores/store-indexeddb.browser.test.js +++ b/packages/access-client/test/stores/store-indexeddb.browser.test.js @@ -14,31 +14,14 @@ describe('IndexedDB store', () => { assert.equal(archive.key.extractable, false) // no accounts or delegations yet - assert.equal(data.accounts.length, 0) - assert.equal(data.delegations.received.length, 0) - assert.equal(data.delegations.created.length, 0) + assert.equal(data.spaces.size, 0) + assert.equal(data.delegations.size, 0) // default meta assert.equal(data.meta.name, 'agent') assert.equal(data.meta.type, 'device') }) - it('should allow an account to be added', async () => { - const store = await StoreIndexedDB.create('test-access-db-' + Date.now()) - let data = await store.load() - assert(data) - - assert.equal(data.accounts.length, 0) - - const account = await store.createAccount() - data.accounts.push(account) - - await store.save(data) - data = await store.load() - - assert.equal(data.accounts.length, 1) - }) - it('should check existence', async () => { const store = new StoreIndexedDB('test-access-db-' + Date.now()) await store.open() diff --git a/packages/access-client/test/stores/store-memory.test.js b/packages/access-client/test/stores/store-memory.test.js new file mode 100644 index 000000000..d88829664 --- /dev/null +++ b/packages/access-client/test/stores/store-memory.test.js @@ -0,0 +1,10 @@ +import assert from 'assert' +import { StoreMemory } from '../../src/stores/store-memory.js' + +describe('Store Memory', function () { + it('should not be initialized', async function () { + const store = new StoreMemory() + + assert.ok(!(await store.exists())) + }) +}) diff --git a/packages/access-ws/package.json b/packages/access-ws/package.json index a39b75954..8baa08f79 100644 --- a/packages/access-ws/package.json +++ b/packages/access-ws/package.json @@ -26,27 +26,27 @@ }, "devDependencies": { "@cloudflare/workers-types": "^3.18.0", - "@sentry/cli": "^2.8.0", - "@sentry/webpack-plugin": "^1.19.1", + "@sentry/cli": "^2.9.0", + "@sentry/webpack-plugin": "^1.20.0", "@types/assert": "^1.5.6", "@types/git-rev-sync": "^2.0.0", - "@types/node": "^18.11.7", + "@types/node": "^18.11.9", "assert": "^2.0.0", - "ava": "^5.0.1", + "ava": "^5.1.0", "buffer": "^6.0.3", "delay": "^5.0.0", "dotenv": "^16.0.3", - "esbuild": "^0.15.12", + "esbuild": "^0.15.14", "execa": "^6.1.0", - "git-rev-sync": "^3.0.1", + "git-rev-sync": "^3.0.2", "hd-scripts": "^3.0.2", - "miniflare": "^2.10.0", + "miniflare": "^2.11.0", "p-wait-for": "^5.0.0", "process": "^0.11.10", - "readable-stream": "^4.1.0", - "sade": "^1.7.4", + "readable-stream": "^4.2.0", + "sade": "^1.8.1", "typescript": "4.8.4", - "wrangler": "^2.1.13" + "wrangler": "^2.4.0" }, "eslintConfig": { "extends": [ diff --git a/packages/upload-client/README.md b/packages/upload-client/README.md index cbbf31597..44774685b 100644 --- a/packages/upload-client/README.md +++ b/packages/upload-client/README.md @@ -5,7 +5,7 @@ Install the package using npm: -```console +```bash npm install @web3-storage/upload-client ``` @@ -119,25 +119,34 @@ await Upload.add(conf, rootCID, carCIDs) ## API -- [`uploadDirectory`](#uploaddirectory) -- [`uploadFile`](#uploadfile) -- `CAR` - - [`encode`](#carencode) -- [`ShardingStream`](#shardingstream) -- [`ShardStoringStream`](#shardstoringstream) -- `Store` - - [`add`](#storeadd) - - [`list`](#storelist) - - [`remove`](#storeremove) -- `UnixFS` - - [`createDirectoryEncoderStream`](#unixfscreatedirectoryencoderstream) - - [`createFileEncoderStream`](#unixfscreatefileencoderstream) - - [`encodeDirectory`](#unixfsencodedirectory) - - [`encodeFile`](#unixfsencodefile) -- `Upload` - - [`add`](#uploadadd) - - [`list`](#uploadlist) - - [`remove`](#uploadremove) +- [Install](#install) +- [Usage](#usage) + - [Create an Agent](#create-an-agent) + - [Uploading files](#uploading-files) + - [Advanced usage](#advanced-usage) + - [Buffering API](#buffering-api) + - [Streaming API](#streaming-api) +- [API](#api) + - [`uploadDirectory`](#uploaddirectory) + - [`uploadFile`](#uploadfile) + - [`CAR.encode`](#carencode) + - [`ShardingStream`](#shardingstream) + - [`ShardStoringStream`](#shardstoringstream) + - [`Store.add`](#storeadd) + - [`Store.list`](#storelist) + - [`Store.remove`](#storeremove) + - [`UnixFS.createDirectoryEncoderStream`](#unixfscreatedirectoryencoderstream) + - [`UnixFS.createFileEncoderStream`](#unixfscreatefileencoderstream) + - [`UnixFS.encodeDirectory`](#unixfsencodedirectory) + - [`UnixFS.encodeFile`](#unixfsencodefile) + - [`Upload.add`](#uploadadd) + - [`Upload.list`](#uploadlist) + - [`Upload.remove`](#uploadremove) +- [Types](#types) + - [`CARFile`](#carfile) + - [`InvocationConfig`](#invocationconfig) +- [Contributing](#contributing) +- [License](#license) --- diff --git a/packages/upload-client/package.json b/packages/upload-client/package.json index cd0484826..34f63ae15 100644 --- a/packages/upload-client/package.json +++ b/packages/upload-client/package.json @@ -91,7 +91,7 @@ "npm-run-all": "^4.1.5", "path": "^0.12.7", "playwright-test": "^8.1.1", - "typescript": "^4.8.4" + "typescript": "4.8.4" }, "eslintConfig": { "extends": [ diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 4433f5503..1f568a894 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -11,18 +11,18 @@ "check": "tsc" }, "dependencies": { - "next": "13.0.2", + "next": "13.0.4", "react": "18.2.0", "react-dom": "18.2.0" }, "devDependencies": { - "@types/node": "^18.11.7", + "@types/node": "^18.11.9", "@types/react": "^18.0.25", "eslint": "^8.27.0", - "eslint-config-next": "13.0.2", + "eslint-config-next": "13.0.4", "hd-scripts": "^3.0.2", "typescript": "4.8.4", - "wrangler": "^2.1.13" + "wrangler": "^2.4.0" }, "eslintConfig": { "extends": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5068cc3a..df1dc6afb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,26 +7,32 @@ importers: lint-staged: ^13.0.3 prettier: 2.7.1 simple-git-hooks: ^2.8.1 - typescript: ^4.8.4 - wrangler: ^2.1.13 + typedoc: ^0.23.21 + typedoc-plugin-missing-exports: ^1.0.0 + typescript: 4.8.4 + wrangler: ^2.4.0 + dependencies: + typedoc: 0.23.21_typescript@4.8.4 + typedoc-plugin-missing-exports: 1.0.0_typedoc@0.23.21 devDependencies: lint-staged: 13.0.3 prettier: 2.7.1 simple-git-hooks: 2.8.1 - typescript: 4.9.3 - wrangler: 2.3.2 + typescript: 4.8.4 + wrangler: 2.4.2 packages/access-api: specifiers: '@cloudflare/workers-types': ^3.18.0 '@databases/escape-identifier': ^1.0.3 + '@databases/split-sql-query': ^1.0.3 '@databases/sql': ^3.2.0 '@ipld/dag-ucan': ^2.0.1 - '@sentry/cli': ^2.8.0 - '@sentry/webpack-plugin': ^1.19.1 + '@sentry/cli': ^2.9.0 + '@sentry/webpack-plugin': ^1.20.0 '@types/assert': ^1.5.6 '@types/git-rev-sync': ^2.0.0 - '@types/node': ^18.11.7 + '@types/node': ^18.11.9 '@types/qrcode': ^1.5.0 '@ucanto/client': ^3.0.2 '@ucanto/core': ^3.0.2 @@ -38,37 +44,37 @@ importers: '@web3-storage/access': workspace:^ '@web3-storage/worker-utils': 0.4.3-dev assert: ^2.0.0 - ava: ^5.0.1 + ava: ^5.1.0 better-sqlite3: 7.6.2 buffer: ^6.0.3 delay: ^5.0.0 dotenv: ^16.0.3 - esbuild: ^0.15.12 + esbuild: ^0.15.14 execa: ^6.1.0 - git-rev-sync: ^3.0.1 + git-rev-sync: ^3.0.2 hd-scripts: ^3.0.2 - miniflare: ^2.10.0 + miniflare: ^2.11.0 multiformats: ^10.0.2 nanoid: ^4.0.0 p-retry: ^5.1.1 p-wait-for: ^5.0.0 - preact: ^10.11.2 + preact: ^10.11.3 preact-render-to-string: ^5.2.6 process: ^0.11.10 qrcode: ^1.5.1 - readable-stream: ^4.1.0 - sade: ^1.7.4 + readable-stream: ^4.2.0 + sade: ^1.8.1 toucan-js: ^2.7.0 typescript: 4.8.4 workers-qb: ^0.1.2 - wrangler: ^2.1.13 + wrangler: ^2.4.0 dependencies: '@ipld/dag-ucan': 2.0.1 '@ucanto/client': 3.0.2 '@ucanto/core': 3.0.2 '@ucanto/interface': 3.0.1 '@ucanto/principal': 3.0.1 - '@ucanto/server': 3.0.4 + '@ucanto/server': 3.0.5 '@ucanto/transport': 3.0.2 '@ucanto/validator': 3.0.4 '@web3-storage/access': link:../access-client @@ -84,6 +90,7 @@ importers: devDependencies: '@cloudflare/workers-types': 3.18.0 '@databases/escape-identifier': 1.0.3 + '@databases/split-sql-query': 1.0.3_@databases+sql@3.2.0 '@databases/sql': 3.2.0 '@sentry/cli': 2.9.0 '@sentry/webpack-plugin': 1.20.0 @@ -97,7 +104,7 @@ importers: buffer: 6.0.3 delay: 5.0.0 dotenv: 16.0.3 - esbuild: 0.15.14 + esbuild: 0.15.15 execa: 6.1.0 git-rev-sync: 3.0.2 hd-scripts: 3.0.2 @@ -107,17 +114,17 @@ importers: readable-stream: 4.2.0 sade: 1.8.1 typescript: 4.8.4 - wrangler: 2.3.2 + wrangler: 2.4.2 packages/access-client: specifiers: - '@ipld/car': ^5.0.0 + '@ipld/car': ^5.0.1 '@ipld/dag-ucan': ^2.0.1 '@noble/ed25519': ^1.7.1 '@types/assert': ^1.5.6 '@types/inquirer': ^9.0.3 '@types/mocha': ^10.0.0 - '@types/node': ^18.11.7 + '@types/node': ^18.11.9 '@types/ws': ^8.5.3 '@ucanto/client': ^3.0.2 '@ucanto/core': ^3.0.2 @@ -129,13 +136,13 @@ importers: '@web-std/fetch': ^4.1.0 assert: ^2.0.0 bigint-mod-arith: ^3.1.2 - conf: ^10.1.2 + conf: ^10.2.0 delay: ^5.0.0 dotenv: ^16.0.3 hd-scripts: ^3.0.2 inquirer: ^9.1.4 isomorphic-ws: ^5.0.0 - miniflare: ^2.9.0 + miniflare: ^2.11.0 mocha: ^10.1.0 multiformats: ^10.0.2 nanoid: ^4.0.0 @@ -143,13 +150,12 @@ importers: ora: ^6.1.2 p-defer: ^4.0.0 p-queue: ^7.3.0 - p-retry: ^5.1.1 p-wait-for: ^5.0.0 playwright-test: ^8.1.1 - sade: ^1.7.4 + sade: ^1.8.1 + type-fest: ^3.2.0 typescript: 4.8.4 uint8arrays: ^4.0.2 - undici: ^5.12.0 watch: ^1.0.2 ws: ^8.11.0 zod: ^3.19.1 @@ -157,12 +163,11 @@ importers: '@ipld/car': 5.0.1 '@ipld/dag-ucan': 2.0.1 '@noble/ed25519': 1.7.1 - '@types/ws': 8.5.3 '@ucanto/client': 3.0.2 '@ucanto/core': 3.0.2 '@ucanto/interface': 3.0.1 '@ucanto/principal': 3.0.1 - '@ucanto/server': 3.0.4 + '@ucanto/server': 3.0.5 '@ucanto/transport': 3.0.2 '@ucanto/validator': 3.0.4 '@web-std/fetch': 4.1.0 @@ -175,11 +180,9 @@ importers: one-webcrypto: 1.0.3 ora: 6.1.2 p-defer: 4.0.0 - p-queue: 7.3.0 - p-retry: 5.1.1 p-wait-for: 5.0.0 + type-fest: 3.2.0 uint8arrays: 4.0.2 - undici: 5.12.0 ws: 8.11.0 zod: 3.19.1 devDependencies: @@ -187,12 +190,14 @@ importers: '@types/inquirer': 9.0.3 '@types/mocha': 10.0.0 '@types/node': 18.11.9 + '@types/ws': 8.5.3 assert: 2.0.0 delay: 5.0.0 dotenv: 16.0.3 hd-scripts: 3.0.2 miniflare: 2.11.0 mocha: 10.1.0 + p-queue: 7.3.0 playwright-test: 8.1.1 sade: 1.8.1 typescript: 4.8.4 @@ -201,33 +206,33 @@ importers: packages/access-ws: specifiers: '@cloudflare/workers-types': ^3.18.0 - '@sentry/cli': ^2.8.0 - '@sentry/webpack-plugin': ^1.19.1 + '@sentry/cli': ^2.9.0 + '@sentry/webpack-plugin': ^1.20.0 '@types/assert': ^1.5.6 '@types/git-rev-sync': ^2.0.0 - '@types/node': ^18.11.7 + '@types/node': ^18.11.9 '@types/ws': ^8.5.3 '@web3-storage/worker-utils': 0.4.3-dev assert: ^2.0.0 - ava: ^5.0.1 + ava: ^5.1.0 buffer: ^6.0.3 delay: ^5.0.0 dotenv: ^16.0.3 - esbuild: ^0.15.12 + esbuild: ^0.15.14 execa: ^6.1.0 - git-rev-sync: ^3.0.1 + git-rev-sync: ^3.0.2 hd-scripts: ^3.0.2 isomorphic-ws: ^5.0.0 - miniflare: ^2.10.0 + miniflare: ^2.11.0 multiformats: ^10.0.2 nanoid: ^4.0.0 p-wait-for: ^5.0.0 process: ^0.11.10 - readable-stream: ^4.1.0 - sade: ^1.7.4 + readable-stream: ^4.2.0 + sade: ^1.8.1 toucan-js: ^2.7.0 typescript: 4.8.4 - wrangler: ^2.1.13 + wrangler: ^2.4.0 ws: ^8.11.0 dependencies: '@types/ws': 8.5.3 @@ -249,7 +254,7 @@ importers: buffer: 6.0.3 delay: 5.0.0 dotenv: 16.0.3 - esbuild: 0.15.14 + esbuild: 0.15.15 execa: 6.1.0 git-rev-sync: 3.0.2 hd-scripts: 3.0.2 @@ -259,7 +264,7 @@ importers: readable-stream: 4.2.0 sade: 1.8.1 typescript: 4.8.4 - wrangler: 2.3.2 + wrangler: 2.4.2 packages/upload-client: specifiers: @@ -289,7 +294,7 @@ importers: p-retry: ^5.1.1 path: ^0.12.7 playwright-test: ^8.1.1 - typescript: ^4.8.4 + typescript: 4.8.4 dependencies: '@ipld/car': 5.0.1 '@ipld/dag-ucan': 2.0.1 @@ -306,7 +311,7 @@ importers: '@types/assert': 1.5.6 '@types/mocha': 10.0.0 '@ucanto/principal': 3.0.1 - '@ucanto/server': 3.0.4 + '@ucanto/server': 3.0.5 '@ucanto/validator': 3.0.4 assert: 2.0.0 blockstore-core: 2.0.2 @@ -318,32 +323,32 @@ importers: npm-run-all: 4.1.5 path: 0.12.7 playwright-test: 8.1.1 - typescript: 4.9.3 + typescript: 4.8.4 packages/wallet: specifiers: - '@types/node': ^18.11.7 + '@types/node': ^18.11.9 '@types/react': ^18.0.25 eslint: ^8.27.0 - eslint-config-next: 13.0.2 + eslint-config-next: 13.0.4 hd-scripts: ^3.0.2 - next: 13.0.2 + next: 13.0.4 react: 18.2.0 react-dom: 18.2.0 typescript: 4.8.4 - wrangler: ^2.1.13 + wrangler: ^2.4.0 dependencies: - next: 13.0.2_biqbaboplfbrettd7655fr4n2y + next: 13.0.4_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 devDependencies: '@types/node': 18.11.9 '@types/react': 18.0.25 - eslint: 8.27.0 - eslint-config-next: 13.0.2_rmayb2veg2btbq6mbmnyivgasy + eslint: 8.28.0 + eslint-config-next: 13.0.4_zksrc6ykdxhogxjbhb5axiabwi hd-scripts: 3.0.2 typescript: 4.8.4 - wrangler: 2.3.2 + wrangler: 2.4.2 packages: @@ -378,14 +383,14 @@ packages: engines: {node: '>=6.9.0'} dependencies: core-js-pure: 3.26.1 - regenerator-runtime: 0.13.10 + regenerator-runtime: 0.13.11 dev: true /@babel/runtime/7.20.1: resolution: {integrity: sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==} engines: {node: '>=6.9.0'} dependencies: - regenerator-runtime: 0.13.10 + regenerator-runtime: 0.13.11 dev: true /@bcoe/v8-coverage/0.2.3: @@ -408,6 +413,14 @@ packages: '@databases/validate-unicode': 1.0.0 dev: true + /@databases/split-sql-query/1.0.3_@databases+sql@3.2.0: + resolution: {integrity: sha512-Q3UYX85e34yE9KXa095AJtJhBQ0NpLfC0kS9ydFKuNB25cto4YddY52RuXN81m2t0pS1Atg31ylNpKfNCnUPdA==} + peerDependencies: + '@databases/sql': '*' + dependencies: + '@databases/sql': 3.2.0 + dev: true + /@databases/sql/3.2.0: resolution: {integrity: sha512-xQZzKIa0lvcdo0MYxnyFMVS1TRla9lpDSCYkobJl19vQEOJ9TqE4o8QBGRJNUfhSkbQIWyvMeBl3KBBbqyUVQQ==} dev: true @@ -443,8 +456,8 @@ packages: rollup-plugin-node-polyfills: 0.2.1 dev: true - /@esbuild/android-arm/0.15.14: - resolution: {integrity: sha512-+Rb20XXxRGisNu2WmNKk+scpanb7nL5yhuI1KR9wQFiC43ddPj/V1fmNyzlFC9bKiG4mYzxW7egtoHVcynr+OA==} + /@esbuild/android-arm/0.15.15: + resolution: {integrity: sha512-JJjZjJi2eBL01QJuWjfCdZxcIgot+VoK6Fq7eKF9w4YHm9hwl7nhBR1o2Wnt/WcANk5l9SkpvrldW1PLuXxcbw==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -452,8 +465,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64/0.15.14: - resolution: {integrity: sha512-eQi9rosGNVQFJyJWV0HCA5WZae/qWIQME7s8/j8DMvnylfBv62Pbu+zJ2eUDqNf2O4u3WB+OEXyfkpBoe194sg==} + /@esbuild/linux-loong64/0.15.15: + resolution: {integrity: sha512-lhz6UNPMDXUhtXSulw8XlFAtSYO26WmHQnCi2Lg2p+/TMiJKNLtZCYUxV4wG6rZMzXmr8InGpNwk+DLT2Hm0PA==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -468,7 +481,7 @@ packages: ajv: 6.12.6 debug: 4.3.4 espree: 9.4.1 - globals: 13.17.0 + globals: 13.18.0 ignore: 5.2.0 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -927,18 +940,18 @@ packages: murmurhash3js-revisited: 3.0.0 dev: true - /@next/env/13.0.2: - resolution: {integrity: sha512-Qb6WPuRriGIQ19qd6NBxpcrFOfj8ziN7l9eZUfwff5gl4zLXluqtuZPddYZM/oWjN53ZYcuRXzL+oowKyJeYtA==} + /@next/env/13.0.4: + resolution: {integrity: sha512-N5Z3bdxBzoxrC5bwykDFITzdWuwDteOdZ+7nxixY+I1XpRX8/iQYbw2wuXMdqdfBGm2NNUpAqg8YF2e4oAC2UQ==} dev: false - /@next/eslint-plugin-next/13.0.2: - resolution: {integrity: sha512-W+fIIIaFU7Kct7Okx91C7XDRGolv/w2RUenX2yZFeeNVcuVzDIKUcNmckrYbYcwrNQUSXmtwrs3g8xwast0YtA==} + /@next/eslint-plugin-next/13.0.4: + resolution: {integrity: sha512-jZ4urKT+aO9QHm3ttihrIQscQISDSKK8isAom750+EySn9o3LCSkTdPGBtvBqY7Yku+NqhfQempR5J58DqTaVg==} dependencies: glob: 7.1.7 dev: true - /@next/swc-android-arm-eabi/13.0.2: - resolution: {integrity: sha512-X54UQCTFyOGnJP//Z71dPPlp4BCYcQL2ncikKXQcPzVpqPs4C3m+tKC8ivBNH6edAXkppwsLRz1/yQwgSZ9Swg==} + /@next/swc-android-arm-eabi/13.0.4: + resolution: {integrity: sha512-SD9H+/zuV3L0oHIhsDdFkDqFtg6pIHtqRUPlsrNdOsmWXgMlSzxBmwt2ta4kyrazS62BQu7XRUG++ZyODS7AWg==} engines: {node: '>= 10'} cpu: [arm] os: [android] @@ -946,8 +959,8 @@ packages: dev: false optional: true - /@next/swc-android-arm64/13.0.2: - resolution: {integrity: sha512-1P00Kv8uKaLubqo7JzPrTqgFAzSOmfb8iwqJrOb9in5IvTRtNGlkR4hU0sXzqbQNM/+SaYxze6Z5ry1IDyb/cQ==} + /@next/swc-android-arm64/13.0.4: + resolution: {integrity: sha512-F8W5WcBbdn/zBoy32/mQiefs9DNsT12CTSSVCsO8GvQR7GjJU+uduQ4drKcSDoDLuAFULc2jDN06Circq4vuQg==} engines: {node: '>= 10'} cpu: [arm64] os: [android] @@ -955,8 +968,8 @@ packages: dev: false optional: true - /@next/swc-darwin-arm64/13.0.2: - resolution: {integrity: sha512-1zGIOkInkOLRv0QQGZ+3wffYsyKI4vIy62LYTvDWUn7TAYqnmXwougp9NSLqDeagLwgsv2URrykyAFixA/YqxA==} + /@next/swc-darwin-arm64/13.0.4: + resolution: {integrity: sha512-/lajev+9GSie+rRTl5z8skW9RJwZ+TwMKLzzM24TbDk8lUjqPTyJZ/cU0NDj8J7VQAZ6EehACSh9rcJeBRtLuA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -964,8 +977,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64/13.0.2: - resolution: {integrity: sha512-ECDAjoMP1Y90cARaelS6X+k6BQx+MikAYJ8f/eaJrLur44NIOYc9HA/dgcTp5jenguY4yT8V+HCquLjAVle6fA==} + /@next/swc-darwin-x64/13.0.4: + resolution: {integrity: sha512-HK4b2rFiju8d40GTL/jH9U6OQ7BYA2MeEHs7Dm7Rp7kwQtLzP3z6osdQS8er20tIFHDE4b+oVBy03ZUQkHf0Pg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -973,8 +986,8 @@ packages: dev: false optional: true - /@next/swc-freebsd-x64/13.0.2: - resolution: {integrity: sha512-2DcL/ofQdBnQX3IoI9sjlIAyLCD1oZoUBuhrhWbejvBQjutWrI0JTEv9uG69WcxWhVMm3BCsjv8GK2/68OKp7A==} + /@next/swc-freebsd-x64/13.0.4: + resolution: {integrity: sha512-xBvIGLaGzZtgJfRRJ2DBN82DQCJ/O7jkXyBp8X/vHefPWyVXVqF6C68Rv8ADp11thPpf8WpjkvDDLb9AuWHQUA==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] @@ -982,8 +995,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm-gnueabihf/13.0.2: - resolution: {integrity: sha512-Y3OQF1CSBSWW2vGkmvOIuOUNqOq8qX7f1ZpcKUVWP3/Uq++DZmVi9d18lgnSe1I3QFqc+nXWyun9ljsN83j0sw==} + /@next/swc-linux-arm-gnueabihf/13.0.4: + resolution: {integrity: sha512-s13pxNp9deKmmxEGTp1MoL1e4nf4wbEymEaHgFxUlhoR1OD9tK8oTNrQphQePJgVjzcWmRGH/dX7O9mVkHbU/g==} engines: {node: '>= 10'} cpu: [arm] os: [linux] @@ -991,8 +1004,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu/13.0.2: - resolution: {integrity: sha512-mNyzwsFF6kwZYEjnGicx9ksDZYEZvyzEc1BtCu8vdZi/v8UeixQwCiAT6FyYX9uxMPEkzk8qiU0t0u9gvltsKw==} + /@next/swc-linux-arm64-gnu/13.0.4: + resolution: {integrity: sha512-Lklo65usNzoYwjX51CpDKOepWVZBdwO49/Jz3djxiYUr2lRtpDVnlfwCvzN+47j3BMVMWtC2ndIi8Q4s3J0v4g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1000,8 +1013,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl/13.0.2: - resolution: {integrity: sha512-M6SdYjWgRrY3tJBxz0663zCRPTu5BRONmxlftKWWHv9LjAJ59neTLaGj4rp0A08DkJglZIoCkLOzLrzST6TGag==} + /@next/swc-linux-arm64-musl/13.0.4: + resolution: {integrity: sha512-+3BXtXBwjVhd5lahDe5nKZ7EwD6hE/oLFQkITCvgxymU5qYHGlLFyF52/lyw8qhyxoCr7mMVsUFhlCzVwCfNjg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1009,8 +1022,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu/13.0.2: - resolution: {integrity: sha512-pi63RoxvG4ES1KS06Zpm0MATVIXTs/TIbLbdckeLoM40u1d3mQl/+hSSrLRSxzc2OtyL8fh92sM4gkJrQXAMAw==} + /@next/swc-linux-x64-gnu/13.0.4: + resolution: {integrity: sha512-QB8qoZrvHhZsz62nUrTKlp5IiZ8I7KZsaa6437H/W/NOZHLGJjCxROnhUjLvKVe/T5P86pjya2SUOUqWAjz4Pg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1018,8 +1031,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl/13.0.2: - resolution: {integrity: sha512-9Pv91gfYnDONgjtRm78n64b/c54+azeHtlnqBLTnIFWSMBDRl1/WDkhKWIj3fBGPLimtK7Tko3ULR3og9RRUPw==} + /@next/swc-linux-x64-musl/13.0.4: + resolution: {integrity: sha512-WaahF6DYUQRg1QqIMcuOu2ZsFhS3aC5iWeQyeptMHklP9wb4FfTNmBArKHknX/GXD8P9gI38WTAHJ25cc0zVwg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1027,8 +1040,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc/13.0.2: - resolution: {integrity: sha512-Nvewe6YZaizAkGHHprbMkYqQulBjZCHKBGKeFPwoPtOA+a2Qi4pZzc/qXFyC5/2A6Z0mr2U1zg9rd04WBYMwBw==} + /@next/swc-win32-arm64-msvc/13.0.4: + resolution: {integrity: sha512-FD+k1j2jeY0aKcqcpzFKfTsv55PPmIZ5GKDyPjjV5AO6XvQ4nALwWl4JwizjH2426TfLXObb+C3MH0bl9Ok1Kw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -1036,8 +1049,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc/13.0.2: - resolution: {integrity: sha512-ZUBYGZw5G3QrqDpRq1EWi3aHmvPZM8ijK5TFL6UbH16cYQ0JpANmuG2P66KB93Qe/lWWzbeAZk/tj1XqwoCuPA==} + /@next/swc-win32-ia32-msvc/13.0.4: + resolution: {integrity: sha512-+Q/Q8Ydvz3X3U84CyZdNv1HC7fE43k+xB8C6b3IFmWGa5Tu2tfskQ2FsUNBrYreZjhFC/894J3rVQ6Vj6Auugg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -1045,8 +1058,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc/13.0.2: - resolution: {integrity: sha512-fA9uW1dm7C0mEYGcKlbmLcVm2sKcye+1kPxh2cM4jVR+kQQMtHWsjIzeSpe2grQLSDan06z4n6hbr8b1c3hA8w==} + /@next/swc-win32-x64-msvc/13.0.4: + resolution: {integrity: sha512-vXtbo9N1FdtZZRcv4BliU28tTYrkb1EnVpUiiFFe88I6kS9aZVTMY9Z/OtDR52rl1JF1hgs9sL/59D/TQqSATQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1087,6 +1100,18 @@ packages: typescript: 4.8.4 dev: true + /@pkgr/utils/2.3.1: + resolution: {integrity: sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + is-glob: 4.0.3 + open: 8.4.0 + picocolors: 1.0.0 + tiny-glob: 0.2.9 + tslib: 2.4.1 + dev: true + /@polka/url/0.5.0: resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==} dev: true @@ -1317,7 +1342,6 @@ packages: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: '@types/node': 18.11.9 - dev: false /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -1329,7 +1353,7 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin/5.43.0_yy4vf4gcvxiubmg7fqa55dqe2i: + /@typescript-eslint/eslint-plugin/5.43.0_ol5heeoi7wmwb4tenztnb7jlze: resolution: {integrity: sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1340,12 +1364,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi '@typescript-eslint/scope-manager': 5.43.0 - '@typescript-eslint/type-utils': 5.43.0_rmayb2veg2btbq6mbmnyivgasy - '@typescript-eslint/utils': 5.43.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/type-utils': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi + '@typescript-eslint/utils': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi debug: 4.3.4 - eslint: 8.27.0 + eslint: 8.28.0 ignore: 5.2.0 natural-compare-lite: 1.4.0 regexpp: 3.2.0 @@ -1356,20 +1380,20 @@ packages: - supports-color dev: true - /@typescript-eslint/experimental-utils/5.43.0_rmayb2veg2btbq6mbmnyivgasy: + /@typescript-eslint/experimental-utils/5.43.0_zksrc6ykdxhogxjbhb5axiabwi: resolution: {integrity: sha512-WkT637CumTJbm/hRbFfnHBMgfUYTKr08LitVsD7gQId7bi6rnkx3pu3jac67lmp5ObW4MpJ9SNFZAIOUB/Qbsw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 5.43.0_rmayb2veg2btbq6mbmnyivgasy - eslint: 8.27.0 + '@typescript-eslint/utils': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi + eslint: 8.28.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/parser/5.43.0_rmayb2veg2btbq6mbmnyivgasy: + /@typescript-eslint/parser/5.43.0_zksrc6ykdxhogxjbhb5axiabwi: resolution: {integrity: sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1383,7 +1407,7 @@ packages: '@typescript-eslint/types': 5.43.0 '@typescript-eslint/typescript-estree': 5.43.0_typescript@4.8.4 debug: 4.3.4 - eslint: 8.27.0 + eslint: 8.28.0 typescript: 4.8.4 transitivePeerDependencies: - supports-color @@ -1397,7 +1421,7 @@ packages: '@typescript-eslint/visitor-keys': 5.43.0 dev: true - /@typescript-eslint/type-utils/5.43.0_rmayb2veg2btbq6mbmnyivgasy: + /@typescript-eslint/type-utils/5.43.0_zksrc6ykdxhogxjbhb5axiabwi: resolution: {integrity: sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1408,9 +1432,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.43.0_typescript@4.8.4 - '@typescript-eslint/utils': 5.43.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/utils': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi debug: 4.3.4 - eslint: 8.27.0 + eslint: 8.28.0 tsutils: 3.21.0_typescript@4.8.4 typescript: 4.8.4 transitivePeerDependencies: @@ -1443,7 +1467,7 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.43.0_rmayb2veg2btbq6mbmnyivgasy: + /@typescript-eslint/utils/5.43.0_zksrc6ykdxhogxjbhb5axiabwi: resolution: {integrity: sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1454,9 +1478,9 @@ packages: '@typescript-eslint/scope-manager': 5.43.0 '@typescript-eslint/types': 5.43.0 '@typescript-eslint/typescript-estree': 5.43.0_typescript@4.8.4 - eslint: 8.27.0 + eslint: 8.28.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.27.0 + eslint-utils: 3.0.0_eslint@8.28.0 semver: 7.3.8 transitivePeerDependencies: - supports-color @@ -1502,8 +1526,8 @@ packages: multiformats: 10.0.2 one-webcrypto: 1.0.3 - /@ucanto/server/3.0.4: - resolution: {integrity: sha512-4a5ou5HZ8ymw39EeZ0024nu0JD916xTMpYsS4eXMxpnoWlOjgmoeWZ5e01dEiw5AlfnSa8e9+a0b1dzvSISQxQ==} + /@ucanto/server/3.0.5: + resolution: {integrity: sha512-6srksAbYrCixnGWMlPXAFa5smjCLZ9+AMLwxS5vbLdVToFcZc2JXFFZUFq7fJouOMETCUsk+q3ank4sNw2BQJg==} dependencies: '@ucanto/core': 3.0.2 '@ucanto/interface': 3.0.1 @@ -1802,6 +1826,16 @@ packages: es-shim-unscopables: 1.0.0 dev: true + /array.prototype.tosorted/1.1.1: + resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.1.3 + dev: true + /arrgv/1.0.2: resolution: {integrity: sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==} engines: {node: '>=8.0.0'} @@ -1909,7 +1943,6 @@ packages: /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1986,7 +2019,6 @@ packages: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 - dev: true /braces/3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -2032,6 +2064,7 @@ packages: engines: {node: '>=10.16.0'} dependencies: streamsearch: 1.1.0 + dev: true /c8/7.12.0: resolution: {integrity: sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==} @@ -2078,8 +2111,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001431: - resolution: {integrity: sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==} + /caniuse-lite/1.0.30001434: + resolution: {integrity: sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==} dev: false /cbor/8.1.0: @@ -2547,6 +2580,11 @@ packages: dependencies: clone: 1.0.4 + /define-lazy-prop/2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: true + /define-properties/1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} engines: {node: '>= 0.4'} @@ -2665,6 +2703,14 @@ packages: once: 1.4.0 dev: true + /enhanced-resolve/5.10.0: + resolution: {integrity: sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.10 + tapable: 2.2.1 + dev: true + /env-paths/2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -2766,8 +2812,8 @@ packages: dev: true optional: true - /esbuild-android-64/0.15.14: - resolution: {integrity: sha512-HuilVIb4rk9abT4U6bcFdU35UHOzcWVGLSjEmC58OVr96q5UiRqzDtWjPlCMugjhgUGKEs8Zf4ueIvYbOStbIg==} + /esbuild-android-64/0.15.15: + resolution: {integrity: sha512-F+WjjQxO+JQOva3tJWNdVjouFMLK6R6i5gjDvgUthLYJnIZJsp1HlF523k73hELY20WPyEO8xcz7aaYBVkeg5Q==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -2793,8 +2839,8 @@ packages: dev: true optional: true - /esbuild-android-arm64/0.15.14: - resolution: {integrity: sha512-/QnxRVxsR2Vtf3XottAHj7hENAMW2wCs6S+OZcAbc/8nlhbAL/bCQRCVD78VtI5mdwqWkVi3wMqM94kScQCgqg==} + /esbuild-android-arm64/0.15.15: + resolution: {integrity: sha512-attlyhD6Y22jNyQ0fIIQ7mnPvDWKw7k6FKnsXlBvQE6s3z6s6cuEHcSgoirquQc7TmZgVCK5fD/2uxmRN+ZpcQ==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -2820,8 +2866,8 @@ packages: dev: true optional: true - /esbuild-darwin-64/0.15.14: - resolution: {integrity: sha512-ToNuf1uifu8hhwWvoZJGCdLIX/1zpo8cOGnT0XAhDQXiKOKYaotVNx7pOVB1f+wHoWwTLInrOmh3EmA7Fd+8Vg==} + /esbuild-darwin-64/0.15.15: + resolution: {integrity: sha512-ohZtF8W1SHJ4JWldsPVdk8st0r9ExbAOSrBOh5L+Mq47i696GVwv1ab/KlmbUoikSTNoXEhDzVpxUR/WIO19FQ==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -2847,8 +2893,8 @@ packages: dev: true optional: true - /esbuild-darwin-arm64/0.15.14: - resolution: {integrity: sha512-KgGP+y77GszfYJgceO0Wi/PiRtYo5y2Xo9rhBUpxTPaBgWDJ14gqYN0+NMbu+qC2fykxXaipHxN4Scaj9tUS1A==} + /esbuild-darwin-arm64/0.15.15: + resolution: {integrity: sha512-P8jOZ5zshCNIuGn+9KehKs/cq5uIniC+BeCykvdVhx/rBXSxmtj3CUIKZz4sDCuESMbitK54drf/2QX9QHG5Ag==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -2874,8 +2920,8 @@ packages: dev: true optional: true - /esbuild-freebsd-64/0.15.14: - resolution: {integrity: sha512-xr0E2n5lyWw3uFSwwUXHc0EcaBDtsal/iIfLioflHdhAe10KSctV978Te7YsfnsMKzcoGeS366+tqbCXdqDHQA==} + /esbuild-freebsd-64/0.15.15: + resolution: {integrity: sha512-KkTg+AmDXz1IvA9S1gt8dE24C8Thx0X5oM0KGF322DuP+P3evwTL9YyusHAWNsh4qLsR80nvBr/EIYs29VSwuA==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -2901,8 +2947,8 @@ packages: dev: true optional: true - /esbuild-freebsd-arm64/0.15.14: - resolution: {integrity: sha512-8XH96sOQ4b1LhMlO10eEWOjEngmZ2oyw3pW4o8kvBcpF6pULr56eeYVP5radtgw54g3T8nKHDHYEI5AItvskZg==} + /esbuild-freebsd-arm64/0.15.15: + resolution: {integrity: sha512-FUcML0DRsuyqCMfAC+HoeAqvWxMeq0qXvclZZ/lt2kLU6XBnDA5uKTLUd379WYEyVD4KKFctqWd9tTuk8C/96g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -2928,8 +2974,8 @@ packages: dev: true optional: true - /esbuild-linux-32/0.15.14: - resolution: {integrity: sha512-6ssnvwaTAi8AzKN8By2V0nS+WF5jTP7SfuK6sStGnDP7MCJo/4zHgM9oE1eQTS2jPmo3D673rckuCzRlig+HMA==} + /esbuild-linux-32/0.15.15: + resolution: {integrity: sha512-q28Qn5pZgHNqug02aTkzw5sW9OklSo96b5nm17Mq0pDXrdTBcQ+M6Q9A1B+dalFeynunwh/pvfrNucjzwDXj+Q==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -2955,8 +3001,8 @@ packages: dev: true optional: true - /esbuild-linux-64/0.15.14: - resolution: {integrity: sha512-ONySx3U0wAJOJuxGUlXBWxVKFVpWv88JEv0NZ6NlHknmDd1yCbf4AEdClSgLrqKQDXYywmw4gYDvdLsS6z0hcw==} + /esbuild-linux-64/0.15.15: + resolution: {integrity: sha512-217KPmWMirkf8liO+fj2qrPwbIbhNTGNVtvqI1TnOWJgcMjUWvd677Gq3fTzXEjilkx2yWypVnTswM2KbXgoAg==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -2982,8 +3028,8 @@ packages: dev: true optional: true - /esbuild-linux-arm/0.15.14: - resolution: {integrity: sha512-D2LImAIV3QzL7lHURyCHBkycVFbKwkDb1XEUWan+2fb4qfW7qAeUtul7ZIcIwFKZgPcl+6gKZmvLgPSj26RQ2Q==} + /esbuild-linux-arm/0.15.15: + resolution: {integrity: sha512-RYVW9o2yN8yM7SB1yaWr378CwrjvGCyGybX3SdzPHpikUHkME2AP55Ma20uNwkNyY2eSYFX9D55kDrfQmQBR4w==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -3009,8 +3055,8 @@ packages: dev: true optional: true - /esbuild-linux-arm64/0.15.14: - resolution: {integrity: sha512-kle2Ov6a1e5AjlHlMQl1e+c4myGTeggrRzArQFmWp6O6JoqqB9hT+B28EW4tjFWgV/NxUq46pWYpgaWXsXRPAg==} + /esbuild-linux-arm64/0.15.15: + resolution: {integrity: sha512-/ltmNFs0FivZkYsTzAsXIfLQX38lFnwJTWCJts0IbCqWZQe+jjj0vYBNbI0kmXLb3y5NljiM5USVAO1NVkdh2g==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -3036,8 +3082,8 @@ packages: dev: true optional: true - /esbuild-linux-mips64le/0.15.14: - resolution: {integrity: sha512-FVdMYIzOLXUq+OE7XYKesuEAqZhmAIV6qOoYahvUp93oXy0MOVTP370ECbPfGXXUdlvc0TNgkJa3YhEwyZ6MRA==} + /esbuild-linux-mips64le/0.15.15: + resolution: {integrity: sha512-PksEPb321/28GFFxtvL33yVPfnMZihxkEv5zME2zapXGp7fA1X2jYeiTUK+9tJ/EGgcNWuwvtawPxJG7Mmn86A==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -3063,8 +3109,8 @@ packages: dev: true optional: true - /esbuild-linux-ppc64le/0.15.14: - resolution: {integrity: sha512-2NzH+iuzMDA+jjtPjuIz/OhRDf8tzbQ1tRZJI//aT25o1HKc0reMMXxKIYq/8nSHXiJSnYV4ODzTiv45s+h73w==} + /esbuild-linux-ppc64le/0.15.15: + resolution: {integrity: sha512-ek8gJBEIhcpGI327eAZigBOHl58QqrJrYYIZBWQCnH3UnXoeWMrMZLeeZL8BI2XMBhP+sQ6ERctD5X+ajL/AIA==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -3090,8 +3136,8 @@ packages: dev: true optional: true - /esbuild-linux-riscv64/0.15.14: - resolution: {integrity: sha512-VqxvutZNlQxmUNS7Ac+aczttLEoHBJ9e3OYGqnULrfipRvG97qLrAv9EUY9iSrRKBqeEbSvS9bSfstZqwz0T4Q==} + /esbuild-linux-riscv64/0.15.15: + resolution: {integrity: sha512-H5ilTZb33/GnUBrZMNJtBk7/OXzDHDXjIzoLXHSutwwsLxSNaLxzAaMoDGDd/keZoS+GDBqNVxdCkpuiRW4OSw==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -3117,8 +3163,8 @@ packages: dev: true optional: true - /esbuild-linux-s390x/0.15.14: - resolution: {integrity: sha512-+KVHEUshX5n6VP6Vp/AKv9fZIl5kr2ph8EUFmQUJnDpHwcfTSn2AQgYYm0HTBR2Mr4d0Wlr0FxF/Cs5pbFgiOw==} + /esbuild-linux-s390x/0.15.15: + resolution: {integrity: sha512-jKaLUg78mua3rrtrkpv4Or2dNTJU7bgHN4bEjT4OX4GR7nLBSA9dfJezQouTxMmIW7opwEC5/iR9mpC18utnxQ==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -3144,8 +3190,8 @@ packages: dev: true optional: true - /esbuild-netbsd-64/0.15.14: - resolution: {integrity: sha512-6D/dr17piEgevIm1xJfZP2SjB9Z+g8ERhNnBdlZPBWZl+KSPUKLGF13AbvC+nzGh8IxOH2TyTIdRMvKMP0nEzQ==} + /esbuild-netbsd-64/0.15.15: + resolution: {integrity: sha512-aOvmF/UkjFuW6F36HbIlImJTTx45KUCHJndtKo+KdP8Dhq3mgLRKW9+6Ircpm8bX/RcS3zZMMmaBLkvGY06Gvw==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -3171,8 +3217,8 @@ packages: dev: true optional: true - /esbuild-openbsd-64/0.15.14: - resolution: {integrity: sha512-rREQBIlMibBetgr2E9Lywt2Qxv2ZdpmYahR4IUlAQ1Efv/A5gYdO0/VIN3iowDbCNTLxp0bb57Vf0LFcffD6kA==} + /esbuild-openbsd-64/0.15.15: + resolution: {integrity: sha512-HFFX+WYedx1w2yJ1VyR1Dfo8zyYGQZf1cA69bLdrHzu9svj6KH6ZLK0k3A1/LFPhcEY9idSOhsB2UyU0tHPxgQ==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -3198,8 +3244,8 @@ packages: dev: true optional: true - /esbuild-sunos-64/0.15.14: - resolution: {integrity: sha512-DNVjSp/BY4IfwtdUAvWGIDaIjJXY5KI4uD82+15v6k/w7px9dnaDaJJ2R6Mu+KCgr5oklmFc0KjBjh311Gxl9Q==} + /esbuild-sunos-64/0.15.15: + resolution: {integrity: sha512-jOPBudffG4HN8yJXcK9rib/ZTFoTA5pvIKbRrt3IKAGMq1EpBi4xoVoSRrq/0d4OgZLaQbmkHp8RO9eZIn5atA==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -3225,8 +3271,8 @@ packages: dev: true optional: true - /esbuild-windows-32/0.15.14: - resolution: {integrity: sha512-pHBWrcA+/oLgvViuG9FO3kNPO635gkoVrRQwe6ZY1S0jdET07xe2toUvQoJQ8KT3/OkxqUasIty5hpuKFLD+eg==} + /esbuild-windows-32/0.15.15: + resolution: {integrity: sha512-MDkJ3QkjnCetKF0fKxCyYNBnOq6dmidcwstBVeMtXSgGYTy8XSwBeIE4+HuKiSsG6I/mXEb++px3IGSmTN0XiA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -3252,8 +3298,8 @@ packages: dev: true optional: true - /esbuild-windows-64/0.15.14: - resolution: {integrity: sha512-CszIGQVk/P8FOS5UgAH4hKc9zOaFo69fe+k1rqgBHx3CSK3Opyk5lwYriIamaWOVjBt7IwEP6NALz+tkVWdFog==} + /esbuild-windows-64/0.15.15: + resolution: {integrity: sha512-xaAUIB2qllE888SsMU3j9nrqyLbkqqkpQyWVkfwSil6BBPgcPk3zOFitTTncEKCLTQy3XV9RuH7PDj3aJDljWA==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -3279,8 +3325,8 @@ packages: dev: true optional: true - /esbuild-windows-arm64/0.15.14: - resolution: {integrity: sha512-KW9W4psdZceaS9A7Jsgl4WialOznSURvqX/oHZk3gOP7KbjtHLSsnmSvNdzagGJfxbAe30UVGXRe8q8nDsOSQw==} + /esbuild-windows-arm64/0.15.15: + resolution: {integrity: sha512-ttuoCYCIJAFx4UUKKWYnFdrVpoXa3+3WWkXVI6s09U+YjhnyM5h96ewTq/WgQj9LFSIlABQvadHSOQyAVjW5xQ==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -3344,34 +3390,34 @@ packages: esbuild-windows-arm64: 0.14.51 dev: true - /esbuild/0.15.14: - resolution: {integrity: sha512-pJN8j42fvWLFWwSMG4luuupl2Me7mxciUOsMegKvwCmhEbJ2covUdFnihxm0FMIBV+cbwbtMoHgMCCI+pj1btQ==} + /esbuild/0.15.15: + resolution: {integrity: sha512-TEw/lwK4Zzld9x3FedV6jy8onOUHqcEX3ADFk4k+gzPUwrxn8nWV62tH0udo8jOtjFodlEfc4ypsqX3e+WWO6w==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.15.14 - '@esbuild/linux-loong64': 0.15.14 - esbuild-android-64: 0.15.14 - esbuild-android-arm64: 0.15.14 - esbuild-darwin-64: 0.15.14 - esbuild-darwin-arm64: 0.15.14 - esbuild-freebsd-64: 0.15.14 - esbuild-freebsd-arm64: 0.15.14 - esbuild-linux-32: 0.15.14 - esbuild-linux-64: 0.15.14 - esbuild-linux-arm: 0.15.14 - esbuild-linux-arm64: 0.15.14 - esbuild-linux-mips64le: 0.15.14 - esbuild-linux-ppc64le: 0.15.14 - esbuild-linux-riscv64: 0.15.14 - esbuild-linux-s390x: 0.15.14 - esbuild-netbsd-64: 0.15.14 - esbuild-openbsd-64: 0.15.14 - esbuild-sunos-64: 0.15.14 - esbuild-windows-32: 0.15.14 - esbuild-windows-64: 0.15.14 - esbuild-windows-arm64: 0.15.14 + '@esbuild/android-arm': 0.15.15 + '@esbuild/linux-loong64': 0.15.15 + esbuild-android-64: 0.15.15 + esbuild-android-arm64: 0.15.15 + esbuild-darwin-64: 0.15.15 + esbuild-darwin-arm64: 0.15.15 + esbuild-freebsd-64: 0.15.15 + esbuild-freebsd-arm64: 0.15.15 + esbuild-linux-32: 0.15.15 + esbuild-linux-64: 0.15.15 + esbuild-linux-arm: 0.15.15 + esbuild-linux-arm64: 0.15.15 + esbuild-linux-mips64le: 0.15.15 + esbuild-linux-ppc64le: 0.15.15 + esbuild-linux-riscv64: 0.15.15 + esbuild-linux-s390x: 0.15.15 + esbuild-netbsd-64: 0.15.15 + esbuild-openbsd-64: 0.15.15 + esbuild-sunos-64: 0.15.15 + esbuild-windows-32: 0.15.15 + esbuild-windows-64: 0.15.15 + esbuild-windows-arm64: 0.15.15 dev: true /escalade/3.1.1: @@ -3398,8 +3444,8 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - /eslint-config-next/13.0.2_rmayb2veg2btbq6mbmnyivgasy: - resolution: {integrity: sha512-SrrHp+zBDYLjOFZdM5b9aW/pliK687Xxfa+qpDuL08Z04ReHhmz3L+maXaAqgrEVZHQximP7nh0El4yNDJW+CA==} + /eslint-config-next/13.0.4_zksrc6ykdxhogxjbhb5axiabwi: + resolution: {integrity: sha512-moEC7BW2TK7JKq3QfnaauqRjWzVcEf71gp5DbClpFPHM6QXE0u0uVvSTiHlmOgtCe1vyWAO+AhF87ZITd8mIDw==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -3407,32 +3453,32 @@ packages: typescript: optional: true dependencies: - '@next/eslint-plugin-next': 13.0.2 + '@next/eslint-plugin-next': 13.0.4 '@rushstack/eslint-patch': 1.2.0 - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy - eslint: 8.27.0 + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi + eslint: 8.28.0 eslint-import-resolver-node: 0.3.6 - eslint-import-resolver-typescript: 2.7.1_dcpv4nbdr5ks2h5677xdltrk6e - eslint-plugin-import: 2.26.0_ttnp75sbivpcvanbhjbkcsh3ly - eslint-plugin-jsx-a11y: 6.6.1_eslint@8.27.0 - eslint-plugin-react: 7.31.10_eslint@8.27.0 - eslint-plugin-react-hooks: 4.6.0_eslint@8.27.0 + eslint-import-resolver-typescript: 3.5.2_ktrec6dplf4now6nlbc6d67jee + eslint-plugin-import: 2.26.0_d5vn4nsvkp5ugznurcfxmdkaeu + eslint-plugin-jsx-a11y: 6.6.1_eslint@8.28.0 + eslint-plugin-react: 7.31.11_eslint@8.28.0 + eslint-plugin-react-hooks: 4.6.0_eslint@8.28.0 typescript: 4.8.4 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color dev: true - /eslint-config-prettier/8.5.0_eslint@8.27.0: + /eslint-config-prettier/8.5.0_eslint@8.28.0: resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.27.0 + eslint: 8.28.0 dev: true - /eslint-config-standard-with-typescript/23.0.0_iajcbk7iadsd3pvvaviyatln7y: + /eslint-config-standard-with-typescript/23.0.0_sssjb74p4ptzc2ttzoppymtkum: resolution: {integrity: sha512-iaaWifImn37Z1OXbNW1es7KI+S7D408F9ys0bpaQf2temeBWlvb0Nc5qHkOgYaRb5QxTZT32GGeN1gtswASOXA==} peerDependencies: '@typescript-eslint/eslint-plugin': ^5.0.0 @@ -3442,19 +3488,19 @@ packages: eslint-plugin-promise: ^6.0.0 typescript: '*' dependencies: - '@typescript-eslint/eslint-plugin': 5.43.0_yy4vf4gcvxiubmg7fqa55dqe2i - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy - eslint: 8.27.0 - eslint-config-standard: 17.0.0_qmmcbu32ybj7ict4ieqqyvwr3m - eslint-plugin-import: 2.26.0_wbsj7pk5g7hogwvhsif6xntns4 - eslint-plugin-n: 15.5.1_eslint@8.27.0 - eslint-plugin-promise: 6.1.1_eslint@8.27.0 + '@typescript-eslint/eslint-plugin': 5.43.0_ol5heeoi7wmwb4tenztnb7jlze + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi + eslint: 8.28.0 + eslint-config-standard: 17.0.0_5dakk4wnrkkieagghiqvu5yn4y + eslint-plugin-import: 2.26.0_ivdjtymx6ubvknadox4oh4qsue + eslint-plugin-n: 15.5.1_eslint@8.28.0 + eslint-plugin-promise: 6.1.1_eslint@8.28.0 typescript: 4.8.4 transitivePeerDependencies: - supports-color dev: true - /eslint-config-standard/17.0.0_qmmcbu32ybj7ict4ieqqyvwr3m: + /eslint-config-standard/17.0.0_5dakk4wnrkkieagghiqvu5yn4y: resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==} peerDependencies: eslint: ^8.0.1 @@ -3462,20 +3508,20 @@ packages: eslint-plugin-n: ^15.0.0 eslint-plugin-promise: ^6.0.0 dependencies: - eslint: 8.27.0 - eslint-plugin-import: 2.26.0_wbsj7pk5g7hogwvhsif6xntns4 - eslint-plugin-n: 15.5.1_eslint@8.27.0 - eslint-plugin-promise: 6.1.1_eslint@8.27.0 + eslint: 8.28.0 + eslint-plugin-import: 2.26.0_ivdjtymx6ubvknadox4oh4qsue + eslint-plugin-n: 15.5.1_eslint@8.28.0 + eslint-plugin-promise: 6.1.1_eslint@8.28.0 dev: true - /eslint-etc/5.2.0_rmayb2veg2btbq6mbmnyivgasy: + /eslint-etc/5.2.0_zksrc6ykdxhogxjbhb5axiabwi: resolution: {integrity: sha512-Gcm/NMa349FOXb1PEEfNMMyIANuorIc2/mI5Vfu1zENNsz+FBVhF62uY6gPUCigm/xDOc8JOnl+71WGnlzlDag==} peerDependencies: eslint: ^8.0.0 typescript: ^4.0.0 dependencies: - '@typescript-eslint/experimental-utils': 5.43.0_rmayb2veg2btbq6mbmnyivgasy - eslint: 8.27.0 + '@typescript-eslint/experimental-utils': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi + eslint: 8.28.0 tsutils: 3.21.0_typescript@4.8.4 tsutils-etc: 1.4.1_mhfzdf4crgayux52p57kxjyixi typescript: 4.8.4 @@ -3492,25 +3538,27 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript/2.7.1_dcpv4nbdr5ks2h5677xdltrk6e: - resolution: {integrity: sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==} - engines: {node: '>=4'} + /eslint-import-resolver-typescript/3.5.2_ktrec6dplf4now6nlbc6d67jee: + resolution: {integrity: sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ==} + engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' dependencies: debug: 4.3.4 - eslint: 8.27.0 - eslint-plugin-import: 2.26.0_ttnp75sbivpcvanbhjbkcsh3ly - glob: 7.2.3 + enhanced-resolve: 5.10.0 + eslint: 8.28.0 + eslint-plugin-import: 2.26.0_d5vn4nsvkp5ugznurcfxmdkaeu + get-tsconfig: 4.2.0 + globby: 13.1.2 + is-core-module: 2.11.0 is-glob: 4.0.3 - resolve: 1.22.1 - tsconfig-paths: 3.14.1 + synckit: 0.8.4 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils/2.7.4_7n62batgpjg7oa6ssbsyzoyo34: + /eslint-module-utils/2.7.4_4sj2pgnhb6wbtprrvnb5kjfgfe: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -3531,15 +3579,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi debug: 3.2.7 - eslint: 8.27.0 + eslint: 8.28.0 eslint-import-resolver-node: 0.3.6 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils/2.7.4_xrelmojid5azn576bwxzifnii4: + /eslint-module-utils/2.7.4_hkyvkcuamndkjh6xd3yrie643u: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -3560,36 +3608,36 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi debug: 3.2.7 - eslint: 8.27.0 + eslint: 8.28.0 eslint-import-resolver-node: 0.3.6 - eslint-import-resolver-typescript: 2.7.1_dcpv4nbdr5ks2h5677xdltrk6e + eslint-import-resolver-typescript: 3.5.2_ktrec6dplf4now6nlbc6d67jee transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-es/4.1.0_eslint@8.27.0: + /eslint-plugin-es/4.1.0_eslint@8.28.0: resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} engines: {node: '>=8.10.0'} peerDependencies: eslint: '>=4.19.1' dependencies: - eslint: 8.27.0 + eslint: 8.28.0 eslint-utils: 2.1.0 regexpp: 3.2.0 dev: true - /eslint-plugin-etc/2.0.2_rmayb2veg2btbq6mbmnyivgasy: + /eslint-plugin-etc/2.0.2_zksrc6ykdxhogxjbhb5axiabwi: resolution: {integrity: sha512-g3b95LCdTCwZA8On9EICYL8m1NMWaiGfmNUd/ftZTeGZDXrwujKXUr+unYzqKjKFo1EbqJ31vt+Dqzrdm/sUcw==} peerDependencies: eslint: ^8.0.0 typescript: ^4.0.0 dependencies: '@phenomnomnominal/tsquery': 4.2.0_typescript@4.8.4 - '@typescript-eslint/experimental-utils': 5.43.0_rmayb2veg2btbq6mbmnyivgasy - eslint: 8.27.0 - eslint-etc: 5.2.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/experimental-utils': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi + eslint: 8.28.0 + eslint-etc: 5.2.0_zksrc6ykdxhogxjbhb5axiabwi requireindex: 1.2.0 tslib: 2.4.1 tsutils: 3.21.0_typescript@4.8.4 @@ -3598,7 +3646,7 @@ packages: - supports-color dev: true - /eslint-plugin-import/2.26.0_ttnp75sbivpcvanbhjbkcsh3ly: + /eslint-plugin-import/2.26.0_d5vn4nsvkp5ugznurcfxmdkaeu: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: @@ -3608,14 +3656,14 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi array-includes: 3.1.6 array.prototype.flat: 1.3.1 debug: 2.6.9 doctrine: 2.1.0 - eslint: 8.27.0 + eslint: 8.28.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.4_xrelmojid5azn576bwxzifnii4 + eslint-module-utils: 2.7.4_hkyvkcuamndkjh6xd3yrie643u has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -3629,7 +3677,7 @@ packages: - supports-color dev: true - /eslint-plugin-import/2.26.0_wbsj7pk5g7hogwvhsif6xntns4: + /eslint-plugin-import/2.26.0_ivdjtymx6ubvknadox4oh4qsue: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: @@ -3639,14 +3687,14 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi array-includes: 3.1.6 array.prototype.flat: 1.3.1 debug: 2.6.9 doctrine: 2.1.0 - eslint: 8.27.0 + eslint: 8.28.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.4_7n62batgpjg7oa6ssbsyzoyo34 + eslint-module-utils: 2.7.4_4sj2pgnhb6wbtprrvnb5kjfgfe has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -3660,7 +3708,7 @@ packages: - supports-color dev: true - /eslint-plugin-jsdoc/39.6.2_eslint@8.27.0: + /eslint-plugin-jsdoc/39.6.2_eslint@8.28.0: resolution: {integrity: sha512-dvgY/W7eUFoAIIiaWHERIMI61ZWqcz9YFjEeyTzdPlrZc3TY/3aZm5aB91NUoTLWYZmO/vFlYSuQi15tF7uE5A==} engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} peerDependencies: @@ -3670,7 +3718,7 @@ packages: comment-parser: 1.3.1 debug: 4.3.4 escape-string-regexp: 4.0.0 - eslint: 8.27.0 + eslint: 8.28.0 esquery: 1.4.0 semver: 7.3.8 spdx-expression-parse: 3.0.1 @@ -3678,7 +3726,7 @@ packages: - supports-color dev: true - /eslint-plugin-jsx-a11y/6.6.1_eslint@8.27.0: + /eslint-plugin-jsx-a11y/6.6.1_eslint@8.28.0: resolution: {integrity: sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==} engines: {node: '>=4.0'} peerDependencies: @@ -3692,7 +3740,7 @@ packages: axobject-query: 2.2.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 8.27.0 + eslint: 8.28.0 has: 1.0.3 jsx-ast-utils: 3.3.3 language-tags: 1.0.5 @@ -3700,16 +3748,16 @@ packages: semver: 6.3.0 dev: true - /eslint-plugin-n/15.5.1_eslint@8.27.0: + /eslint-plugin-n/15.5.1_eslint@8.28.0: resolution: {integrity: sha512-kAd+xhZm7brHoFLzKLB7/FGRFJNg/srmv67mqb7tto22rpr4wv/LV6RuXzAfv3jbab7+k1wi42PsIhGviywaaw==} engines: {node: '>=12.22.0'} peerDependencies: eslint: '>=7.0.0' dependencies: builtins: 5.0.1 - eslint: 8.27.0 - eslint-plugin-es: 4.1.0_eslint@8.27.0 - eslint-utils: 3.0.0_eslint@8.27.0 + eslint: 8.28.0 + eslint-plugin-es: 4.1.0_eslint@8.28.0 + eslint-utils: 3.0.0_eslint@8.28.0 ignore: 5.2.0 is-core-module: 2.11.0 minimatch: 3.1.2 @@ -3722,34 +3770,35 @@ packages: engines: {node: '>=5.0.0'} dev: true - /eslint-plugin-promise/6.1.1_eslint@8.27.0: + /eslint-plugin-promise/6.1.1_eslint@8.28.0: resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.27.0 + eslint: 8.28.0 dev: true - /eslint-plugin-react-hooks/4.6.0_eslint@8.27.0: + /eslint-plugin-react-hooks/4.6.0_eslint@8.28.0: resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.27.0 + eslint: 8.28.0 dev: true - /eslint-plugin-react/7.31.10_eslint@8.27.0: - resolution: {integrity: sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==} + /eslint-plugin-react/7.31.11_eslint@8.28.0: + resolution: {integrity: sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: array-includes: 3.1.6 array.prototype.flatmap: 1.3.1 + array.prototype.tosorted: 1.1.1 doctrine: 2.1.0 - eslint: 8.27.0 + eslint: 8.28.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.3 minimatch: 3.1.2 @@ -3763,7 +3812,7 @@ packages: string.prototype.matchall: 4.0.8 dev: true - /eslint-plugin-unicorn/44.0.2_eslint@8.27.0: + /eslint-plugin-unicorn/44.0.2_eslint@8.28.0: resolution: {integrity: sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==} engines: {node: '>=14.18'} peerDependencies: @@ -3772,8 +3821,8 @@ packages: '@babel/helper-validator-identifier': 7.19.1 ci-info: 3.6.1 clean-regexp: 1.0.0 - eslint: 8.27.0 - eslint-utils: 3.0.0_eslint@8.27.0 + eslint: 8.28.0 + eslint-utils: 3.0.0_eslint@8.28.0 esquery: 1.4.0 indent-string: 4.0.0 is-builtin-module: 3.2.0 @@ -3809,13 +3858,13 @@ packages: eslint-visitor-keys: 1.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.27.0: + /eslint-utils/3.0.0_eslint@8.28.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.27.0 + eslint: 8.28.0 eslint-visitor-keys: 2.1.0 dev: true @@ -3834,8 +3883,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.27.0: - resolution: {integrity: sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==} + /eslint/8.28.0: + resolution: {integrity: sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: @@ -3850,7 +3899,7 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.27.0 + eslint-utils: 3.0.0_eslint@8.28.0 eslint-visitor-keys: 3.3.0 espree: 9.4.1 esquery: 1.4.0 @@ -3859,14 +3908,14 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.17.0 + globals: 13.18.0 grapheme-splitter: 1.0.4 ignore: 5.2.0 import-fresh: 3.3.0 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-sdsl: 4.1.5 + js-sdsl: 4.2.0 js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 @@ -4174,6 +4223,10 @@ packages: get-intrinsic: 1.1.3 dev: true + /get-tsconfig/4.2.0: + resolution: {integrity: sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==} + dev: true + /git-rev-sync/3.0.2: resolution: {integrity: sha512-Nd5RiYpyncjLv0j6IONy0lGzAqdRXUaBctuGBbrEA2m6Bn4iDrN/9MeQTXuiquw8AEKL9D2BW0nw5m/lQvxqnQ==} dependencies: @@ -4233,13 +4286,17 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals/13.17.0: - resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==} + /globals/13.18.0: + resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 dev: true + /globalyzer/0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + dev: true + /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -4263,6 +4320,10 @@ packages: slash: 4.0.0 dev: true + /globrex/0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + /gopd/1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -4339,21 +4400,21 @@ packages: resolution: {integrity: sha512-Pg7yAvasl8QRsymK/m0S184fDPiz03/VOAdbRbHdsaFcp41drGh9NdzG2jLJHpRuNoWxL/bH0R4kSULT6DvpSw==} engines: {node: '>=14'} dependencies: - '@typescript-eslint/eslint-plugin': 5.43.0_yy4vf4gcvxiubmg7fqa55dqe2i - '@typescript-eslint/parser': 5.43.0_rmayb2veg2btbq6mbmnyivgasy - eslint: 8.27.0 - eslint-config-prettier: 8.5.0_eslint@8.27.0 - eslint-config-standard: 17.0.0_qmmcbu32ybj7ict4ieqqyvwr3m - eslint-config-standard-with-typescript: 23.0.0_iajcbk7iadsd3pvvaviyatln7y - eslint-plugin-etc: 2.0.2_rmayb2veg2btbq6mbmnyivgasy - eslint-plugin-import: 2.26.0_wbsj7pk5g7hogwvhsif6xntns4 - eslint-plugin-jsdoc: 39.6.2_eslint@8.27.0 - eslint-plugin-n: 15.5.1_eslint@8.27.0 + '@typescript-eslint/eslint-plugin': 5.43.0_ol5heeoi7wmwb4tenztnb7jlze + '@typescript-eslint/parser': 5.43.0_zksrc6ykdxhogxjbhb5axiabwi + eslint: 8.28.0 + eslint-config-prettier: 8.5.0_eslint@8.28.0 + eslint-config-standard: 17.0.0_5dakk4wnrkkieagghiqvu5yn4y + eslint-config-standard-with-typescript: 23.0.0_sssjb74p4ptzc2ttzoppymtkum + eslint-plugin-etc: 2.0.2_zksrc6ykdxhogxjbhb5axiabwi + eslint-plugin-import: 2.26.0_ivdjtymx6ubvknadox4oh4qsue + eslint-plugin-jsdoc: 39.6.2_eslint@8.28.0 + eslint-plugin-n: 15.5.1_eslint@8.28.0 eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-promise: 6.1.1_eslint@8.27.0 - eslint-plugin-react: 7.31.10_eslint@8.27.0 - eslint-plugin-react-hooks: 4.6.0_eslint@8.27.0 - eslint-plugin-unicorn: 44.0.2_eslint@8.27.0 + eslint-plugin-promise: 6.1.1_eslint@8.28.0 + eslint-plugin-react: 7.31.11_eslint@8.28.0 + eslint-plugin-react-hooks: 4.6.0_eslint@8.28.0 + eslint-plugin-unicorn: 44.0.2_eslint@8.28.0 lint-staged: 13.0.3 prettier: 2.7.1 simple-git-hooks: 2.8.1 @@ -4607,6 +4668,12 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-docker/2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + /is-error/2.2.2: resolution: {integrity: sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==} dev: true @@ -4785,6 +4852,13 @@ packages: get-intrinsic: 1.1.3 dev: true + /is-wsl/2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + /isarray/1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: true @@ -4889,8 +4963,8 @@ packages: resolution: {integrity: sha512-u7I6qhhxH7pSevcYNaMECtkvZW365ARqAIt9K+xjdK1B2WUDEjQSfETkOCT8bxFq/59LqrN3cMLUtTgmDBaygw==} dev: true - /js-sdsl/4.1.5: - resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==} + /js-sdsl/4.2.0: + resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} dev: true /js-string-escape/1.0.1: @@ -4952,6 +5026,10 @@ packages: minimist: 1.2.7 dev: true + /jsonc-parser/3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: false + /jsx-ast-utils/3.3.3: resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} engines: {node: '>=4.0'} @@ -5134,6 +5212,10 @@ packages: dependencies: yallist: 4.0.0 + /lunr/2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + dev: false + /magic-string/0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: @@ -5154,6 +5236,12 @@ packages: p-defer: 1.0.0 dev: true + /marked/4.2.3: + resolution: {integrity: sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw==} + engines: {node: '>= 12'} + hasBin: true + dev: false + /matcher/5.0.0: resolution: {integrity: sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5343,6 +5431,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch/5.1.0: + resolution: {integrity: sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: false + /minimist/1.2.7: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} dev: true @@ -5462,8 +5557,8 @@ packages: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} dev: true - /next/13.0.2_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-uQ5z5e4D9mOe8+upy6bQdYYjo/kk1v3jMW87kTy2TgAyAsEO+CkwRnMgyZ4JoHEnhPZLHwh7dk0XymRNLe1gFw==} + /next/13.0.4_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-4P0MvbjPCI1E/UPL1GrTXtYlgFnbBbY3JQ+AMY8jYE2SwyvCWctEJySoRjveznAHjrl6TIjuAJeB8u1c2StYUQ==} engines: {node: '>=14.6.0'} hasBin: true peerDependencies: @@ -5480,28 +5575,28 @@ packages: sass: optional: true dependencies: - '@next/env': 13.0.2 + '@next/env': 13.0.4 '@swc/helpers': 0.4.11 - caniuse-lite: 1.0.30001431 + caniuse-lite: 1.0.30001434 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 styled-jsx: 5.1.0_react@18.2.0 use-sync-external-store: 1.2.0_react@18.2.0 optionalDependencies: - '@next/swc-android-arm-eabi': 13.0.2 - '@next/swc-android-arm64': 13.0.2 - '@next/swc-darwin-arm64': 13.0.2 - '@next/swc-darwin-x64': 13.0.2 - '@next/swc-freebsd-x64': 13.0.2 - '@next/swc-linux-arm-gnueabihf': 13.0.2 - '@next/swc-linux-arm64-gnu': 13.0.2 - '@next/swc-linux-arm64-musl': 13.0.2 - '@next/swc-linux-x64-gnu': 13.0.2 - '@next/swc-linux-x64-musl': 13.0.2 - '@next/swc-win32-arm64-msvc': 13.0.2 - '@next/swc-win32-ia32-msvc': 13.0.2 - '@next/swc-win32-x64-msvc': 13.0.2 + '@next/swc-android-arm-eabi': 13.0.4 + '@next/swc-android-arm64': 13.0.4 + '@next/swc-darwin-arm64': 13.0.4 + '@next/swc-darwin-x64': 13.0.4 + '@next/swc-freebsd-x64': 13.0.4 + '@next/swc-linux-arm-gnueabihf': 13.0.4 + '@next/swc-linux-arm64-gnu': 13.0.4 + '@next/swc-linux-arm64-musl': 13.0.4 + '@next/swc-linux-x64-gnu': 13.0.4 + '@next/swc-linux-x64-musl': 13.0.4 + '@next/swc-win32-arm64-msvc': 13.0.4 + '@next/swc-win32-ia32-msvc': 13.0.4 + '@next/swc-win32-x64-msvc': 13.0.4 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -5688,6 +5783,15 @@ packages: mimic-fn: 4.0.0 dev: true + /open/8.4.0: + resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} @@ -5963,7 +6067,6 @@ packages: /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: false /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -6319,8 +6422,8 @@ packages: resolve: 1.22.1 dev: true - /regenerator-runtime/0.13.10: - resolution: {integrity: sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==} + /regenerator-runtime/0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} dev: true /regexp-tree/0.1.24: @@ -6599,6 +6702,14 @@ packages: rechoir: 0.6.2 dev: true + /shiki/0.11.1: + resolution: {integrity: sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==} + dependencies: + jsonc-parser: 3.2.0 + vscode-oniguruma: 1.6.2 + vscode-textmate: 6.0.0 + dev: false + /side-channel/1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: @@ -6780,6 +6891,7 @@ packages: /streamsearch/1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + dev: true /string-argv/0.3.1: resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} @@ -6968,6 +7080,19 @@ packages: engines: {node: '>= 0.4'} dev: true + /synckit/0.8.4: + resolution: {integrity: sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/utils': 2.3.1 + tslib: 2.4.1 + dev: true + + /tapable/2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + /tape/5.6.1: resolution: {integrity: sha512-reNzS3rzsJtKk0f+zJx2XlzIsjJXlIcOIrIxk5shHAG/DzW3BKyMg8UfN79oluYlcWo4lIt56ahLqwgpRT4idg==} hasBin: true @@ -7056,6 +7181,13 @@ packages: engines: {node: '>=4'} dev: true + /tiny-glob/0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + dev: true + /tmp/0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -7189,17 +7321,32 @@ packages: engines: {node: '>=14.16'} dev: false - /typescript/4.8.4: - resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} - engines: {node: '>=4.2.0'} + /typedoc-plugin-missing-exports/1.0.0_typedoc@0.23.21: + resolution: {integrity: sha512-7s6znXnuAj1eD9KYPyzVzR1lBF5nwAY8IKccP5sdoO9crG4lpd16RoFpLsh2PccJM+I2NASpr0+/NMka6ThwVA==} + peerDependencies: + typedoc: 0.22.x || 0.23.x + dependencies: + typedoc: 0.23.21_typescript@4.8.4 + dev: false + + /typedoc/0.23.21_typescript@4.8.4: + resolution: {integrity: sha512-VNE9Jv7BgclvyH9moi2mluneSviD43dCE9pY8RWkO88/DrEgJZk9KpUk7WO468c9WWs/+aG6dOnoH7ccjnErhg==} + engines: {node: '>= 14.14'} hasBin: true - dev: true + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x + dependencies: + lunr: 2.3.9 + marked: 4.2.3 + minimatch: 5.1.0 + shiki: 0.11.1 + typescript: 4.8.4 + dev: false - /typescript/4.9.3: - resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} + /typescript/4.8.4: + resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} engines: {node: '>=4.2.0'} hasBin: true - dev: true /uint8arrays/4.0.2: resolution: {integrity: sha512-8CWXXZdOvVrIL4SeY/Gnp+idxxiGK4XFkP4FY26Sx/fpTz/b6vv4BVWELMDzQweSyyhdcuAcU14H6izzB6k1Cw==} @@ -7216,13 +7363,6 @@ packages: which-boxed-primitive: 1.0.2 dev: true - /undici/5.12.0: - resolution: {integrity: sha512-zMLamCG62PGjd9HHMpo05bSLvvwWOZgGeiWlN/vlqu3+lRo3elxktVGEyLMX+IO7c2eflLjcW74AlkhEZm15mg==} - engines: {node: '>=12.18'} - dependencies: - busboy: 1.6.0 - dev: false - /undici/5.9.1: resolution: {integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==} engines: {node: '>=12.18'} @@ -7296,6 +7436,14 @@ packages: /varint/6.0.0: resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + /vscode-oniguruma/1.6.2: + resolution: {integrity: sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==} + dev: false + + /vscode-textmate/6.0.0: + resolution: {integrity: sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==} + dev: false + /watch/1.0.2: resolution: {integrity: sha512-1u+Z5n9Jc1E2c7qDO8SinPoZuHj7FgbgU1olSFoyaklduDvvtX7GMMtlE6OC9FTXq4KvNAOfj6Zu4vI1e9bAKA==} engines: {node: '>=0.1.95'} @@ -7412,8 +7560,8 @@ packages: resolution: {integrity: sha512-y38RiVOEJPmqSSQniVPKQrWE8XocqeRWO9TwgfXTMARzQa3aGwsaBwUdOBn5MJukZwIkGOYsljK76kMGLu5aOA==} dev: false - /wrangler/2.3.2: - resolution: {integrity: sha512-PWqNJclB7SGIRpuJSrF9zuTfLAqrS1VPembhf6HPGJC6zL96hx3XcHC01VjChC0lsadUqGRa7/iEvWtwdUy6kw==} + /wrangler/2.4.2: + resolution: {integrity: sha512-fgcP+y4aEXozeP0hGKohfodxBm1ah/qwclw499TvoBogLKEhkPHH2w+MHh2PBFek467I4iaOLpSh4Sho+J6GuQ==} engines: {node: '>=16.13.0'} hasBin: true dependencies: diff --git a/spec/docs.css b/spec/docs.css new file mode 100644 index 000000000..be72e0923 --- /dev/null +++ b/spec/docs.css @@ -0,0 +1,3 @@ +a[href*='_internal_.html'] + ul { + display: none; +} diff --git a/tsconfig.json b/tsconfig.json index 610b3e99b..f56941d30 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,5 +32,15 @@ "skipLibCheck": true, "stripInternal": true, "resolveJsonModule": true + }, + "typedocOptions": { + "entryPointStrategy": "packages", + "entryPoints": ["packages/access-client", "packages/upload-client"], + "excludeExternals": true, + "darkHighlightTheme": "github-dark", + "navigationLinks": { + "Github": "https://github.com/web3-storage/w3protocol" + }, + "customCss": "./spec/docs.css" } }