From 59bb673c4b24a6aa7cda6c1421fcdc9925a0ef27 Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Wed, 5 Oct 2022 16:13:37 +0100 Subject: [PATCH] feat: attach pipeline --- .npmrc | 1 + package.json | 4 + packages/attach-pipeline/README.md | 39 + packages/attach-pipeline/attach-pipeline.png | Bin 0 -> 51056 bytes packages/attach-pipeline/ava.config.js | 6 + packages/attach-pipeline/package.json | 44 + packages/attach-pipeline/scripts/build.js | 63 ++ packages/attach-pipeline/scripts/cli.js | 16 + packages/attach-pipeline/src/auth.js | 54 ++ packages/attach-pipeline/src/bindings.d.ts | 85 ++ packages/attach-pipeline/src/cors.js | 35 + packages/attach-pipeline/src/env.js | 70 ++ packages/attach-pipeline/src/error-handler.js | 22 + packages/attach-pipeline/src/errors.js | 66 ++ packages/attach-pipeline/src/handlers.js | 46 + packages/attach-pipeline/src/index.js | 115 +++ packages/attach-pipeline/src/version.js | 15 + packages/attach-pipeline/test/auth.spec.js | 32 + packages/attach-pipeline/test/index.spec.js | 80 ++ .../attach-pipeline/test/utils/miniflare.js | 33 + packages/attach-pipeline/test/utils/setup.js | 23 + .../test/utils/worker-globals.js | 3 + packages/attach-pipeline/test/version.spec.js | 29 + packages/attach-pipeline/tsconfig.json | 17 + packages/attach-pipeline/wrangler.toml | 74 ++ pnpm-lock.yaml | 867 +++++++++++++++++- 26 files changed, 1814 insertions(+), 25 deletions(-) create mode 100644 .npmrc create mode 100644 packages/attach-pipeline/README.md create mode 100644 packages/attach-pipeline/attach-pipeline.png create mode 100644 packages/attach-pipeline/ava.config.js create mode 100644 packages/attach-pipeline/package.json create mode 100644 packages/attach-pipeline/scripts/build.js create mode 100644 packages/attach-pipeline/scripts/cli.js create mode 100644 packages/attach-pipeline/src/auth.js create mode 100644 packages/attach-pipeline/src/bindings.d.ts create mode 100644 packages/attach-pipeline/src/cors.js create mode 100644 packages/attach-pipeline/src/env.js create mode 100644 packages/attach-pipeline/src/error-handler.js create mode 100644 packages/attach-pipeline/src/errors.js create mode 100644 packages/attach-pipeline/src/handlers.js create mode 100644 packages/attach-pipeline/src/index.js create mode 100644 packages/attach-pipeline/src/version.js create mode 100644 packages/attach-pipeline/test/auth.spec.js create mode 100644 packages/attach-pipeline/test/index.spec.js create mode 100644 packages/attach-pipeline/test/utils/miniflare.js create mode 100644 packages/attach-pipeline/test/utils/setup.js create mode 100644 packages/attach-pipeline/test/utils/worker-globals.js create mode 100644 packages/attach-pipeline/test/version.spec.js create mode 100644 packages/attach-pipeline/tsconfig.json create mode 100644 packages/attach-pipeline/wrangler.toml diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..f87a044 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +auto-install-peers=true \ No newline at end of file diff --git a/package.json b/package.json index 26619ee..970cdfa 100644 --- a/package.json +++ b/package.json @@ -8,18 +8,22 @@ "license": "Apache-2.0 OR MIT", "scripts": { "lint": "run-s lint:no-fix-*", + "lint:no-fix-attach-pipeline": "pnpm --filter attach-pipeline lint", "lint:no-fix-cid-verifier": "pnpm --filter cid-verifier lint", "lint:no-fix-edge-gateway": "pnpm --filter edge-gateway lint", "lint:no-fix-ipfs-gateway-race": "pnpm --filter ipfs-gateway-race lint", "lint:fix": "run-s lint:fix-*", + "lint:fix-attach-pipeline": "pnpm --filter attach-pipeline lint --fix", "lint:fix-cid-verifier": "pnpm --filter cid-verifier lint --fix", "lint:fix-edge-gateway": "pnpm --filter edge-gateway lint --fix", "lint:fix-ipfs-gateway-race": "pnpm --filter ipfs-gateway-race lint --fix", "build": "run-s build:*", + "build:attach-pipeline": "pnpm --filter attach-pipeline build", "build:cid-verifier": "pnpm --filter cid-verifier build", "build:edge-gateway": "pnpm --filter edge-gateway build", "build:ipfs-gateway-race": "pnpm --filter ipfs-gateway-race build", "test": "run-s test:*", + "test:attach-pipeline": "pnpm --filter attach-pipeline test", "test:cid-verifier": "pnpm --filter cid-verifier test", "test:edge-gateway": "pnpm --filter edge-gateway test", "test:ipfs-gateway-race": "pnpm --filter ipfs-gateway-race test", diff --git a/packages/attach-pipeline/README.md b/packages/attach-pipeline/README.md new file mode 100644 index 0000000..98685bd --- /dev/null +++ b/packages/attach-pipeline/README.md @@ -0,0 +1,39 @@ +# attach-write-to-read + +> Attach Pipeline is responsible for pulling CAR files written in a S3 bucket and streamming them into R2. + +# Table of Contents + +- [High level attach pipeline architecture](#high-level-attach-pipeline-architecture) +- [Contributing](#contributing) +- [License](#license) + +## High level attach pipeline architecture + +![High level Architecture](./attach-pipeline.png) + +The attach pipeline is composed by: +- a Gateway API running in Cloudlare Workers +- a Attach Queue to trigger requests to pull CARs +- a Car Puller handler running in Cloudflare Workers +- a R2 bucket + +The Gateway endpoint receives requests with batches of CAR URLs (from S3) to be pulled into R2. + +The Car Puller handler, which pulls CAR files from S3 and write them into R2 (mirroring with same key format `carCid/carCid.car`). Worth pointing out that Car Puller API is currently opinated and requires the `ETag` header provided on AWS S3 response to guarantee data integrity while writing to R2 via md5. + +## Usage + +The `attach-pipeline` entry point is the Gateway API. It exposes an authenticated endpoint that receives requests with batches of CAR URLs (from S3) and delegates the batch processing to a Queue. + +```console +$ echo -e '{"bafy0":"https://cars.s3.amazonaws.com/bafy0/bafy0.car"}' | curl -X POST -H 'Authorization: Basic ACCESS_KEY=' --data-binary @- https://attach-gateway.web3.storage.web3.storage +``` + +# Contributing + +Feel free to join in. All welcome. [Open an issue](https://github.com/web3-storage/attach-pipeline/issues)! + +# License + +Dual-licensed under [MIT + Apache 2.0](https://github.com/web3-storage/attach-pipeline/blob/main/LICENSE.md) diff --git a/packages/attach-pipeline/attach-pipeline.png b/packages/attach-pipeline/attach-pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..0d307136d19b8e82e38effcd8a6cb1e12c9b4aca GIT binary patch literal 51056 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 N`Hh=$^cw&;RUD9C!-Jn@_OaZ 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 RQ9STZRZ&x+T+Tf3{{i02CL#a; literal 0 HcmV?d00001 diff --git a/packages/attach-pipeline/ava.config.js b/packages/attach-pipeline/ava.config.js new file mode 100644 index 0000000..f620f52 --- /dev/null +++ b/packages/attach-pipeline/ava.config.js @@ -0,0 +1,6 @@ +export default { + files: ['test/*.spec.js'], + timeout: '5m', + concurrency: 1, + nodeArguments: ['--experimental-vm-modules'] +} diff --git a/packages/attach-pipeline/package.json b/packages/attach-pipeline/package.json new file mode 100644 index 0000000..28e3355 --- /dev/null +++ b/packages/attach-pipeline/package.json @@ -0,0 +1,44 @@ +{ + "name": "attach-pipeline", + "version": "1.0.0", + "description": "Attach Pipeline is responsible for pulling CAR files written in a S3 bucket and streamming them into R2", + "private": true, + "type": "module", + "main": "./dist/worker.js", + "scripts": { + "lint": "standard", + "build": "tsc && node scripts/cli.js build", + "dev": "miniflare dist/worker.js --watch --debug -m", + "test": "npm run test:setup && npm run test:worker", + "test:worker": "ava --verbose test/*.spec.js", + "test:setup": "npm run build" + }, + "dependencies": { + "@web3-storage/worker-utils": "^0.4.3-dev", + "itty-router": "^2.6.6", + "toucan-js": "^2.7.0" + }, + "devDependencies": { + "@cloudflare/workers-types": "^3.16.0", + "@miniflare/core": "^2.10.0", + "@sentry/cli": "^1.71.0", + "@types/git-rev-sync": "^2.0.0", + "@web-std/fetch": "^4.0.0", + "ava": "^3.15.0", + "esbuild": "^0.15.10", + "git-rev-sync": "^3.0.1", + "miniflare": "^2.10.0", + "sade": "^1.8.1", + "standard": "^17.0.0", + "typescript": "4.8.4", + "toml": "^3.0.0", + "undici": "^5.9.1" + }, + "standard": { + "ignore": [ + "dist" + ] + }, + "author": "Vasco Santos ", + "license": "Apache-2.0 OR MIT" +} diff --git a/packages/attach-pipeline/scripts/build.js b/packages/attach-pipeline/scripts/build.js new file mode 100644 index 0000000..e2ec36e --- /dev/null +++ b/packages/attach-pipeline/scripts/build.js @@ -0,0 +1,63 @@ +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' +import { build } from 'esbuild' +import git from 'git-rev-sync' +import Sentry from '@sentry/cli' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const pkg = JSON.parse( + fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8') +) + +/** + * @param {{ env: string; }} opts + */ +export async function buildCmd (opts) { + const sentryRelease = `attach-pipeline@${pkg.version}-${opts.env}+${git.short( + __dirname + )}` + console.log(`Building ${sentryRelease}`) + + await build({ + entryPoints: [path.join(__dirname, '..', 'src', 'index.js')], + bundle: true, + format: 'esm', + outfile: path.join(__dirname, '..', 'dist', 'worker.js'), + legalComments: 'external', + define: { + SENTRY_RELEASE: JSON.stringify(sentryRelease), + VERSION: JSON.stringify(pkg.version), + COMMITHASH: JSON.stringify(git.long(__dirname)), + BRANCH: JSON.stringify(git.branch(__dirname)), + global: 'globalThis' + }, + minify: opts.env !== 'dev', + sourcemap: 'external' + }) + + // Sentry release and sourcemap upload + if (process.env.SENTRY_UPLOAD === 'true') { + const cli = new Sentry(undefined, { + authToken: process.env.SENTRY_TOKEN, + org: 'protocol-labs-it', + project: 'attach-pipeline', + dist: git.short(__dirname) + }) + + await cli.releases.new(sentryRelease) + await cli.releases.setCommits(sentryRelease, { + auto: true, + ignoreEmpty: true, + ignoreMissing: true + }) + await cli.releases.uploadSourceMaps(sentryRelease, { + include: [path.join(__dirname, '..', 'dist')], + ext: ['map', 'js'] + }) + await cli.releases.finalize(sentryRelease) + await cli.releases.newDeploy(sentryRelease, { + env: opts.env + }) + } +} diff --git a/packages/attach-pipeline/scripts/cli.js b/packages/attach-pipeline/scripts/cli.js new file mode 100644 index 0000000..1e967d6 --- /dev/null +++ b/packages/attach-pipeline/scripts/cli.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +import sade from 'sade' + +import { buildCmd } from './build.js' + +const env = process.env.ENV || 'dev' +const prog = sade('attach-pipeline') + +prog + .command('build') + .describe('Build the worker.') + .option('--env', 'Environment', env) + .action(buildCmd) + +prog.parse(process.argv) diff --git a/packages/attach-pipeline/src/auth.js b/packages/attach-pipeline/src/auth.js new file mode 100644 index 0000000..198fc20 --- /dev/null +++ b/packages/attach-pipeline/src/auth.js @@ -0,0 +1,54 @@ +import { + NoTokenError, + ExpectedBasicStringError, + NoValidTokenError +} from './errors.js' + +/** + * Middleware: verify the request is authenticated with a valid JWT token. + * + * @param {import('itty-router').RouteHandler} handler + * @returns {import('itty-router').RouteHandler} + */ +export function withAuthToken (handler) { + /** + * @param {Request} request + * @param {import('./env').Env} env + * @returns {Promise} + */ + return async (request, env, ctx) => { + const token = getTokenFromRequest(request) + if (token !== env.SECRET) { + throw new NoValidTokenError() + } + return handler(request, env, ctx) + } +} + +/** + * @param {Request} request + */ +function getTokenFromRequest (request) { + const authHeader = request.headers.get('Authorization') || '' + if (!authHeader) { + throw new NoTokenError() + } + + const token = parseAuthorizationHeader(authHeader) + if (!token) { + throw new NoTokenError() + } + return token +} + +/** + * @param {string} header + * @returns + */ +function parseAuthorizationHeader (header) { + if (!header.toLowerCase().startsWith('basic ')) { + throw new ExpectedBasicStringError() + } + + return header.substring(6) +} diff --git a/packages/attach-pipeline/src/bindings.d.ts b/packages/attach-pipeline/src/bindings.d.ts new file mode 100644 index 0000000..79a4bf6 --- /dev/null +++ b/packages/attach-pipeline/src/bindings.d.ts @@ -0,0 +1,85 @@ +import Toucan from 'toucan-js' +import { Logging } from '@web3-storage/worker-utils/loki' + +export {} + +export interface EnvInput { + ENV: string + DEBUG: string + SECRET: string + ATTACH_PIPELINE_SECRET: string + SENTRY_DSN?: string + LOKI_URL?: string + LOKI_TOKEN?: string + ATTACH_PIPELINE_QUEUE: Queue + CARPARK: R2Bucket +} + +export interface EnvTransformed { + VERSION: string + BRANCH: string + COMMITHASH: string + SENTRY_RELEASE: string + sentry?: Toucan + log: Logging +} + +export type Env = EnvInput & EnvTransformed + +declare global { + const BRANCH: string + const VERSION: string + const COMMITHASH: string + const SENTRY_RELEASE: string + const ENV: string +} + +// Temporary Queue types while not in https://github.com/cloudflare/workers-types/blob/master/index.d.ts +// These come from https://github.com/cloudflare/miniflare/blob/4c1bfdb8e4da7fa87ec69fcc28531b894b858693/packages/shared/src/queues.ts +export const kGetConsumer = Symbol("kGetConsumer"); +export const kSetConsumer = Symbol("kSetConsumer"); + +export type QueueEventDispatcher = (batch: MessageBatch) => Promise; + +export interface QueueBroker { + getOrCreateQueue(name: string): Queue; + + setConsumer(queue: Queue, consumer: Consumer): void; +} + +export interface Consumer { + queueName: string; + maxBatchSize: number; + maxWaitMs: number; + dispatcher: QueueEventDispatcher; +} + +// External types (exposed to user code): +export type MessageSendOptions = { + // Reserved +}; + +export type MessageSendRequest = { + body: Body; +} & MessageSendOptions; + +export interface Queue { + send(message: Body, options?: MessageSendOptions): Promise; + sendBatch(batch: Iterable>): Promise; + + [kSetConsumer](consumer: Consumer): void; + [kGetConsumer](): Consumer | null; +} + +export interface Message { + readonly id: string; + readonly timestamp: Date; + readonly body: Body; + retry(): void; +} + +export interface MessageBatch { + readonly queue: string; + readonly messages: Message[]; + retryAll(): void; +} diff --git a/packages/attach-pipeline/src/cors.js b/packages/attach-pipeline/src/cors.js new file mode 100644 index 0000000..1a22ec0 --- /dev/null +++ b/packages/attach-pipeline/src/cors.js @@ -0,0 +1,35 @@ +/* eslint-env serviceworker */ + +/** + * @param {import('itty-router').RouteHandler} handler + * @returns {import('itty-router').RouteHandler} + */ + export function withCorsHeaders (handler) { + /** + * @param {Request} request + * @returns {Promise} + */ + return async (request, ...rest) => { + const response = await handler(request, ...rest) + return addCorsHeaders(request, response) + } +} + +/** + * @param {Request} request + * @param {Response} response + * @returns {Response} + */ +export function addCorsHeaders (request, response) { + // Clone the response so that it's no longer immutable (like if it comes from cache or fetch) + response = new Response(response.body, response) + const origin = request.headers.get('origin') + if (origin) { + response.headers.set('Access-Control-Allow-Origin', origin) + response.headers.set('Vary', 'Origin') + } else { + response.headers.set('Access-Control-Allow-Origin', '*') + } + response.headers.set('Access-Control-Expose-Headers', 'Link') + return response +} diff --git a/packages/attach-pipeline/src/env.js b/packages/attach-pipeline/src/env.js new file mode 100644 index 0000000..9880b79 --- /dev/null +++ b/packages/attach-pipeline/src/env.js @@ -0,0 +1,70 @@ +/* global BRANCH, VERSION, COMMITHASH, SENTRY_RELEASE */ + +import Toucan from 'toucan-js' +import { Logging } from '@web3-storage/worker-utils/loki' + +import pkg from '../package.json' + +/** + * @typedef {import('./bindings').Env} Env + * @typedef {import('.').Ctx} Ctx + */ + +/** + * @param {Request} request + * @param {Env} env + * @param {import('.').Ctx} ctx + */ +export function envAll (request, env, ctx) { + // These values are replaced at build time by esbuild `define` + env.BRANCH = BRANCH + env.VERSION = VERSION + env.COMMITHASH = COMMITHASH + env.SENTRY_RELEASE = SENTRY_RELEASE + + env.sentry = getSentry(request, env, ctx) + + // @ts-ignore ctx type conflict + env.log = new Logging(request, ctx, { + // @ts-ignore TODO: url should be optional together with token + url: env.LOKI_URL, + token: env.LOKI_TOKEN, + debug: Boolean(env.DEBUG), + version: env.VERSION, + commit: env.COMMITHASH, + branch: env.BRANCH, + worker: 'attach-pipeline/gateway', + env: env.ENV, + sentry: env.sentry + }) + env.log.time('request') +} + +/** + * Get sentry instance if configured + * + * @param {Request} request + * @param {Env} env + * @param {Ctx} ctx + */ +function getSentry (request, env, ctx) { + if (!env.SENTRY_DSN) { + return + } + + return new Toucan({ + request, + dsn: env.SENTRY_DSN, + context: ctx, + allowedHeaders: ['user-agent'], + allowedSearchParams: /(.*)/, + debug: false, + environment: env.ENV || 'dev', + rewriteFrames: { + // sourcemaps only work if stack filepath are absolute like `/worker.js` + root: '/' + }, + release: env.SENTRY_RELEASE, + pkg + }) +} diff --git a/packages/attach-pipeline/src/error-handler.js b/packages/attach-pipeline/src/error-handler.js new file mode 100644 index 0000000..db5719c --- /dev/null +++ b/packages/attach-pipeline/src/error-handler.js @@ -0,0 +1,22 @@ +/* eslint-env serviceworker, browser */ + +/** + * @param {Error & {status?: number;code?: string; contentType?: string;}} err + * @param {import('./env').Env} env + */ + export function errorHandler (err, env) { + console.error(err.stack) + + const status = err.status || 500 + + if (env.sentry && status >= 500) { + env.log.error(err) + } + + return new Response(err.message || 'Server Error', { + status, + headers: { + 'content-type': err.contentType || 'text/plain' + } + }) +} diff --git a/packages/attach-pipeline/src/errors.js b/packages/attach-pipeline/src/errors.js new file mode 100644 index 0000000..ffffea9 --- /dev/null +++ b/packages/attach-pipeline/src/errors.js @@ -0,0 +1,66 @@ +export class HTTPError extends Error { + /** + * + * @param {string} message + * @param {number} [status] + */ + constructor (message, status = 500) { + super(message) + this.name = 'HTTPError' + this.status = status + } +} + +export class NoValidContentTypeError extends HTTPError { + constructor (msg = 'No valid content-type provided') { + super(msg, 400) + this.name = 'NoValidContentType' + this.code = NoValidContentTypeError.CODE + } +} +NoValidContentTypeError.CODE = 'ERROR_NO_VALID_CONTENT_TYPE' + +export class NoValidUrlError extends HTTPError { + constructor (msg = 'No valid URL to pull provided') { + super(msg, 400) + this.name = 'NoValidUrl' + this.code = NoValidUrlError.CODE + } +} +NoValidUrlError.CODE = 'ERROR_NO_VALID_URL' + +export class NoTokenError extends HTTPError { + constructor (msg = 'No token found in `Authorization: Basic ` header') { + super(msg, 401) + this.name = 'NoToken' + this.code = NoTokenError.CODE + } +} +NoTokenError.CODE = 'ERROR_NO_TOKEN' + +export class ExpectedBasicStringError extends HTTPError { + constructor (msg = 'Expected argument to be a string in the `Basic {token}` format') { + super(msg, 401) + this.name = 'ExpectedBasicString' + this.code = ExpectedBasicStringError.CODE + } +} +ExpectedBasicStringError.CODE = 'ERROR_NO_TOKEN' + +export class NoValidTokenError extends HTTPError { + constructor (msg = 'Provided token is not valid') { + super(msg, 401) + this.name = 'NoValidToken' + this.code = NoValidTokenError.CODE + } +} +NoValidTokenError.CODE = 'ERROR_NO_VALID_TOKEN' + +export class NoSuccessMd5WriteError extends HTTPError { + constructor (msg = 'No success writing CAR to R2 due to md5 mismatch') { + super(msg, 400) + this.name = 'NoSuccessResponse' + this.code = NoSuccessMd5WriteError.CODE + } +} +NoSuccessMd5WriteError.CODE = 'ERROR_NO_SUCCESS_MD5' \ No newline at end of file diff --git a/packages/attach-pipeline/src/handlers.js b/packages/attach-pipeline/src/handlers.js new file mode 100644 index 0000000..080840b --- /dev/null +++ b/packages/attach-pipeline/src/handlers.js @@ -0,0 +1,46 @@ +import { + NoValidUrlError, + NoValidContentTypeError +} from './errors.js' + +/** + * Handle queue Post. + * + * @param {Request} request + * @param {import('./env').Env} env + */ +export async function queuePost (request, env) { + // Validate content type and valid URLs + if (!request.headers.get('content-type')?.includes('application/json')) { + throw new NoValidContentTypeError() + } + + /** @type {Record} */ + const pullBody = await request.json() + const urls = Object.values(pullBody) + + if (!urls.length) { + throw new NoValidUrlError() + } + + // Validate URL is valid + try { + urls.map(u => new URL(u)) + } catch (err) { + throw new NoValidUrlError() + } + + // TODO: Do we get Queue ID to return? + await env.ATTACH_PIPELINE_QUEUE.sendBatch( + Object.entries(pullBody).map( + ([carCid, url]) => ({ + body: { + carCid, + url + } + }) + ) + ) + + return new Response() +} diff --git a/packages/attach-pipeline/src/index.js b/packages/attach-pipeline/src/index.js new file mode 100644 index 0000000..8c7027c --- /dev/null +++ b/packages/attach-pipeline/src/index.js @@ -0,0 +1,115 @@ +/* eslint-env serviceworker */ + +import { Router } from 'itty-router' + +import { queuePost } from './handlers.js' +import { versionGet } from './version.js' + +import { withAuthToken } from './auth.js' +import { addCorsHeaders, withCorsHeaders } from './cors.js' +import { errorHandler } from './error-handler.js' +import { envAll } from './env.js' +import { + NoSuccessMd5WriteError +} from './errors.js' + +/** + * https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent + * @typedef {{ waitUntil(p: Promise): void }} Ctx + * + * @typedef {{ carCid: string, url: string }} CarLocation + * @typedef {import('./bindings').Env} Env + * @typedef {import('./bindings').MessageBatch} MessageBatch + * */ + +const router = Router() + +const auth = { + '🤲': (/** @type {import("itty-router").RouteHandler} */ handler) => withCorsHeaders(handler), + '🔒': (/** @type {import("itty-router").RouteHandler} */ handler) => withCorsHeaders(withAuthToken(handler)) +} + +router + .all('*', envAll) + .get('/version', auth['🤲'](versionGet)) + .post('/', auth['🔒'](queuePost)) + +/** + * @param {Error} error + * @param {Request} request + * @param {Env} env + */ + function serverError (error, request, env) { + return addCorsHeaders(request, errorHandler(error, env)) +} + +export default { + /** + * + * @param {Request} request + * @param {import('./bindings').Env} env + * @param {Ctx} ctx + */ + async fetch (request, env, ctx) { + try { + const res = await router.handle(request, env, ctx) + env.log.timeEnd('request') + return env.log.end(res) + } catch (/** @type {any} */ error) { + if (env.log) { + env.log.timeEnd('request') + return env.log.end(serverError(error, request, env)) + } + return serverError(error, request, env) + } + }, + /** + * + * @param {MessageBatch} batch + * @param {import('./bindings').Env} env + */ + async queue(batch, env) { + /** @type {CarLocation[]} */ + const carsToFetch = batch.messages.map(m => m.body) + + await Promise.all( + carsToFetch.map(carLocation => runTask(carLocation, env.CARPARK)) + ) + } +} + +/** + * @param {CarLocation} carLocation + * @param {R2Bucket} carPark + */ +async function runTask (carLocation, carPark) { + // check R2 + const bucketKey = `${carLocation.carCid}/${carLocation.carCid}.car` + + // TODO: how to handle index file to write? + const existing = await carPark.head(bucketKey) + if (existing) { + return + } + + const response = await fetch(carLocation.url) + if (!response.ok) { + throw new Error(`CAR not found at ${carLocation.url}`) + } + + // Get md5 hash to use to check the received object’s integrity. + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html + const md5 = response.headers.get('ETag') || undefined + + try { + await carPark.put(bucketKey, response.body, { + httpMetadata: response.headers, + md5: md5 && md5.replaceAll('"', '') + }) + } catch (/** @type {any} */ err) { + if (err.message.includes('The Content-MD5 you specified did not match what we received.')) { + throw new NoSuccessMd5WriteError() + } + throw err + } +} diff --git a/packages/attach-pipeline/src/version.js b/packages/attach-pipeline/src/version.js new file mode 100644 index 0000000..deb9544 --- /dev/null +++ b/packages/attach-pipeline/src/version.js @@ -0,0 +1,15 @@ +import { JSONResponse } from '@web3-storage/worker-utils/response' + +/** + * Get edge gateway API version information. + * + * @param {Request} request + * @param {import('./env').Env} env + */ +export async function versionGet (request, env) { + return new JSONResponse({ + version: env.VERSION, + commit: env.COMMITHASH, + branch: env.BRANCH + }) +} diff --git a/packages/attach-pipeline/test/auth.spec.js b/packages/attach-pipeline/test/auth.spec.js new file mode 100644 index 0000000..1e7b3b5 --- /dev/null +++ b/packages/attach-pipeline/test/auth.spec.js @@ -0,0 +1,32 @@ +import { + test, + createTestToken, + getMiniflare +} from './utils/setup.js' + +test.beforeEach(async (t) => { + // Create a new Miniflare environment for each test + t.context = { + mf: getMiniflare() + } +}) + +test('Fails with 401 authentication when no token provided', async (t) => { + const { mf } = t.context + + const response = await mf.dispatchFetch('https://localhost:8787', { + method: 'POST' + }) + t.is(response.status, 401) +}) + +test('Fails with 401 authentication when invalid token provided', async (t) => { + const { mf } = t.context + const token = await createTestToken() + + const response = await mf.dispatchFetch('https://localhost:8787', { + method: 'POST', + headers: { Authorization: `${token}` } // Not Basic /token/ + }) + t.is(response.status, 401) +}) diff --git a/packages/attach-pipeline/test/index.spec.js b/packages/attach-pipeline/test/index.spec.js new file mode 100644 index 0000000..ebba22e --- /dev/null +++ b/packages/attach-pipeline/test/index.spec.js @@ -0,0 +1,80 @@ +import { createFetchMock } from '@miniflare/core' +import { Request } from 'miniflare' +import { test, getMiniflare, createTestToken } from './utils/setup.js' + +// Create a new Miniflare environment for each test +test.beforeEach(async (t) => { + const fetchMock = createFetchMock() + const token = await createTestToken() + const mf = getMiniflare({ fetchMock }) + + t.context = { + mf, + token, + // @ts-ignore TS different deps versions for undici + fetchMock + } +}) + +test('attach pipeline gateway errors if invalid content is provided', async (t) => { + const { mf, token } = t.context + if (!token) throw new Error() + + const response = await mf.dispatchFetch( + new Request('http://localhost:8787', { + method: 'POST', + headers: { + Authorization: `Basic ${token}`, + 'content-type': 'text/html; charset=utf-8' + } + }) + ) + t.is(response.status, 400) +}) + +test('gateway errors if invalid URL is provided', async (t) => { + const { mf, token } = t.context + if (!token) throw new Error() + + const cars = { + bafy0: 'https://cars.s3.amazonaws.com/bafy0/bafy0.car', + bafy1: 'invalid_url' + } + + const response = await mf.dispatchFetch( + new Request('http://localhost:8787', { + method: 'POST', + headers: { + Authorization: `Basic ${token}`, + 'content-type': 'text/html; charset=utf-8' + }, + body: JSON.stringify(cars) + }) + ) + t.is(response.status, 400) +}) + +test('Gateway triggers queue handler', async (t) => { + const { mf, token, fetchMock } = t.context + if (!token || !fetchMock) throw new Error() + + const cars = { + bafy0: 'https://cars.s3.amazonaws.com/bafy0/bafy0.car', + bafy1: 'https://cars.s3.amazonaws.com/bafy1/bafy1.car', + bafy2: 'https://cars.s3.amazonaws.com/bafy2/bafy2.car' + } + + const response = await mf.dispatchFetch( + new Request('http://localhost:8787', { + method: 'POST', + headers: { + Authorization: `Basic ${token}`, + 'content-type': 'application/json; charset=utf-8' + }, + body: JSON.stringify(cars) + }) + ) + t.is(response.status, 200) + + // TODO: Validate queue... +}) diff --git a/packages/attach-pipeline/test/utils/miniflare.js b/packages/attach-pipeline/test/utils/miniflare.js new file mode 100644 index 0000000..012126c --- /dev/null +++ b/packages/attach-pipeline/test/utils/miniflare.js @@ -0,0 +1,33 @@ +import fs from 'fs' +import path from 'path' +import { Miniflare } from 'miniflare' +import { globals } from './worker-globals.js' + +/** + * @param {any} param + */ +export function getMiniflare ({ fetchMock } = {}) { + let envPath = path.join(process.cwd(), '../../.env') + if (!fs.statSync(envPath, { throwIfNoEntry: false })) { + // @ts-ignore + envPath = true + } + + return new Miniflare({ + envPath, + scriptPath: 'dist/worker.js', + port: 8788, + packagePath: true, + wranglerConfigPath: true, + // We don't want to rebuild our worker for each test, we're already doing + // it once before we run all tests in package.json, so disable it here. + // This will override the option in wrangler.toml. + buildCommand: undefined, + wranglerConfigEnv: 'test', + modules: true, + fetchMock, + bindings: { + ...globals + } + }) +} diff --git a/packages/attach-pipeline/test/utils/setup.js b/packages/attach-pipeline/test/utils/setup.js new file mode 100644 index 0000000..497efd4 --- /dev/null +++ b/packages/attach-pipeline/test/utils/setup.js @@ -0,0 +1,23 @@ +import anyTest from 'ava' + +import { globals } from './worker-globals.js' +export * from './miniflare.js' + +/** + * @typedef {import('miniflare').Miniflare} Miniflare + * + * @typedef {Object} Context + * @property {Miniflare} mf + * @property {string} [token] + * @property {import('undici').MockAgent} [fetchMock] + * + * @typedef {import('ava').TestInterface} TestFn + */ + +export const test = /** @type {TestFn} */ (anyTest) + +export async function createTestToken () { + const token = globals.SECRET + + return token +} diff --git a/packages/attach-pipeline/test/utils/worker-globals.js b/packages/attach-pipeline/test/utils/worker-globals.js new file mode 100644 index 0000000..f893318 --- /dev/null +++ b/packages/attach-pipeline/test/utils/worker-globals.js @@ -0,0 +1,3 @@ +export const globals = { + SECRET: 'secret' +} diff --git a/packages/attach-pipeline/test/version.spec.js b/packages/attach-pipeline/test/version.spec.js new file mode 100644 index 0000000..9766243 --- /dev/null +++ b/packages/attach-pipeline/test/version.spec.js @@ -0,0 +1,29 @@ +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' +import git from 'git-rev-sync' + +import { test, getMiniflare } from './utils/setup.js' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const pkg = JSON.parse( + fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8') +) + +// Create a new Miniflare environment for each test +test.before((t) => { + t.context = { + mf: getMiniflare() + } +}) + +test('Gets Version', async (t) => { + const { mf } = t.context + + const response = await mf.dispatchFetch('http://localhost:8787/version') + const { version, commit, branch } = await response.json() + + t.is(version, pkg.version) + t.is(commit, git.long(__dirname)) + t.is(branch, git.branch(__dirname)) +}) diff --git a/packages/attach-pipeline/tsconfig.json b/packages/attach-pipeline/tsconfig.json new file mode 100644 index 0000000..d71a579 --- /dev/null +++ b/packages/attach-pipeline/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "noEmit": true, + "lib": ["ESNext"], + "types": ["@cloudflare/workers-types"] + }, + "include": [ + "src", + "test", + "package.json" + ], + "exclude": [ + "**/node_modules/**" + ] +} \ No newline at end of file diff --git a/packages/attach-pipeline/wrangler.toml b/packages/attach-pipeline/wrangler.toml new file mode 100644 index 0000000..e80c8e6 --- /dev/null +++ b/packages/attach-pipeline/wrangler.toml @@ -0,0 +1,74 @@ +# attach-pipeline wrangler config. +name = "attach-pipeline" +main = "./dist/worker.js" +compatibility_date = "2022-10-01" +compatibility_flags = [ "url_standard" ] +no_bundle = true + +[build] +command = "npm run build" + +# miniflare +[vars] +DEBUG = "true" +ENV = "dev" + +# PROD! +[env.production] +# name = "attach-pipeline-production" +account_id = "fffa4b4363a7e5250af8357087263b3a" # Protocol Labs CF account +route = { pattern = "https://attach-pipeline.web3.storage/*", zone_id = "7eee3323c1b35b6650568604c65f441e" } +vars = { ENV = "production" } + +[[env.production.r2_buckets]] +# what's it called in r2? +bucket_name = "car-park-prod-0" +# what's it called as a property of the env object +binding = "CARPARK" + +[[env.production.queues.producers]] + queue = "attach-queue-production" + binding = "ATTACH_PIPELINE_QUEUE" + +[[env.production.queues.consumers]] + queue = "attach-queue-production" + max_batch_size = 1 + max_batch_timeout = 100000 +## TODO: config + +# STAGING! +[env.staging] +# name = "attach-pipeline-staging" +account_id = "fffa4b4363a7e5250af8357087263b3a" # Protocol Labs CF account +route = { pattern = "https://attach-pipeline-staging.web3.storage/*", zone_id = "7eee3323c1b35b6650568604c65f441e" } +vars = { ENV = "staging" } + +[[env.staging.r2_buckets]] +# what's it called in r2? +bucket_name = "car-park-attach-staging-0" +# what's it called as a property of the env object +binding = "CARPARK" + +[[env.staging.queues.producers]] + queue = "attach-queue-staging" + binding = "ATTACH_PIPELINE_QUEUE" + +[[env.staging.queues.consumers]] + queue = "attach-queue-staging" + max_batch_size = 1 + max_batch_timeout = 100000 +## TODO: config + +# Test! +[env.test] +workers_dev = true + +[[env.test.queues.producers]] + queue = "attach-queue-test" + binding = "ATTACH_PIPELINE_QUEUE" + +[[env.test.queues.consumers]] + queue = "attach-queue-test" + max_batch_size = 1 + max_batch_timeout = 300 +## TODO: config diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8a841c9..fab17da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,45 @@ importers: simple-git-hooks: 2.8.0 wrangler: 2.0.23 + packages/attach-pipeline: + specifiers: + '@cloudflare/workers-types': ^3.16.0 + '@miniflare/core': ^2.10.0 + '@sentry/cli': ^1.71.0 + '@types/git-rev-sync': ^2.0.0 + '@web-std/fetch': ^4.0.0 + '@web3-storage/worker-utils': ^0.4.3-dev + ava: ^3.15.0 + esbuild: ^0.15.10 + git-rev-sync: ^3.0.1 + itty-router: ^2.6.6 + miniflare: ^2.10.0 + sade: ^1.8.1 + standard: ^17.0.0 + toml: ^3.0.0 + toucan-js: ^2.7.0 + typescript: 4.8.4 + undici: ^5.9.1 + dependencies: + '@web3-storage/worker-utils': 0.4.3-dev + itty-router: 2.6.6 + toucan-js: 2.7.0 + devDependencies: + '@cloudflare/workers-types': 3.16.0 + '@miniflare/core': 2.10.0 + '@sentry/cli': 1.74.4 + '@types/git-rev-sync': 2.0.0 + '@web-std/fetch': 4.1.0 + ava: 3.15.0 + esbuild: 0.15.10 + git-rev-sync: 3.0.2 + miniflare: 2.10.0 + sade: 1.8.1 + standard: 17.0.0 + toml: 3.0.0 + typescript: 4.8.4 + undici: 5.11.0 + packages/cid-verifier: specifiers: '@cloudflare/workers-types': ^3.7.1 @@ -41,7 +80,7 @@ importers: uint8arrays: ^3.0.0 dependencies: '@web3-storage/worker-utils': 0.3.0-dev - ipfs-core-utils: 0.15.1_undici@5.5.1 + ipfs-core-utils: 0.15.1_undici@5.11.0 itty-router: 2.6.1 multiformats: 9.7.0 p-retry: 5.1.1 @@ -101,7 +140,7 @@ importers: uint8arrays: ^3.0.0 dependencies: '@web3-storage/worker-utils': 0.3.0-dev - ipfs-core-utils: 0.15.1_undici@5.5.1 + ipfs-core-utils: 0.15.1_undici@5.11.0 ipfs-gateway-race: link:../ipfs-gateway-race itty-router: 2.6.1 multiformats: 9.7.0 @@ -230,6 +269,10 @@ packages: resolution: {integrity: sha512-F6lZuzu/vNl9AxOq1U3x2UCi68bcE1O59cKGQ5ntRh3lGQfd3vers6NUag02T7P+qFCHaAp78QN6fRmq12Y3iA==} dev: true + /@cloudflare/workers-types/3.16.0: + resolution: {integrity: sha512-gaBUSaKS65mN3iKZEgichbXYEmAa/pXkc5Gbt+1BptYphdGkj09ggdsiE4w8g0F/uI1g36QaTKrzVnBAWMipvQ==} + dev: true + /@concordance/react/2.0.0: resolution: {integrity: sha512-huLSkUuM2/P+U0uy2WwlKuixMsTODD8p4JVQBI4VKeopkiN0C7M3N9XYVawb4M+4spN5RrO/eLhk7KoQX6nsfA==} engines: {node: '>=6.12.3 <7 || >=8.9.4 <9 || >=10.0.0'} @@ -255,6 +298,24 @@ packages: rollup-plugin-node-polyfills: 0.2.1 dev: true + /@esbuild/android-arm/0.15.10: + resolution: {integrity: sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.15.10: + resolution: {integrity: sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@eslint/eslintrc/1.3.0: resolution: {integrity: sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -310,11 +371,11 @@ packages: dependencies: multiformats: 9.7.0 - /@libp2p/interfaces/2.0.4_undici@5.5.1: + /@libp2p/interfaces/2.0.4_undici@5.11.0: resolution: {integrity: sha512-MfwkTFyHJtvwNxkjOjzkXyIVvKFtEW2Q3IGRJPyPQMrtB6ll0rGMTlyJ3BQS1bcD0YkNhggFm+8XiU2/0LCBhQ==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - '@multiformats/multiaddr': 10.3.3_undici@5.5.1 + '@multiformats/multiaddr': 10.3.3_undici@5.11.0 err-code: 3.0.1 interface-datastore: 6.1.1 it-pushable: 2.0.2 @@ -325,11 +386,11 @@ packages: - undici dev: false - /@libp2p/logger/1.1.6_undici@5.5.1: + /@libp2p/logger/1.1.6_undici@5.11.0: resolution: {integrity: sha512-ZKoRUt7cyHlbxHYDZ1Fn3A+ByqGABdmd4z07+1TfVvpEQSpn2IVcV0mt6ff5kUUtGuVeSrqK1/ZDzWqhgg56vg==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - '@libp2p/interfaces': 2.0.4_undici@5.5.1 + '@libp2p/interfaces': 2.0.4_undici@5.11.0 debug: 4.3.4 interface-datastore: 6.1.1 multiformats: 9.7.0 @@ -338,6 +399,16 @@ packages: - undici dev: false + /@miniflare/cache/2.10.0: + resolution: {integrity: sha512-nzEqFVPnD7Yf0HMDv7gCPpf4NSXfjhc+zg3gSwUS4Dad5bWV10B1ujTZW6HxQulW3CBHIg616mTjXIiaimVuEQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.10.0 + '@miniflare/shared': 2.10.0 + http-cache-semantics: 4.1.0 + undici: 5.9.1 + dev: true + /@miniflare/cache/2.5.1: resolution: {integrity: sha512-qH5PC4zb7mHdQHlcaOuP0KUXuRbNSuB/HU7gpoeplV8J6CgNJGceVmQCZVZLycgDKZtAlhyGE1gkpJmeW7GCyw==} engines: {node: '>=16.7'} @@ -358,6 +429,24 @@ packages: undici: 5.5.1 dev: true + /@miniflare/cache/2.9.0: + resolution: {integrity: sha512-lriPxUEva9TJ01vU9P7pI60s3SsFnb4apWkNwZ+D7CRqyXPipSbapY8BWI2FUIwkEG7xap6UhzeTS76NettCXQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.9.0 + '@miniflare/shared': 2.9.0 + http-cache-semantics: 4.1.0 + undici: 5.9.1 + dev: true + + /@miniflare/cli-parser/2.10.0: + resolution: {integrity: sha512-NAiCtqlHTUKCmV+Jl9af+ixGmMhiGhIyIfr/vCdbismNEBxEsrQGg3sQYTNfvCkdHtODurQqayQreFq21OuEow==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.10.0 + kleur: 4.1.5 + dev: true + /@miniflare/cli-parser/2.5.1: resolution: {integrity: sha512-itlMDe9jwO806mkNkg3G70QYoG9YQHW6V10AF9L5b8J4LYt/V78uCEJSwNnCpL7zfKrScRPtDfXZxhrFzMXiUw==} engines: {node: '>=16.7'} @@ -374,6 +463,30 @@ packages: kleur: 4.1.5 dev: true + /@miniflare/cli-parser/2.9.0: + resolution: {integrity: sha512-gu8Z7NWNcYw6514/yOvajaj3GmebRucx+EEt3p1vKirO+gvFgKAt/puyUN3p7u8ZZmLuLF/B+wVnH3lj8BWKlg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.9.0 + kleur: 4.1.5 + dev: true + + /@miniflare/core/2.10.0: + resolution: {integrity: sha512-Jx1M5oXQua0jzsJVdZSq07baVRmGC/6JkglrPQGAlZ7gQ1sunVZzq9fjxFqj0bqfEuYS0Wy6+lvK4rOAHISIjw==} + engines: {node: '>=16.13'} + dependencies: + '@iarna/toml': 2.2.5 + '@miniflare/queues': 2.10.0 + '@miniflare/shared': 2.10.0 + '@miniflare/watcher': 2.10.0 + busboy: 1.6.0 + dotenv: 10.0.0 + kleur: 4.1.5 + set-cookie-parser: 2.5.0 + undici: 5.9.1 + urlpattern-polyfill: 4.0.3 + dev: true + /@miniflare/core/2.5.1: resolution: {integrity: sha512-0oEBLV5AM3xxs6TS+7/fn4MSGNBfhUFVv41R8uc72H1a89+kBfRoz+xYI2RnJ3Yo+we66UgU3fXdG+R2KyESlQ==} engines: {node: '>=16.7'} @@ -404,6 +517,48 @@ packages: urlpattern-polyfill: 4.0.3 dev: true + /@miniflare/core/2.9.0: + resolution: {integrity: sha512-QqSwF6oHvgrFvN5lnrLc6EEagFlZWW+UMU8QdrE8305cNGHrIOxKCA2nte4PVFZUVw/Ts13a0tVhUk3a2fAyxQ==} + engines: {node: '>=16.13'} + dependencies: + '@iarna/toml': 2.2.5 + '@miniflare/queues': 2.9.0 + '@miniflare/shared': 2.9.0 + '@miniflare/watcher': 2.9.0 + busboy: 1.6.0 + dotenv: 10.0.0 + kleur: 4.1.5 + set-cookie-parser: 2.5.0 + undici: 5.9.1 + urlpattern-polyfill: 4.0.3 + dev: true + + /@miniflare/d1/2.10.0: + resolution: {integrity: sha512-mOYZSmpTthH0tmFTQ+O9G0Q+iDAd7oiUtoIBianlKa9QiqYAoO7EBUPy6kUgDHXapOcN5Ri1u3J5UTpxXvw3qg==} + engines: {node: '>=16.7'} + dependencies: + '@miniflare/core': 2.10.0 + '@miniflare/shared': 2.10.0 + dev: true + + /@miniflare/d1/2.9.0: + resolution: {integrity: sha512-swK9nzxw1SvVh/4cH3bRR1SBuHQU/YsB8WvuHojxufmgviAD1xhms3XO3rkpAzfKoGM5Oy6DovMe0xUXV/GS0w==} + engines: {node: '>=16.7'} + dependencies: + '@miniflare/core': 2.9.0 + '@miniflare/shared': 2.9.0 + dev: true + + /@miniflare/durable-objects/2.10.0: + resolution: {integrity: sha512-gU45f52gveFtCasm0ixYnt0mHI1lHrPomtmF+89oZGKBzOqUfO5diDs6wmoRSnovOWZCwtmwQGRoorAQN7AmoA==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.10.0 + '@miniflare/shared': 2.10.0 + '@miniflare/storage-memory': 2.10.0 + undici: 5.9.1 + dev: true + /@miniflare/durable-objects/2.5.1: resolution: {integrity: sha512-AZEGSA9LMA6vBzwADAzr81RBSWYlMfa/cDHnHaFL31w4mQwMUcqXOvemoqe6sTSq1KI0TTtvYbxPt0Lui8tEPw==} engines: {node: '>=16.7'} @@ -424,6 +579,26 @@ packages: undici: 5.5.1 dev: true + /@miniflare/durable-objects/2.9.0: + resolution: {integrity: sha512-7uTvfEUXS7xqwrsWOwWrFUuKc4EiMpVkAWPeYGLB/0TJaJ6N+sZMpYYymdW79TQwPIDfgtpfkIy93MRydqpnrw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.9.0 + '@miniflare/shared': 2.9.0 + '@miniflare/storage-memory': 2.9.0 + undici: 5.9.1 + dev: true + + /@miniflare/html-rewriter/2.10.0: + resolution: {integrity: sha512-hCdG99L8+Ros4dn3B5H37PlQPBH0859EoRslzNTd4jzGIkwdiawpJvrvesL8056GjbUjeJN1zh7OPBRuMgyGLw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.10.0 + '@miniflare/shared': 2.10.0 + html-rewriter-wasm: 0.4.1 + undici: 5.9.1 + dev: true + /@miniflare/html-rewriter/2.5.1: resolution: {integrity: sha512-fdO1qme8ukucejRz5yXJN/F4B9qEDRbBLPOEG94zwx8bHGGIo5VX15+J6oHubhjifLwzNuvOcg16Bu5dyR1KxQ==} engines: {node: '>=16.7'} @@ -444,6 +619,33 @@ packages: undici: 5.5.1 dev: true + /@miniflare/html-rewriter/2.9.0: + resolution: {integrity: sha512-K5OB70PtkMo7M+tU46s/cX/j/qtjD9AlJ0hecYswrxVsfrT/YWyrCQJevmShFfJ92h7jPNigbeC3Od3JiVb6QA==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.9.0 + '@miniflare/shared': 2.9.0 + html-rewriter-wasm: 0.4.1 + undici: 5.9.1 + dev: true + + /@miniflare/http-server/2.10.0: + resolution: {integrity: sha512-cm6hwkONucll93yoY8dteMp//Knvmb7n6zAgeHrtuNYKn//lAL6bRY//VLTttrMmfWxZFi1C7WpOeCv8Mn6/ug==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.10.0 + '@miniflare/shared': 2.10.0 + '@miniflare/web-sockets': 2.10.0 + kleur: 4.1.5 + selfsigned: 2.0.1 + undici: 5.9.1 + ws: 8.8.0 + youch: 2.2.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@miniflare/http-server/2.5.1: resolution: {integrity: sha512-K+VoBU0LN8/oku/JWLEyX8wrp9fiaTC8/dosbY/6VWizyIrgQze16uD21GnK5+NBtbCAtLRryS5dZ3PnhiTR1w==} engines: {node: '>=16.7'} @@ -478,6 +680,30 @@ packages: - utf-8-validate dev: true + /@miniflare/http-server/2.9.0: + resolution: {integrity: sha512-IVJMkFfMpecq9WiCTvATEKhMuKPK9fMs2E6zmgexaefr3u1VlNtj2QxBxoPUXkT9xMJQlT5sSKstlRR1XKDz9Q==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.9.0 + '@miniflare/shared': 2.9.0 + '@miniflare/web-sockets': 2.9.0 + kleur: 4.1.5 + selfsigned: 2.0.1 + undici: 5.9.1 + ws: 8.8.0 + youch: 2.2.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@miniflare/kv/2.10.0: + resolution: {integrity: sha512-3+u1lO77FnlS0lQ6b1VgM1E/ZgQ/zy/FU+SdBG5LUOIiv3x522VYHOApeJLnSEo0KtZUB22Ni0fWQM6DgpaREg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.10.0 + dev: true + /@miniflare/kv/2.5.1: resolution: {integrity: sha512-ODTUqI7on3egHluBpFHifO0a9QFQUZscciASWKxGOt8VDp1vp0vIfU9ykQZrdZYFVeSKNVlUNqNQx+NMYZ6gIg==} engines: {node: '>=16.7'} @@ -492,6 +718,35 @@ packages: '@miniflare/shared': 2.6.0 dev: true + /@miniflare/kv/2.9.0: + resolution: {integrity: sha512-EqG51okY5rDtgjYs2Ny6j6IUVdTlJzDjwBKBIuW+wOV9NsAAzEchKVdYAXc8CyxvkggpYX481HydTD2OzK3INQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.9.0 + dev: true + + /@miniflare/queues/2.10.0: + resolution: {integrity: sha512-WKdO6qI9rfS96KlCjazzPFf+qj6DPov4vONyf18+jzbRjRJh/xwWSk1/1h5A+gDPwVNG8TsNRPh9DW5OKBGNjw==} + engines: {node: '>=16.7'} + dependencies: + '@miniflare/shared': 2.10.0 + dev: true + + /@miniflare/queues/2.9.0: + resolution: {integrity: sha512-cAHWIlLF57rxQaJl19AzXw1k0SOM/uLTlx8r2PylHajZ/RRSs7CkCox3oKA6E5zKyfyxk2M64bmsAFZ9RCA0gw==} + engines: {node: '>=16.7'} + dependencies: + '@miniflare/shared': 2.9.0 + dev: true + + /@miniflare/r2/2.10.0: + resolution: {integrity: sha512-uC1CCWbwM1t8DdpZgrveg6+CkZLfTq+wUMqs20BC5rCT8u8UyRv6ZVRQ7pTPiswLyt1oYDTXsZJK7tjV0U0zew==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.10.0 + undici: 5.9.1 + dev: true + /@miniflare/r2/2.6.0: resolution: {integrity: sha512-Ymbqu17ajtuk9b11txF2h1Ewqqlu3XCCpAwAgCQa6AK1yRidQECCPq9w9oXZxE1p5aaSuLTOUbgSdtveFCsLxQ==} engines: {node: '>=16.7'} @@ -500,6 +755,21 @@ packages: undici: 5.5.1 dev: true + /@miniflare/r2/2.9.0: + resolution: {integrity: sha512-aMFWxxciAE3YsVok2OLy3A7hP5+2j/NaK7txmadgoe1CA8HYZyNuvv7v6bn8HKM5gWnJdT8sk4yEbMbBQ7Jv/A==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.9.0 + undici: 5.9.1 + dev: true + + /@miniflare/runner-vm/2.10.0: + resolution: {integrity: sha512-oTsHitQdQ1B1kT3G/6n9AEXsMd/sT1D8tLGzc7Xr79ZrxYxwRO0ATF3cdkxk4dUjUqg/RUqvOJV4YjJGyqvctg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.10.0 + dev: true + /@miniflare/runner-vm/2.5.1: resolution: {integrity: sha512-7U7BPgzaikwWkAMonlmyy4lDpW1H7mqHFr7NdK9kA6BbXZ2GY6uro69QsGw0c4Y/vyKBodKiqXAq53iGdM3Kug==} engines: {node: '>=16.7'} @@ -514,6 +784,22 @@ packages: '@miniflare/shared': 2.6.0 dev: true + /@miniflare/runner-vm/2.9.0: + resolution: {integrity: sha512-vewP+Fy7Czb261GmB9x/YtQkoDs/QP9B5LbP0YfJ35bI2C2j940eJLm8JP72IHV7ILtWNOqMc3Ure8uAbpf9NQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.9.0 + dev: true + + /@miniflare/scheduler/2.10.0: + resolution: {integrity: sha512-eGt2cZFE/yo585nT8xINQwdbTotZfeRIh6FUWmZkbva1i5SW0zTiOojr5a95vAGBF3TzwWGsUuzJpLhBB69a/g==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.10.0 + '@miniflare/shared': 2.10.0 + cron-schedule: 3.0.6 + dev: true + /@miniflare/scheduler/2.5.1: resolution: {integrity: sha512-ybho5Kg3Cfl4E0JleKAbiv/RTA+/PVqH6Y/PuCH2oowSM7qeAvFkrwiRvxtN7BuAl+5lsGyVxFe4gL+weXohEw==} engines: {node: '>=16.7'} @@ -532,6 +818,25 @@ packages: cron-schedule: 3.0.6 dev: true + /@miniflare/scheduler/2.9.0: + resolution: {integrity: sha512-eodSCGkJYi4Z+Imbx/bNScDfDSt5HOypVSYjbFHj+hA2aNOdkGw6a1b6mzwx49jJD3GadIkonZAKD0S114yWMA==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.9.0 + '@miniflare/shared': 2.9.0 + cron-schedule: 3.0.6 + dev: true + + /@miniflare/shared/2.10.0: + resolution: {integrity: sha512-GDSweEhJ3nNtStGm6taZGUNytM0QTQ/sjZSedAKyF1/aHRaZUcD9cuKAMgIbSpKfvgGdLMNS7Bhd8jb249TO7g==} + engines: {node: '>=16.13'} + dependencies: + '@types/better-sqlite3': 7.6.0 + kleur: 4.1.5 + npx-import: 1.1.3 + picomatch: 2.3.1 + dev: true + /@miniflare/shared/2.5.1: resolution: {integrity: sha512-DObgqbFml3qetIBtZa8fNqkBqUH9XtI6rdrWtTYVrx0rzKsd5PDf6gdMoxy7v1rr9zBAipKJxrcBqlEgjPl53Q==} engines: {node: '>=16.7'} @@ -548,6 +853,25 @@ packages: kleur: 4.1.5 dev: true + /@miniflare/shared/2.9.0: + resolution: {integrity: sha512-5Ew/Ph0cHDQqKvOlmN70kz+qZW0hdgE9fQBStKLY3vDYhnBEhopbCUChSS+FCcL7WtxVJJVE7iB6J09NQTnQ/A==} + engines: {node: '>=16.13'} + dependencies: + '@types/better-sqlite3': 7.6.0 + kleur: 4.1.5 + npx-import: 1.1.3 + picomatch: 2.3.1 + dev: true + + /@miniflare/sites/2.10.0: + resolution: {integrity: sha512-1NVAT6+JS2OubL+pOOR5E/6MMddxQHWMi/yIDSumyyfXmj7Sm7n5dE1FvNPetggMP4f8+AjoyT9AYvdd1wkspQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/kv': 2.10.0 + '@miniflare/shared': 2.10.0 + '@miniflare/storage-file': 2.10.0 + dev: true + /@miniflare/sites/2.5.1: resolution: {integrity: sha512-7V/fAzR50LYgMcOfoCaoppqBCjagBpGWFbZgMyJi/Hj4oVlSIzxo+424hzdjitNzikCpv+AryF9tXfy9j6qiOg==} engines: {node: '>=16.7'} @@ -566,6 +890,23 @@ packages: '@miniflare/storage-file': 2.6.0 dev: true + /@miniflare/sites/2.9.0: + resolution: {integrity: sha512-+tWf7znxSQqXWGzPup8Xqkl8EmLmx+HaLC+UBtWPNnaJZrsjbbVxKwHpmGIdm+wZasEGfQk/82R21gUs9wdZnw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/kv': 2.9.0 + '@miniflare/shared': 2.9.0 + '@miniflare/storage-file': 2.9.0 + dev: true + + /@miniflare/storage-file/2.10.0: + resolution: {integrity: sha512-K/cRIWiTl4+Z+VO6tl4VfuYXA3NLJgvGPV+BCRYD7uTKuPYHqDMErtD1BI1I7nc3WJhwIXfzJrAR3XXhSKKWQQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.10.0 + '@miniflare/storage-memory': 2.10.0 + dev: true + /@miniflare/storage-file/2.5.1: resolution: {integrity: sha512-o12KFXgc1M0nHD98mrA/IqwBsJ6KYLWH9NaTwqLhxhpGz/KSo5kWb7z/vrz2I/Rk2XR/gHSYQm2XR9XE6IJCdA==} engines: {node: '>=16.7'} @@ -582,6 +923,21 @@ packages: '@miniflare/storage-memory': 2.6.0 dev: true + /@miniflare/storage-file/2.9.0: + resolution: {integrity: sha512-HZHtHfJaLoDzQFddoIMcDGgAJ3/Nee98gwUYusQam7rj9pbEXnWmk54dzjzsDlkQpB/3MBFQNbtN5Bj1NIt0pg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.9.0 + '@miniflare/storage-memory': 2.9.0 + dev: true + + /@miniflare/storage-memory/2.10.0: + resolution: {integrity: sha512-ZATU+qZtJ9yG0umgTrOEUi9SU//YyDb8nYXMgqT4JHODYA3RTz1SyyiQSOOz589upJPdu1LN+0j8W24WGRwwxQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.10.0 + dev: true + /@miniflare/storage-memory/2.5.1: resolution: {integrity: sha512-LIdBEFcwY7yLCeowO34p5bajRsvU1XuQjXIqcgfiCVt1+qa3D0seELTpW1NSFEJzxulVtu/KsScEug9GipEt7A==} engines: {node: '>=16.7'} @@ -596,6 +952,20 @@ packages: '@miniflare/shared': 2.6.0 dev: true + /@miniflare/storage-memory/2.9.0: + resolution: {integrity: sha512-p2yrr0omQhv6teDbdzhdBKzoQAFmUBMLEx+PtrO7CJHX15ICD08/pFAFAp96IcljNwZZDchU20Z3AcbldMj6Tw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.9.0 + dev: true + + /@miniflare/watcher/2.10.0: + resolution: {integrity: sha512-X9CFYYyszfSYDzs07KhbWC2i08Dpyh3D60fPonYZcoZAfa5h9eATHUdRGvNCdax7awYp4b8bvU8upAI//OPlMg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.10.0 + dev: true + /@miniflare/watcher/2.5.1: resolution: {integrity: sha512-8oOdgWA7CZ7uIAwbjqSrhDnuQXRJqd9e3yDHsMa91E/jkC/GDmlt5SJh6VEMlNDtBGWd661IpVErZf7injI52w==} engines: {node: '>=16.7'} @@ -610,6 +980,26 @@ packages: '@miniflare/shared': 2.6.0 dev: true + /@miniflare/watcher/2.9.0: + resolution: {integrity: sha512-Yqz8Q1He/2chebXvmCft8sMamuUiDQ4FIn0bwiF0+GBP2vvGCmy6SejXZY4ZD4REluPqQSis3CLKcIOWlHnIsw==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/shared': 2.9.0 + dev: true + + /@miniflare/web-sockets/2.10.0: + resolution: {integrity: sha512-W+PrapdQqNEEFeD+amENgPQWcETGDp7OEh6JAoSzCRhHA0OoMe8DG0xb5a5+2FjGW/J7FFKsv84wkURpmFT4dQ==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.10.0 + '@miniflare/shared': 2.10.0 + undici: 5.9.1 + ws: 8.8.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@miniflare/web-sockets/2.5.1: resolution: {integrity: sha512-GyXHoDAI5LDF87rmD+d0cSoN7Xs2pCYjSyUR6Vf6exkS4CN6F/Rtr8vIM5+om9kRkS6qxAyOYjWeEqBOjLm/og==} engines: {node: '>=16.7'} @@ -636,24 +1026,37 @@ packages: - utf-8-validate dev: true + /@miniflare/web-sockets/2.9.0: + resolution: {integrity: sha512-Nob9e84m78qeQCka6OQf/JdNOmMkKCkX+i3rg+TYKSSITiMVuyzWp3vz3Ma184lAZiLg44lxBF4ZzENEdi99Kg==} + engines: {node: '>=16.13'} + dependencies: + '@miniflare/core': 2.9.0 + '@miniflare/shared': 2.9.0 + undici: 5.9.1 + ws: 8.8.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@multiformats/base-x/4.0.1: resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==} dev: true - /@multiformats/multiaddr-to-uri/9.0.1_undici@5.5.1: + /@multiformats/multiaddr-to-uri/9.0.1_undici@5.11.0: resolution: {integrity: sha512-kSyHZ2lKjoEzHu/TM4ZVwFj4AWV1B9qFBFJjYb/fK1NqrnrNb/M3uhoyckJvP7WZvpDsnEc7fUCpmPipDY6LMw==} dependencies: - '@multiformats/multiaddr': 10.3.3_undici@5.5.1 + '@multiformats/multiaddr': 10.3.3_undici@5.11.0 transitivePeerDependencies: - supports-color - undici dev: false - /@multiformats/multiaddr/10.3.3_undici@5.5.1: + /@multiformats/multiaddr/10.3.3_undici@5.11.0: resolution: {integrity: sha512-+LX9RovG7DJsANb+U6VchV/tApcdJzeafbi5+MPUam90oL91BbEh6ozNZOz4Qf5ZEeilexc12oomatmODJh1/w==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - dns-over-http-resolver: 2.1.0_undici@5.5.1 + dns-over-http-resolver: 2.1.0_undici@5.11.0 err-code: 3.0.1 is-ip: 4.0.0 multiformats: 9.7.0 @@ -801,6 +1204,12 @@ packages: '@types/glob': 7.2.0 dev: true + /@types/better-sqlite3/7.6.0: + resolution: {integrity: sha512-rnSP9vY+fVsF3iJja5yRGBJV63PNBiezJlYrCkqUmQWFoB16cxAHwOkjsAYEu317miOfKaJpa65cbp0P4XJ/jw==} + dependencies: + '@types/node': 18.0.1 + dev: true + /@types/cookie/0.5.0: resolution: {integrity: sha512-CJWHVHHupxBYfIlMM+qzXx4dRKIV1VzOm0cP3Wpqten8MDx1tK+y92YDXUshN1ONAfwodvKxDNkw35/pNs+izg==} dev: false @@ -930,7 +1339,15 @@ packages: dependencies: '@web-std/fetch': 4.1.0 nanoid: 4.0.0 - toucan-js: 2.6.1 + toucan-js: 2.7.0 + dev: false + + /@web3-storage/worker-utils/0.4.3-dev: + resolution: {integrity: sha512-PJ30KkwtyNoTkVEQjrNhNYQw3JdDRd23GnVfega114m2TIge33qBfD2/X2+Pn1uC4WKpOHKnCmR6tXHmRLgYig==} + dependencies: + '@web-std/fetch': 4.1.0 + nanoid: 4.0.0 + toucan-js: 2.7.0 dev: false /@zxing/text-encoding/0.9.0: @@ -1498,7 +1915,6 @@ packages: engines: {node: '>=10.16.0'} dependencies: streamsearch: 1.1.0 - dev: true /byline/5.0.0: resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} @@ -2116,12 +2532,12 @@ packages: - supports-color dev: true - /dns-over-http-resolver/2.1.0_undici@5.5.1: + /dns-over-http-resolver/2.1.0_undici@5.11.0: resolution: {integrity: sha512-eb8RGy6k54JdD7Rjw8g65y1MyA4z3m3IIYh7uazkgZuKIdFn8gYt8dydMm3op+2UshDdk9EexrXcDluKNY/CDg==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: debug: 4.3.4 - native-fetch: 4.0.2_undici@5.5.1 + native-fetch: 4.0.2_undici@5.11.0 receptacle: 1.3.2 transitivePeerDependencies: - supports-color @@ -2335,6 +2751,15 @@ packages: dev: true optional: true + /esbuild-android-64/0.15.10: + resolution: {integrity: sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /esbuild-android-arm64/0.14.47: resolution: {integrity: sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==} engines: {node: '>=12'} @@ -2353,6 +2778,15 @@ packages: dev: true optional: true + /esbuild-android-arm64/0.15.10: + resolution: {integrity: sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /esbuild-darwin-64/0.14.47: resolution: {integrity: sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==} engines: {node: '>=12'} @@ -2371,6 +2805,15 @@ packages: dev: true optional: true + /esbuild-darwin-64/0.15.10: + resolution: {integrity: sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /esbuild-darwin-arm64/0.14.47: resolution: {integrity: sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==} engines: {node: '>=12'} @@ -2389,6 +2832,15 @@ packages: dev: true optional: true + /esbuild-darwin-arm64/0.15.10: + resolution: {integrity: sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /esbuild-freebsd-64/0.14.47: resolution: {integrity: sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==} engines: {node: '>=12'} @@ -2407,6 +2859,15 @@ packages: dev: true optional: true + /esbuild-freebsd-64/0.15.10: + resolution: {integrity: sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /esbuild-freebsd-arm64/0.14.47: resolution: {integrity: sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==} engines: {node: '>=12'} @@ -2425,6 +2886,15 @@ packages: dev: true optional: true + /esbuild-freebsd-arm64/0.15.10: + resolution: {integrity: sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-32/0.14.47: resolution: {integrity: sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==} engines: {node: '>=12'} @@ -2443,6 +2913,15 @@ packages: dev: true optional: true + /esbuild-linux-32/0.15.10: + resolution: {integrity: sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-64/0.14.47: resolution: {integrity: sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==} engines: {node: '>=12'} @@ -2461,6 +2940,15 @@ packages: dev: true optional: true + /esbuild-linux-64/0.15.10: + resolution: {integrity: sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-arm/0.14.47: resolution: {integrity: sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==} engines: {node: '>=12'} @@ -2479,6 +2967,15 @@ packages: dev: true optional: true + /esbuild-linux-arm/0.15.10: + resolution: {integrity: sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-arm64/0.14.47: resolution: {integrity: sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==} engines: {node: '>=12'} @@ -2497,6 +2994,15 @@ packages: dev: true optional: true + /esbuild-linux-arm64/0.15.10: + resolution: {integrity: sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-mips64le/0.14.47: resolution: {integrity: sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==} engines: {node: '>=12'} @@ -2515,6 +3021,15 @@ packages: dev: true optional: true + /esbuild-linux-mips64le/0.15.10: + resolution: {integrity: sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-ppc64le/0.14.47: resolution: {integrity: sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==} engines: {node: '>=12'} @@ -2533,6 +3048,15 @@ packages: dev: true optional: true + /esbuild-linux-ppc64le/0.15.10: + resolution: {integrity: sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-riscv64/0.14.47: resolution: {integrity: sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==} engines: {node: '>=12'} @@ -2551,6 +3075,15 @@ packages: dev: true optional: true + /esbuild-linux-riscv64/0.15.10: + resolution: {integrity: sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-s390x/0.14.47: resolution: {integrity: sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==} engines: {node: '>=12'} @@ -2569,6 +3102,15 @@ packages: dev: true optional: true + /esbuild-linux-s390x/0.15.10: + resolution: {integrity: sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-netbsd-64/0.14.47: resolution: {integrity: sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==} engines: {node: '>=12'} @@ -2587,6 +3129,15 @@ packages: dev: true optional: true + /esbuild-netbsd-64/0.15.10: + resolution: {integrity: sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /esbuild-openbsd-64/0.14.47: resolution: {integrity: sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==} engines: {node: '>=12'} @@ -2605,6 +3156,15 @@ packages: dev: true optional: true + /esbuild-openbsd-64/0.15.10: + resolution: {integrity: sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /esbuild-sunos-64/0.14.47: resolution: {integrity: sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==} engines: {node: '>=12'} @@ -2623,6 +3183,15 @@ packages: dev: true optional: true + /esbuild-sunos-64/0.15.10: + resolution: {integrity: sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /esbuild-windows-32/0.14.47: resolution: {integrity: sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==} engines: {node: '>=12'} @@ -2641,6 +3210,15 @@ packages: dev: true optional: true + /esbuild-windows-32/0.15.10: + resolution: {integrity: sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild-windows-64/0.14.47: resolution: {integrity: sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==} engines: {node: '>=12'} @@ -2659,6 +3237,15 @@ packages: dev: true optional: true + /esbuild-windows-64/0.15.10: + resolution: {integrity: sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild-windows-arm64/0.14.47: resolution: {integrity: sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==} engines: {node: '>=12'} @@ -2677,6 +3264,15 @@ packages: dev: true optional: true + /esbuild-windows-arm64/0.15.10: + resolution: {integrity: sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild/0.14.47: resolution: {integrity: sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==} engines: {node: '>=12'} @@ -2733,6 +3329,36 @@ packages: esbuild-windows-arm64: 0.14.48 dev: true + /esbuild/0.15.10: + resolution: {integrity: sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.10 + '@esbuild/linux-loong64': 0.15.10 + esbuild-android-64: 0.15.10 + esbuild-android-arm64: 0.15.10 + esbuild-darwin-64: 0.15.10 + esbuild-darwin-arm64: 0.15.10 + esbuild-freebsd-64: 0.15.10 + esbuild-freebsd-arm64: 0.15.10 + esbuild-linux-32: 0.15.10 + esbuild-linux-64: 0.15.10 + esbuild-linux-arm: 0.15.10 + esbuild-linux-arm64: 0.15.10 + esbuild-linux-mips64le: 0.15.10 + esbuild-linux-ppc64le: 0.15.10 + esbuild-linux-riscv64: 0.15.10 + esbuild-linux-s390x: 0.15.10 + esbuild-netbsd-64: 0.15.10 + esbuild-openbsd-64: 0.15.10 + esbuild-sunos-64: 0.15.10 + esbuild-windows-32: 0.15.10 + esbuild-windows-64: 0.15.10 + esbuild-windows-arm64: 0.15.10 + dev: true + /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -3102,6 +3728,21 @@ packages: strip-final-newline: 2.0.0 dev: true + /execa/6.1.0: + resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 3.0.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + /express-http-proxy/1.6.3: resolution: {integrity: sha512-/l77JHcOUrDUX8V67E287VEUQT0lbm71gdGVoodnlWBziarYKgMcpqT7xvh/HM8Jv52phw8Bd8tY+a7QjOr7Yg==} engines: {node: '>=6.0.0'} @@ -3641,6 +4282,11 @@ packages: engines: {node: '>=10.17.0'} dev: true + /human-signals/3.0.1: + resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} + engines: {node: '>=12.20.0'} + dev: true + /iconv-lite/0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -3769,11 +4415,11 @@ packages: engines: {node: '>= 0.10'} dev: true - /ipfs-core-types/0.11.1_undici@5.5.1: + /ipfs-core-types/0.11.1_undici@5.11.0: resolution: {integrity: sha512-K7ZSx9EPEvT4At2kExKyMCqY4Jo3Nb/VnC/iSWqFKRaqb0MTB4IJgvWrwwDuN541tn+dvUnTfOp2wWQSov1UAw==} dependencies: '@ipld/dag-pb': 2.1.17 - '@multiformats/multiaddr': 10.3.3_undici@5.5.1 + '@multiformats/multiaddr': 10.3.3_undici@5.11.0 interface-datastore: 6.1.1 ipfs-unixfs: 6.0.9 multiformats: 9.7.0 @@ -3821,17 +4467,17 @@ packages: - supports-color dev: true - /ipfs-core-utils/0.15.1_undici@5.5.1: + /ipfs-core-utils/0.15.1_undici@5.11.0: resolution: {integrity: sha512-nZmUiLctJWMFrEciVkKdDUO2xLpXWy7Ilt0VMJ35Y5+OJznCXxMHUQo1WUALATlo9ziHgDdHFrAUuyW0yB2rww==} dependencies: - '@libp2p/logger': 1.1.6_undici@5.5.1 - '@multiformats/multiaddr': 10.3.3_undici@5.5.1 - '@multiformats/multiaddr-to-uri': 9.0.1_undici@5.5.1 + '@libp2p/logger': 1.1.6_undici@5.11.0 + '@multiformats/multiaddr': 10.3.3_undici@5.11.0 + '@multiformats/multiaddr-to-uri': 9.0.1_undici@5.11.0 any-signal: 3.0.1 blob-to-it: 1.0.4 browser-readablestream-to-it: 1.0.3 err-code: 3.0.1 - ipfs-core-types: 0.11.1_undici@5.5.1 + ipfs-core-types: 0.11.1_undici@5.11.0 ipfs-unixfs: 6.0.9 ipfs-utils: 9.0.7 it-all: 1.0.6 @@ -4167,6 +4813,11 @@ packages: engines: {node: '>=8'} dev: true + /is-stream/3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /is-string/1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -4282,6 +4933,10 @@ packages: resolution: {integrity: sha512-l9gxWe5TOLUESYnBn85Jxd6tIZLWdRX5YKkHIBfSgbNQ7UFPNUGuWihRV+LlEbfJJIzgLmhwAbaWRi5yWJm8kg==} dev: false + /itty-router/2.6.6: + resolution: {integrity: sha512-hIPHtXGymCX7Lzb2I4G6JgZFE4QEEQwst9GORK7sMYUpJvLfy4yZJr95r04e8DzoAnj6HcxM2m4TbK+juu+18g==} + dev: false + /js-sha3/0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} dev: true @@ -4791,6 +5446,11 @@ packages: engines: {node: '>=8'} dev: true + /mimic-fn/4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -4801,6 +5461,48 @@ packages: engines: {node: '>=4'} dev: true + /miniflare/2.10.0: + resolution: {integrity: sha512-WPveqChVDdmDGv+wFqXjFqEZlZ5/aBlAKX37h/e4TAjl2XsK5nPfQATP8jZXwNDEC5iE29bYZymOqeZkp+t7OA==} + engines: {node: '>=16.13'} + hasBin: true + peerDependencies: + '@miniflare/storage-redis': 2.10.0 + cron-schedule: ^3.0.4 + ioredis: ^4.27.9 + peerDependenciesMeta: + '@miniflare/storage-redis': + optional: true + cron-schedule: + optional: true + ioredis: + optional: true + dependencies: + '@miniflare/cache': 2.10.0 + '@miniflare/cli-parser': 2.10.0 + '@miniflare/core': 2.10.0 + '@miniflare/d1': 2.10.0 + '@miniflare/durable-objects': 2.10.0 + '@miniflare/html-rewriter': 2.10.0 + '@miniflare/http-server': 2.10.0 + '@miniflare/kv': 2.10.0 + '@miniflare/queues': 2.10.0 + '@miniflare/r2': 2.10.0 + '@miniflare/runner-vm': 2.10.0 + '@miniflare/scheduler': 2.10.0 + '@miniflare/shared': 2.10.0 + '@miniflare/sites': 2.10.0 + '@miniflare/storage-file': 2.10.0 + '@miniflare/storage-memory': 2.10.0 + '@miniflare/web-sockets': 2.10.0 + kleur: 4.1.5 + semiver: 1.1.0 + source-map-support: 0.5.21 + undici: 5.9.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /miniflare/2.5.1: resolution: {integrity: sha512-PT56C/j7U6n7WDxnIUHu0d8EY/gedPRsta2b+LsrIHGZPSkxAcPzf2DgbbPU7obv0C4hT9H0GL1fWpWtr2SbDQ==} engines: {node: '>=16.7'} @@ -4880,6 +5582,48 @@ packages: - utf-8-validate dev: true + /miniflare/2.9.0: + resolution: {integrity: sha512-HBGQ5Jj6sMU1B1hX6G3ML46ThtUvu1nvxgXjDDmhp2RhWKYj0XvcohW/nPPL/MTP1gpvfT880De9EHmobVsDsw==} + engines: {node: '>=16.13'} + hasBin: true + peerDependencies: + '@miniflare/storage-redis': 2.9.0 + cron-schedule: ^3.0.4 + ioredis: ^4.27.9 + peerDependenciesMeta: + '@miniflare/storage-redis': + optional: true + cron-schedule: + optional: true + ioredis: + optional: true + dependencies: + '@miniflare/cache': 2.9.0 + '@miniflare/cli-parser': 2.9.0 + '@miniflare/core': 2.9.0 + '@miniflare/d1': 2.9.0 + '@miniflare/durable-objects': 2.9.0 + '@miniflare/html-rewriter': 2.9.0 + '@miniflare/http-server': 2.9.0 + '@miniflare/kv': 2.9.0 + '@miniflare/queues': 2.9.0 + '@miniflare/r2': 2.9.0 + '@miniflare/runner-vm': 2.9.0 + '@miniflare/scheduler': 2.9.0 + '@miniflare/shared': 2.9.0 + '@miniflare/sites': 2.9.0 + '@miniflare/storage-file': 2.9.0 + '@miniflare/storage-memory': 2.9.0 + '@miniflare/web-sockets': 2.9.0 + kleur: 4.1.5 + semiver: 1.1.0 + source-map-support: 0.5.21 + undici: 5.9.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -5081,12 +5825,12 @@ packages: node-fetch: 3.2.6 dev: true - /native-fetch/4.0.2_undici@5.5.1: + /native-fetch/4.0.2_undici@5.11.0: resolution: {integrity: sha512-4QcVlKFtv2EYVS5MBgsGX5+NWKtbDbIECdUXDBGDMAZXq3Jkv9zf+y8iS7Ub8fEdga3GpYeazp9gauNqXHJOCg==} peerDependencies: undici: '*' dependencies: - undici: 5.5.1 + undici: 5.11.0 dev: false /natural-compare/1.4.0: @@ -5185,6 +5929,13 @@ packages: path-key: 3.1.1 dev: true + /npm-run-path/5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + /npmlog/4.1.2: resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} dependencies: @@ -5194,6 +5945,15 @@ packages: set-blocking: 2.0.0 dev: true + /npx-import/1.1.3: + resolution: {integrity: sha512-zy6249FJ81OtPsvz2y0+rgis31EN5wbdwBG2umtEh65W/4onYArHuoUSZ+W+T7BQYK7YF+h9G4CuGPusMCcLOw==} + dependencies: + execa: 6.1.0 + parse-package-name: 1.0.0 + semver: 7.3.7 + validate-npm-package-name: 4.0.0 + dev: true + /number-is-nan/1.0.1: resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} engines: {node: '>=0.10.0'} @@ -5294,6 +6054,13 @@ packages: mimic-fn: 2.1.0 dev: true + /onetime/6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + /optionator/0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -5551,6 +6318,10 @@ packages: engines: {node: '>=6'} dev: true + /parse-package-name/1.0.0: + resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==} + dev: true + /parse5/5.1.0: resolution: {integrity: sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==} dev: true @@ -5585,6 +6356,11 @@ packages: engines: {node: '>=8'} dev: true + /path-key/4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true @@ -6537,7 +7313,6 @@ 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==} @@ -6653,6 +7428,11 @@ packages: engines: {node: '>=6'} dev: true + /strip-final-newline/3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + /strip-indent/3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -6822,6 +7602,18 @@ packages: stacktrace-js: 2.0.2 dev: false + /toucan-js/2.7.0: + resolution: {integrity: sha512-vbvRbFfMLN2Jf9lOkwL1KwIwuCcS/ko0MVACZTYTnbdVlVjsIviwCU0inH0CRcMXCvFAS+uL8z/gSV3y7FpZUQ==} + dependencies: + '@sentry/core': 6.19.6 + '@sentry/hub': 6.19.6 + '@sentry/types': 6.19.6 + '@sentry/utils': 6.19.6 + '@types/cookie': 0.5.0 + cookie: 0.5.0 + stacktrace-js: 2.0.2 + dev: false + /tough-cookie/2.5.0: resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} engines: {node: '>=0.8'} @@ -6956,6 +7748,12 @@ packages: hasBin: true dev: true + /typescript/4.8.4: + resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + /uint8arrays/2.1.10: resolution: {integrity: sha512-Q9/hhJa2836nQfEJSZTmr+pg9+cDJS9XEAp7N2Vg5MzL3bK/mkMVfjscRGYruP9jNda6MAdf4QD/y78gSzkp6A==} dependencies: @@ -6975,9 +7773,21 @@ packages: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + /undici/5.11.0: + resolution: {integrity: sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==} + engines: {node: '>=12.18'} + dependencies: + busboy: 1.6.0 + /undici/5.5.1: resolution: {integrity: sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==} engines: {node: '>=12.18'} + dev: true + + /undici/5.9.1: + resolution: {integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==} + engines: {node: '>=12.18'} + dev: true /unique-string/2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} @@ -7068,6 +7878,13 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /validate-npm-package-name/4.0.0: + resolution: {integrity: sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + builtins: 5.0.1 + dev: true + /varint/5.0.2: resolution: {integrity: sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==} dev: true @@ -7232,7 +8049,7 @@ packages: blake3-wasm: 2.1.5 chokidar: 3.5.3 esbuild: 0.14.47 - miniflare: 2.6.0 + miniflare: 2.9.0 nanoid: 3.3.4 path-to-regexp: 6.2.1 selfsigned: 2.0.1