From d2816c8bf5ba71f2ff1c9120804ceca260d3c3ae Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Wed, 20 Sep 2023 17:47:07 +0100 Subject: [PATCH 1/2] refactor: filecoin client to use new capabilities docs: better docs fix: client tests --- packages/filecoin-client/README.md | 173 +++++++++++++++--- packages/filecoin-client/package.json | 1 + packages/filecoin-client/src/aggregator.js | 43 +++-- packages/filecoin-client/src/chain-tracker.js | 50 ----- packages/filecoin-client/src/deal-tracker.js | 57 ++++++ packages/filecoin-client/src/dealer.js | 52 +++--- packages/filecoin-client/src/index.js | 2 +- packages/filecoin-client/src/service.js | 10 +- packages/filecoin-client/src/storefront.js | 89 +++++++-- packages/filecoin-client/src/types.ts | 72 ++++---- .../filecoin-client/test/aggregator.test.js | 160 +++++----------- ...n-tracker.test.js => deal-tracker.test.js} | 42 ++--- packages/filecoin-client/test/dealer.test.js | 142 +++++++------- .../filecoin-client/test/helpers/mocks.js | 31 ++-- .../filecoin-client/test/storefront.test.js | 172 +++++++++++------ pnpm-lock.yaml | 25 +-- 16 files changed, 655 insertions(+), 466 deletions(-) delete mode 100644 packages/filecoin-client/src/chain-tracker.js create mode 100644 packages/filecoin-client/src/deal-tracker.js rename packages/filecoin-client/test/{chain-tracker.test.js => deal-tracker.test.js} (64%) diff --git a/packages/filecoin-client/README.md b/packages/filecoin-client/README.md index dc5f96533..86cb248bd 100644 --- a/packages/filecoin-client/README.md +++ b/packages/filecoin-client/README.md @@ -3,7 +3,7 @@ ## About -The `@web3-storage/filecoin-client` package provides the "low level" client API to make data uploaded with the w3up platform available in Filecoin Storage providers. It is based on [web3-storage/specs/w3-filecoin.md])https://github.com/web3-storage/specs/blob/feat/filecoin-spec/w3-filecoin.md) and is not intended for web3.storage end users. +The `@web3-storage/filecoin-client` package provides the "low level" client API to make data uploaded with the w3up platform available in Filecoin Storage providers. It is based on [web3-storage/specs/w3-filecoin.md](https://github.com/web3-storage/specs/blob/feat/filecoin-spec/w3-filecoin.md) and is not intended for web3.storage end users. ## Install @@ -15,38 +15,102 @@ npm install @web3-storage/filecoin-client ## Usage -### `Storefront.filecoinAdd` +### `Storefront.filecoinOffer` -Request a Storefront service to add computed filecoin piece into Filecoin Storage Providers. +The [`filecoin/offer`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#filecoinoffer) task can be executed to request storing a content piece in Filecoin. It issues a signed receipt of the execution result. + +A receipt for successful execution will contain an effect, linking to a `filecoin/submit` task that will complete asynchronously. + +Otherwise the task is failed and the receipt will contain details of the reason behind the failure. ```js import { Storefront } from '@web3-storage/filecoin-client' -const add = await Storefront.filecoinQueue( +const res = await Storefront.filecoinOffer( invocationConfig, - piece, - content + content, + piece +) +``` + +```typescript +function filecoinOffer( + conf: InvocationConfig, + content: Link, // Content CID + piece: Piece, // Filecoin piece +): Promise +``` + +More information: [`InvocationConfig`](#invocationconfig) + +### `Storefront.filecoinSubmit` + +The [`filecoin/submit`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#filecoinsubmit) task is an _effect_ linked from successful execution of a `filecoin/offer` task, it is executed to issue a receipt for the success or failure of the task. + +A receipt for successful execution indicates that the offered piece has been submitted to the pipeline. In this case the receipt will contain an effect, linking to a `piece/offer` task that will complete asynchronously. + +Otherwise the task is failed and the receipt will contain details of the reason behind the failure. + +```js +import { Storefront } from '@web3-storage/filecoin-client' + +const res = await Storefront.filecoinSubmit( + invocationConfig, + content, + piece ) ``` ```typescript -function filecoinQueue( +function filecoinSubmit( conf: InvocationConfig, + content: Link, // Content CID piece: Piece, // Filecoin piece +): Promise +``` + +More information: [`InvocationConfig`](#invocationconfig) + +### `Storefront.filecoinAccept` + +The [`filecoin/accept`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#filecoinsubmit) task is an _effect_ linked from successful execution of a `filecoin/offer` task, it is executed to issue a receipt for the success or failure of the task. + +A receipt for successful execution indicates that the offered piece has been accepted in a Filecoin deal. In this case the receipt will contain proofs that the piece was included in an aggregate and deal. + +Otherwise the task is failed and the receipt will contain details of the reason behind the failure. + +```js +import { Storefront } from '@web3-storage/filecoin-client' + +const res = await Storefront.filecoinAccept( + invocationConfig, + content, + piece +) +``` + +```typescript +function filecoinAccept( + conf: InvocationConfig, content: Link, // Content CID + piece: Piece, // Filecoin piece ): Promise ``` More information: [`InvocationConfig`](#invocationconfig) -### `Aggregator.pieceAdd` +### `Aggregator.pieceOffer` + +The [`piece/offer`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#pieceoffer) task can be executed to request that a piece be aggregated for inclusion in an upcoming an Filecoin deal. It issues a signed receipt of the execution result. It is _also_ an effect linked from successful execution of a `filecoin/submit` task. + +A receipt for successful execution will contain an effect, linking to a `piece/accept` task that will complete asynchronously. -Request an Aggregator service to add a filecoin piece into an aggregate to be offered to Filecoin Storage Providers. +Otherwise the task is failed and the receipt will contain details of the reason behind the failure. ```js import { Aggregator } from '@web3-storage/filecoin-client' -const add = await Aggregator.aggregateQueue( +const res = await Aggregator.pieceOffer( invocationConfig, piece, group @@ -54,7 +118,7 @@ const add = await Aggregator.aggregateQueue( ``` ```typescript -function aggregateQueue( +function pieceOffer( conf: InvocationConfig, piece: Piece, // Filecoin piece group: string, // Aggregate grouping with different criterium like storefront @@ -63,48 +127,109 @@ function aggregateQueue( More information: [`InvocationConfig`](#invocationconfig) -### `Dealer.aggregateAdd` +### `Aggregator.pieceAccept` -Request a Dealer service to offer a filecoin piece (larger aggregate of pieces) to Filecoin Storage Providers. +The [`piece/accept`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#pieceaccept) task is an _effect_ linked from successful execution of a `piece/offer` task, it is executed to issue a receipt for the success or failure of the task. + +A receipt for successful execution indicates that the offered piece was included in an aggregate. In this case the receipt will contain the aggregate piece CID and a proof that the piece was included in the aggregate. It also includes an effect, linking to an `aggregate/offer` task that will complete asynchronously. + +Otherwise the task is failed and the receipt will contain details of the reason behind the failure. + +```js +import { Aggregator } from '@web3-storage/filecoin-client' + +const res = await Aggregator.pieceAccept( + invocationConfig, + piece, + group +) +``` + +```typescript +function pieceAccept( + conf: InvocationConfig, + piece: Piece, // Filecoin piece + group: string, // Aggregate grouping with different criterium like storefront +): Promise +``` + +More information: [`InvocationConfig`](#invocationconfig) + +### `Dealer.aggregateOffer` + +The [`aggregate/offer`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#aggregateoffer) task can be executed to request an aggregate be added to a deal with a Storage Provider. It issues a signed receipt of the execution result. It is _also_ an effect linked from successful execution of a `piece/accept` task. + +A receipt for successful execution will contain an effect, linking to an `aggregate/accept` task that will complete asynchronously. + +Otherwise the task is failed and the receipt will contain details of the reason behind the failure. ```js import { Dealer } from '@web3-storage/filecoin-client' -const add = await Dealer.dealQueue( +const res = await Dealer.aggregateOffer( invocationConfig, aggregate, - pieces, - storefront, - label + pieces ) ``` ```typescript -function dealQueue( +function aggregateOffer( conf: InvocationConfig, aggregate: Piece, // Filecoin piece representing aggregate pieces: Piece[], // Filecoin pieces part of the aggregate (sorted) - label: string // optional label for deal ): Promise ``` More information: [`InvocationConfig`](#invocationconfig) -### `Chain.chainInfo` +### `Dealer.aggregateAccept` + +The [`aggregate/accept`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#aggregateaccept) task is an _effect_ linked from successful execution of a `aggregate/offer` task, it is executed to issue a receipt for the success or failure of the task. + +A receipt for successful execution indicates that an aggregate has been accepted for inclusion in a Filecoin deal. In this case the receipt will contain proofs that the piece was included in an aggregate and deal. + +Otherwise the task is failed and the receipt will contain details of the reason behind the failure, as well as multiple effects, linking to `piece/offer` tasks that will retry _valid_ pieces and complete asynchronously. + +```js +import { Dealer } from '@web3-storage/filecoin-client' + +const res = await Dealer.aggregateAccept( + invocationConfig, + aggregate, + pieces +) +``` + +```typescript +function aggregateAccept( + conf: InvocationConfig, + aggregate: Piece, // Filecoin piece representing aggregate + pieces: Piece[], // Filecoin pieces part of the aggregate (sorted) +): Promise +``` + +More information: [`InvocationConfig`](#invocationconfig) + +### `DealTracker.dealInfo` + +The [`deal/info`](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#dealinfo) task can be executed to request deal information for a given piece. It issues a signed receipt of the execution result. + +A receipt for successful execution will contain details of deals the provided piece CID is currently active in. -Request a Chain service to find chain information of a given piece. It will return deals where given piece is present in Receipt. +Otherwise the task is failed and the receipt will contain details of the reason behind the failure. ```js -import { Chain } from '@web3-storage/filecoin-client' +import { DealTracker } from '@web3-storage/filecoin-client' -const add = await Chain.chainInfo( +const add = await DealTracker.dealInfo( invocationConfig, piece ) ``` ```typescript -function chainInfo( +function dealInfo( conf: InvocationConfig, piece: Piece, // Filecoin piece to check ): Promise diff --git a/packages/filecoin-client/package.json b/packages/filecoin-client/package.json index 5556350e1..3f11fccae 100644 --- a/packages/filecoin-client/package.json +++ b/packages/filecoin-client/package.json @@ -65,6 +65,7 @@ }, "devDependencies": { "@ipld/car": "^5.1.1", + "@ipld/dag-json": "^10.1.4", "@types/assert": "^1.5.6", "@types/mocha": "^10.0.1", "@ucanto/principal": "^8.0.0", diff --git a/packages/filecoin-client/src/aggregator.js b/packages/filecoin-client/src/aggregator.js index 22ff517af..d702dddf3 100644 --- a/packages/filecoin-client/src/aggregator.js +++ b/packages/filecoin-client/src/aggregator.js @@ -1,8 +1,6 @@ import { connect } from '@ucanto/client' import { CAR, HTTP } from '@ucanto/transport' - -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - +import * as Aggregator from '@web3-storage/capabilities/filecoin/aggregator' import { services } from './service.js' /** @@ -21,14 +19,25 @@ export const connection = connect({ }) /** - * Queues a piece to the aggregator system of the filecoin pipeline. + * The `piece/offer` task can be executed to request that a piece be aggregated + * for inclusion in an upcoming an Filecoin deal. It issues a signed receipt + * of the execution result. It is _also_ an effect linked from successful + * execution of a `filecoin/submit` task. + * + * A receipt for successful execution will contain an effect, linking to a + * `piece/accept` task that will complete asynchronously. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#pieceoffer * * @param {import('./types.js').InvocationConfig} conf - Configuration * @param {import('@web3-storage/data-segment').PieceLink} piece * @param {string} group * @param {import('./types.js').RequestOptions} [options] */ -export async function aggregateQueue( +export async function pieceOffer( { issuer, with: resource, proofs, audience }, piece, group, @@ -37,7 +46,7 @@ export async function aggregateQueue( /* c8 ignore next */ const conn = options.connection ?? connection - const invocation = FilecoinCapabilities.aggregateQueue.invoke({ + const invocation = Aggregator.pieceOffer.invoke({ issuer, /* c8 ignore next */ audience: audience ?? services.AGGREGATOR.principal, @@ -53,32 +62,42 @@ export async function aggregateQueue( } /** - * Add a piece to the aggregator system of the filecoin pipeline. + * The `piece/accept` task is an _effect_ linked from successful execution of a + * `piece/offer` task, it is executed to issue a receipt for the success or + * failure of the task. + * + * A receipt for successful execution indicates that the offered piece was + * included in an aggregate. In this case the receipt will contain the + * aggregate piece CID and a proof that the piece was included in the + * aggregate. It also includes an effect, linking to an `aggregate/offer` task + * that will complete asynchronously. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#pieceaccept * * @param {import('./types.js').InvocationConfig} conf - Configuration * @param {import('@web3-storage/data-segment').PieceLink} piece - * @param {string} storefront * @param {string} group * @param {import('./types.js').RequestOptions} [options] */ -export async function aggregateAdd( +export async function pieceAccept( { issuer, with: resource, proofs, audience }, piece, - storefront, group, options = {} ) { /* c8 ignore next */ const conn = options.connection ?? connection - const invocation = FilecoinCapabilities.aggregateAdd.invoke({ + const invocation = Aggregator.pieceAccept.invoke({ issuer, /* c8 ignore next */ audience: audience ?? services.AGGREGATOR.principal, with: resource, nb: { piece, - storefront, group, }, proofs, diff --git a/packages/filecoin-client/src/chain-tracker.js b/packages/filecoin-client/src/chain-tracker.js deleted file mode 100644 index d06dfb7b5..000000000 --- a/packages/filecoin-client/src/chain-tracker.js +++ /dev/null @@ -1,50 +0,0 @@ -import { connect } from '@ucanto/client' -import { CAR, HTTP } from '@ucanto/transport' - -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import { services } from './service.js' - -/** - * @typedef {import('./types.js').ChainTrackerService} ChainTrackerService - * @typedef {import('@ucanto/interface').ConnectionView} ConnectionView - */ - -/** @type {ConnectionView} */ -export const connection = connect({ - id: services.CHAIN_TRACKER.principal, - codec: CAR.outbound, - channel: HTTP.open({ - url: services.CHAIN_TRACKER.url, - method: 'POST', - }), -}) - -/** - * Get chain information for a given a piece.. - * - * @param {import('./types.js').InvocationConfig} conf - Configuration - * @param {import('@web3-storage/data-segment').PieceLink} piece - * @param {import('./types.js').RequestOptions} [options] - */ -export async function chainInfo( - { issuer, with: resource, proofs, audience }, - piece, - options = {} -) { - /* c8 ignore next */ - const conn = options.connection ?? connection - - const invocation = FilecoinCapabilities.chainTrackerInfo.invoke({ - issuer, - /* c8 ignore next */ - audience: audience ?? services.CHAIN_TRACKER.principal, - with: resource, - nb: { - piece, - }, - proofs, - }) - - return await invocation.execute(conn) -} diff --git a/packages/filecoin-client/src/deal-tracker.js b/packages/filecoin-client/src/deal-tracker.js new file mode 100644 index 000000000..4069ee689 --- /dev/null +++ b/packages/filecoin-client/src/deal-tracker.js @@ -0,0 +1,57 @@ +import { connect } from '@ucanto/client' +import { CAR, HTTP } from '@ucanto/transport' +import * as DealTracker from '@web3-storage/capabilities/filecoin/deal-tracker' +import { services } from './service.js' + +/** + * @typedef {import('./types.js').DealTrackerService} DealTrackerService + * @typedef {import('@ucanto/interface').ConnectionView} ConnectionView + */ + +/** @type {ConnectionView} */ +export const connection = connect({ + id: services.DEAL_TRACKER.principal, + codec: CAR.outbound, + channel: HTTP.open({ + url: services.DEAL_TRACKER.url, + method: 'POST', + }), +}) + +/** + * The `deal/info` task can be executed to request deal information for a given + * piece. It issues a signed receipt of the execution result. + * + * A receipt for successful execution will contain details of deals the + * provided piece CID is currently active in. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#dealinfo + * + * @param {import('./types.js').InvocationConfig} conf - Configuration + * @param {import('@web3-storage/data-segment').PieceLink} piece + * @param {import('./types.js').RequestOptions} [options] + */ +export async function dealInfo( + { issuer, with: resource, proofs, audience }, + piece, + options = {} +) { + /* c8 ignore next */ + const conn = options.connection ?? connection + + const invocation = DealTracker.dealInfo.invoke({ + issuer, + /* c8 ignore next */ + audience: audience ?? services.DEAL_TRACKER.principal, + with: resource, + nb: { + piece, + }, + proofs, + }) + + return await invocation.execute(conn) +} diff --git a/packages/filecoin-client/src/dealer.js b/packages/filecoin-client/src/dealer.js index 4ff29852d..f8ad92cab 100644 --- a/packages/filecoin-client/src/dealer.js +++ b/packages/filecoin-client/src/dealer.js @@ -1,9 +1,7 @@ import { connect } from '@ucanto/client' import { CAR, HTTP } from '@ucanto/transport' import { CBOR } from '@ucanto/core' - -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - +import * as Dealer from '@web3-storage/capabilities/filecoin/dealer' import { services } from './service.js' /** @@ -22,28 +20,35 @@ export const connection = connect({ }) /** - * Queues a piece (aggregate) to the dealer system of the filecoin pipeline to offer to SPs. + * The `aggregate/offer` task can be executed to request an aggregate be added + * to a deal with a Storage Provider. It issues a signed receipt of the + * execution result. It is _also_ an effect linked from successful execution of + * a `piece/accept` task. + * + * A receipt for successful execution will contain an effect, linking to an + * `aggregate/accept` task that will complete asynchronously. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#aggregateoffer * * @param {import('./types.js').InvocationConfig} conf - Configuration * @param {import('@web3-storage/data-segment').PieceLink} aggregate * @param {import('@web3-storage/data-segment').PieceLink[]} pieces - * @param {string} storefront - * @param {string} label * @param {import('./types.js').RequestOptions} [options] */ -export async function dealQueue( +export async function aggregateOffer( { issuer, with: resource, proofs, audience }, aggregate, pieces, - storefront, - label, options = {} ) { /* c8 ignore next */ const conn = options.connection ?? connection const block = await CBOR.write(pieces) - const invocation = FilecoinCapabilities.dealQueue.invoke({ + const invocation = Dealer.aggregateOffer.invoke({ issuer, /* c8 ignore next */ audience: audience ?? services.AGGREGATOR.principal, @@ -51,8 +56,6 @@ export async function dealQueue( nb: { aggregate, pieces: block.cid, - storefront, - label, }, proofs, }) @@ -62,28 +65,37 @@ export async function dealQueue( } /** - * Add a piece (aggregate) to the dealer system of the filecoin pipeline to offer to SPs. + * The `aggregate/accept` task is an _effect_ linked from successful execution + * of a `aggregate/offer` task, it is executed to issue a receipt for the + * success or failure of the task. + * + * A receipt for successful execution indicates that an aggregate has been + * accepted for inclusion in a Filecoin deal. In this case the receipt will + * contain proofs that the piece was included in an aggregate and deal. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure, as well as multiple effects, linking to + * `piece/offer` tasks that will retry _valid_ pieces and complete + * asynchronously. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#aggregateaccept * * @param {import('./types.js').InvocationConfig} conf - Configuration * @param {import('@web3-storage/data-segment').PieceLink} aggregate * @param {import('@web3-storage/data-segment').PieceLink[]} pieces - * @param {string} storefront - * @param {string} label * @param {import('./types.js').RequestOptions} [options] */ -export async function dealAdd( +export async function aggregateAccept( { issuer, with: resource, proofs, audience }, aggregate, pieces, - storefront, - label, options = {} ) { /* c8 ignore next */ const conn = options.connection ?? connection const block = await CBOR.write(pieces) - const invocation = FilecoinCapabilities.dealAdd.invoke({ + const invocation = Dealer.aggregateAccept.invoke({ issuer, /* c8 ignore next */ audience: audience ?? services.AGGREGATOR.principal, @@ -91,8 +103,6 @@ export async function dealAdd( nb: { aggregate, pieces: block.cid, - storefront, - label, }, proofs, }) diff --git a/packages/filecoin-client/src/index.js b/packages/filecoin-client/src/index.js index 4ac9cae0b..dc85ebedb 100644 --- a/packages/filecoin-client/src/index.js +++ b/packages/filecoin-client/src/index.js @@ -1,4 +1,4 @@ export * as Storefront from './storefront.js' export * as Aggregator from './aggregator.js' export * as Dealer from './dealer.js' -export * as Chain from './chain-tracker.js' +export * as DealTracker from './deal-tracker.js' diff --git a/packages/filecoin-client/src/service.js b/packages/filecoin-client/src/service.js index c5e62c2ee..f4fdd62df 100644 --- a/packages/filecoin-client/src/service.js +++ b/packages/filecoin-client/src/service.js @@ -18,11 +18,11 @@ export const services = { principal: DID.parse('did:web:web3.storage'), }, DEALER: { - url: new URL('https://spade-proxy.web3.storage'), - principal: DID.parse('did:web:spade.web3.storage'), + url: new URL('https://dealer.web3.storage'), + principal: DID.parse('did:web:dealer.web3.storage'), }, - CHAIN_TRACKER: { - url: new URL('https://spade-proxy.web3.storage'), - principal: DID.parse('did:web:spade.web3.storage'), + DEAL_TRACKER: { + url: new URL('https://tracker.web3.storage'), + principal: DID.parse('did:web:tracker.web3.storage'), }, } diff --git a/packages/filecoin-client/src/storefront.js b/packages/filecoin-client/src/storefront.js index 4d6c05042..7eed402d8 100644 --- a/packages/filecoin-client/src/storefront.js +++ b/packages/filecoin-client/src/storefront.js @@ -1,8 +1,6 @@ import { connect } from '@ucanto/client' import { CAR, HTTP } from '@ucanto/transport' - -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - +import * as Storefront from '@web3-storage/capabilities/filecoin/storefront' import { services } from './service.js' /** @@ -21,29 +19,38 @@ export const connection = connect({ }) /** - * Queues a piece to the filecoin pipeline. + * The `filecoin/offer` task can be executed to request storing a content piece + * in Filecoin. It issues a signed receipt of the execution result. + * + * A receipt for successful execution will contain an effect, linking to a + * `filecoin/submit` task that will complete asynchronously. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#filecoinoffer * * @param {import('./types.js').InvocationConfig} conf - Configuration - * @param {import('@web3-storage/data-segment').PieceLink} piece * @param {import('multiformats').UnknownLink} content + * @param {import('@web3-storage/data-segment').PieceLink} piece * @param {import('./types.js').RequestOptions} [options] */ -export async function filecoinQueue( +export async function filecoinOffer( { issuer, with: resource, proofs, audience }, - piece, content, + piece, options = {} ) { /* c8 ignore next */ const conn = options.connection ?? connection - const invocation = FilecoinCapabilities.filecoinQueue.invoke({ + const invocation = Storefront.filecoinOffer.invoke({ issuer, /* c8 ignore next */ audience: audience ?? services.STOREFRONT.principal, with: resource, nb: { - content: content, + content, piece, }, proofs, @@ -53,29 +60,83 @@ export async function filecoinQueue( } /** - * Add a piece to the filecoin pipeline. + * The `filecoin/submit` task is an _effect_ linked from successful execution + * of a `filecoin/offer` task, it is executed to issue a receipt for the + * success or failure of the task. + * + * A receipt for successful execution indicates that the offered piece has been + * submitted to the pipeline. In this case the receipt will contain an effect, + * linking to a `piece/offer` task that will complete asynchronously. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#filecoinsubmit * * @param {import('./types.js').InvocationConfig} conf - Configuration - * @param {import('@web3-storage/data-segment').PieceLink} piece * @param {import('multiformats').UnknownLink} content + * @param {import('@web3-storage/data-segment').PieceLink} piece * @param {import('./types.js').RequestOptions} [options] */ -export async function filecoinAdd( +export async function filecoinSubmit( { issuer, with: resource, proofs, audience }, + content, piece, + options = {} +) { + /* c8 ignore next */ + const conn = options.connection ?? connection + + const invocation = Storefront.filecoinSubmit.invoke({ + issuer, + /* c8 ignore next */ + audience: audience ?? services.STOREFRONT.principal, + with: resource, + nb: { + content, + piece, + }, + proofs, + }) + + return await invocation.execute(conn) +} + +/** + * The `filecoin/accept` task is an _effect_ linked from successful execution + * of a `filecoin/offer` task, it is executed to issue a receipt for the + * success or failure of the task. + * + * A receipt for successful execution indicates that the offered piece has been + * accepted in a Filecoin deal. In this case the receipt will contain proofs + * that the piece was included in an aggregate and deal. + * + * Otherwise the task is failed and the receipt will contain details of the + * reason behind the failure. + * + * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#filecoinaccept + * + * @param {import('./types.js').InvocationConfig} conf - Configuration + * @param {import('multiformats').UnknownLink} content + * @param {import('@web3-storage/data-segment').PieceLink} piece + * @param {import('./types.js').RequestOptions} [options] + */ +export async function filecoinAccept( + { issuer, with: resource, proofs, audience }, content, + piece, options = {} ) { /* c8 ignore next */ const conn = options.connection ?? connection - const invocation = FilecoinCapabilities.filecoinAdd.invoke({ + const invocation = Storefront.filecoinAccept.invoke({ issuer, /* c8 ignore next */ audience: audience ?? services.STOREFRONT.principal, with: resource, nb: { - content: content, + content, piece, }, proofs, diff --git a/packages/filecoin-client/src/types.ts b/packages/filecoin-client/src/types.ts index 6fe79ee90..963212f0d 100644 --- a/packages/filecoin-client/src/types.ts +++ b/packages/filecoin-client/src/types.ts @@ -7,24 +7,33 @@ import { Principal, } from '@ucanto/interface' import { - FilecoinQueue, - FilecoinAdd, - FilecoinAddSuccess, - FilecoinAddFailure, - AggregateQueue, - AggregateAdd, - AggregateAddSuccess, - AggregateAddFailure, - DealQueue, - DealAdd, - DealAddSuccess, - DealAddFailure, - ChainTrackerInfo, - ChainTrackerInfoSuccess, - ChainTrackerInfoFailure, + FilecoinOffer, + FilecoinOfferSuccess, + FilecoinOfferFailure, + FilecoinSubmit, + FilecoinSubmitSuccess, + FilecoinSubmitFailure, + FilecoinAccept, + FilecoinAcceptSuccess, + FilecoinAcceptFailure, + PieceOffer, + PieceOfferSuccess, + PieceOfferFailure, + PieceAccept, + PieceAcceptSuccess, + PieceAcceptFailure, + AggregateOffer, + AggregateOfferSuccess, + AggregateOfferFailure, + AggregateAccept, + AggregateAcceptSuccess, + AggregateAcceptFailure, + DealInfo, + DealInfoSuccess, + DealInfoFailure } from '@web3-storage/capabilities/types' -export type SERVICE = 'STOREFRONT' | 'AGGREGATOR' | 'DEALER' | 'CHAIN_TRACKER' +export type SERVICE = 'STOREFRONT' | 'AGGREGATOR' | 'DEALER' | 'DEAL_TRACKER' export interface ServiceConfig { url: URL principal: Principal @@ -51,36 +60,29 @@ export interface InvocationConfig { export interface StorefrontService { filecoin: { - queue: ServiceMethod - add: ServiceMethod + offer: ServiceMethod + submit: ServiceMethod + accept: ServiceMethod } } export interface AggregatorService { - aggregate: { - queue: ServiceMethod< - AggregateQueue, - AggregateAddSuccess, - AggregateAddFailure - > - add: ServiceMethod + piece: { + offer: ServiceMethod + accept: ServiceMethod } } export interface DealerService { - deal: { - queue: ServiceMethod - add: ServiceMethod + aggregate: { + offer: ServiceMethod + accept: ServiceMethod } } -export interface ChainTrackerService { - 'chain-tracker': { - info: ServiceMethod< - ChainTrackerInfo, - ChainTrackerInfoSuccess, - ChainTrackerInfoFailure - > +export interface DealTrackerService { + deal: { + info: ServiceMethod } } diff --git a/packages/filecoin-client/test/aggregator.test.js b/packages/filecoin-client/test/aggregator.test.js index 558d0f82d..7d3796694 100644 --- a/packages/filecoin-client/test/aggregator.test.js +++ b/packages/filecoin-client/test/aggregator.test.js @@ -3,41 +3,35 @@ import * as Signer from '@ucanto/principal/ed25519' import * as Client from '@ucanto/client' import * as Server from '@ucanto/server' import * as CAR from '@ucanto/transport/car' -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import { aggregateQueue, aggregateAdd } from '../src/aggregator.js' - -import { randomCargo } from './helpers/random.js' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' +import { pieceOffer, pieceAccept } from '../src/aggregator.js' +import { randomAggregate, randomCargo } from './helpers/random.js' import { mockService } from './helpers/mocks.js' -import { OperationFailed, OperationErrorName } from './helpers/errors.js' import { serviceProvider as aggregatorService } from './fixtures.js' -describe('aggregate/add', () => { - it('storefront queues a filecoin piece for aggregator to handle', async () => { +describe('aggregator', () => { + it('storefront offers a filecoin piece', async () => { const { storefront } = await getContext() // Generate cargo to add const [cargo] = await randomCargo(1, 100) const group = 'did:web:free.web3.storage' - /** @type {import('@web3-storage/capabilities/types').AggregateAddSuccess} */ - const pieceAddResponse = { + /** @type {import('@web3-storage/capabilities/types').PieceOfferSuccess} */ + const pieceOfferResponse = { piece: cargo.link, } // Create Ucanto service const service = mockService({ - aggregate: { - queue: Server.provideAdvanced({ - capability: FilecoinCapabilities.aggregateQueue, + piece: { + offer: Server.provideAdvanced({ + capability: AggregatorCaps.pieceOffer, handler: async ({ invocation, context }) => { assert.strictEqual(invocation.issuer.did(), storefront.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual( - invCap.can, - FilecoinCapabilities.aggregateQueue.can - ) + assert.strictEqual(invCap.can, AggregatorCaps.pieceOffer.can) assert.equal(invCap.with, invocation.issuer.did()) // piece link assert.ok(invCap.nb?.piece.equals(cargo.link.link())) @@ -45,30 +39,25 @@ describe('aggregate/add', () => { assert.strictEqual(invCap.nb?.group, group) // Create effect for receipt with self signed queued operation - const fx = await FilecoinCapabilities.aggregateAdd + const fx = await AggregatorCaps.pieceAccept .invoke({ issuer: context.id, audience: context.id, with: context.id.did(), nb: { ...invCap.nb, - // add storefront - storefront: invCap.with, }, }) .delegate() - return Server.ok(pieceAddResponse).join(fx.link()) + return Server.ok(pieceOfferResponse).join(fx.link()) }, }), - add: () => { - throw new Error('not implemented') - }, }, }) - // invoke piece add from storefront - const res = await aggregateQueue( + // invoke piece offer from storefront + const res = await pieceOffer( { issuer: storefront, with: storefront.did(), @@ -80,130 +69,69 @@ describe('aggregate/add', () => { ) assert.ok(res.out.ok) - assert.ok(res.out.ok.piece.equals(pieceAddResponse.piece)) + assert.ok(res.out.ok.piece.equals(pieceOfferResponse.piece)) // includes effect fx in receipt assert.ok(res.fx.join) }) - it('aggregator self invokes add a filecoin piece to accept the piece queued', async () => { - const { storefront } = await getContext() - - // Generate cargo to add - const [cargo] = await randomCargo(1, 100) - const storefrontId = storefront.did() + it('aggregator accepts a filecoin piece', async () => { + const { pieces, aggregate } = await randomAggregate(100, 100) const group = 'did:web:free.web3.storage' - /** @type {import('@web3-storage/capabilities/types').AggregateAddSuccess} */ - const pieceAddResponse = { - piece: cargo.link, + /** @type {import('@web3-storage/capabilities/types').PieceAcceptSuccess} */ + const pieceAcceptResponse = { + piece: pieces[0].link, + aggregate: aggregate.link, + inclusion: { + subtree: { + path: [], + index: 0n, + }, + index: { + path: [], + index: 0n, + }, + } } // Create Ucanto service const service = mockService({ - aggregate: { - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.aggregateAdd, + piece: { + accept: Server.provideAdvanced({ + capability: AggregatorCaps.pieceAccept, handler: async ({ invocation }) => { assert.strictEqual(invocation.issuer.did(), aggregatorService.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual( - invCap.can, - FilecoinCapabilities.aggregateAdd.can - ) + assert.strictEqual(invCap.can, AggregatorCaps.pieceAccept.can) assert.equal(invCap.with, invocation.issuer.did()) // piece link - assert.ok(invCap.nb?.piece.equals(cargo.link.link())) + assert.ok(invCap.nb?.piece.equals(pieces[0].link)) // group assert.strictEqual(invCap.nb?.group, group) - return Server.ok(pieceAddResponse) + return Server.ok(pieceAcceptResponse) }, }), - queue: () => { - throw new Error('not implemented') - }, }, }) - // self invoke piece/add from aggregator - const res = await aggregateAdd( + // self invoke piece/offer from aggregator + const res = await pieceAccept( { issuer: aggregatorService, with: aggregatorService.did(), audience: aggregatorService, }, - cargo.link.link(), - storefrontId, + pieces[0].link, group, { connection: getConnection(service).connection } ) assert.ok(res.out.ok) - assert.ok(res.out.ok.piece.equals(pieceAddResponse.piece)) - // does not include effect fx in receipt - assert.ok(!res.fx.join) - }) - - it('aggregator self invokes add a filecoin piece to reject the piece queued', async () => { - const { storefront } = await getContext() - - // Generate cargo to add - const [cargo] = await randomCargo(1, 100) - const storefrontId = storefront.did() - const group = 'did:web:free.web3.storage' - - /** @type {import('@web3-storage/capabilities/types').AggregateAddFailure} */ - const pieceAddResponse = new OperationFailed( - 'failed to add to aggregate', - cargo.link - ) - - // Create Ucanto service - const service = mockService({ - aggregate: { - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.aggregateAdd, - handler: async ({ invocation }) => { - assert.strictEqual(invocation.issuer.did(), aggregatorService.did()) - assert.strictEqual(invocation.capabilities.length, 1) - const invCap = invocation.capabilities[0] - assert.strictEqual( - invCap.can, - FilecoinCapabilities.aggregateAdd.can - ) - assert.equal(invCap.with, invocation.issuer.did()) - // piece link - assert.ok(invCap.nb?.piece.equals(cargo.link.link())) - // group - assert.strictEqual(invCap.nb?.group, group) - - return { - error: pieceAddResponse, - } - }, - }), - queue: () => { - throw new Error('not implemented') - }, - }, - }) - - // self invoke piece add from aggregator - const res = await aggregateAdd( - { - issuer: aggregatorService, - with: aggregatorService.did(), - audience: aggregatorService, - }, - cargo.link.link(), - storefrontId, - group, - { connection: getConnection(service).connection } - ) - - assert.ok(res.out.error) - assert.deepEqual(res.out.error.name, OperationErrorName) + assert.ok(res.out.ok.piece.equals(pieceAcceptResponse.piece)) + assert.ok(res.out.ok.aggregate.equals(pieceAcceptResponse.aggregate)) + assert.deepEqual(res.out.ok.inclusion, pieceAcceptResponse.inclusion) // does not include effect fx in receipt assert.ok(!res.fx.join) }) diff --git a/packages/filecoin-client/test/chain-tracker.test.js b/packages/filecoin-client/test/deal-tracker.test.js similarity index 64% rename from packages/filecoin-client/test/chain-tracker.test.js rename to packages/filecoin-client/test/deal-tracker.test.js index f525afa80..ae12d202e 100644 --- a/packages/filecoin-client/test/chain-tracker.test.js +++ b/packages/filecoin-client/test/deal-tracker.test.js @@ -3,52 +3,49 @@ import * as Signer from '@ucanto/principal/ed25519' import * as Client from '@ucanto/client' import * as Server from '@ucanto/server' import * as CAR from '@ucanto/transport/car' -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import { chainInfo } from '../src/chain-tracker.js' - +import * as DealTrackerCaps from '@web3-storage/capabilities/filecoin/deal-tracker' +import { dealInfo } from '../src/deal-tracker.js' import { randomCargo } from './helpers/random.js' import { mockService } from './helpers/mocks.js' import { serviceProvider as chainService } from './fixtures.js' -describe('chain.info', () => { - it('storefront gets info of a filecoin piece from chain', async () => { +describe('deal tracker', () => { + it('storefront gets deal information', async () => { const { storefront } = await getContext() // Generate cargo to get info const [cargo] = await randomCargo(1, 100) - /** @type {import('@web3-storage/capabilities/types').ChainTrackerInfoSuccess} */ - const chainInfoResponse = { - piece: cargo.link, + /** @type {import('@web3-storage/capabilities/types').DealInfoSuccess} */ + const dealInfoResponse = { + deals: { + '12345': { + provider: 'f099' + } + } } // Create Ucanto service const service = mockService({ - 'chain-tracker': { + deal: { info: Server.provideAdvanced({ - capability: FilecoinCapabilities.chainTrackerInfo, + capability: DealTrackerCaps.dealInfo, handler: async ({ invocation, context }) => { assert.strictEqual(invocation.issuer.did(), storefront.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual( - invCap.can, - FilecoinCapabilities.chainTrackerInfo.can - ) + assert.strictEqual(invCap.can, DealTrackerCaps.dealInfo.can) assert.equal(invCap.with, invocation.issuer.did()) - // piece link - assert.ok(invCap.nb?.piece.equals(cargo.link.link())) - - return Server.ok(chainInfoResponse) + assert.ok(invCap.nb?.piece.equals(cargo.link)) + return Server.ok(dealInfoResponse) }, }), }, }) // invoke piece add from storefront - const res = await chainInfo( + const res = await dealInfo( { issuer: storefront, with: storefront.did(), @@ -59,8 +56,7 @@ describe('chain.info', () => { ) assert.ok(res.out.ok) - // @ts-expect-error todo check error - assert.ok(res.out.ok.piece.equals(chainInfoResponse.piece)) + assert.deepEqual(res.out.ok, dealInfoResponse) }) }) @@ -71,7 +67,7 @@ async function getContext() { } /** - * @param {import('../src/types.js').ChainTrackerService} service + * @param {import('../src/types.js').DealTrackerService} service */ function getConnection(service) { const server = Server.create({ diff --git a/packages/filecoin-client/test/dealer.test.js b/packages/filecoin-client/test/dealer.test.js index fbdc6269f..08ff14124 100644 --- a/packages/filecoin-client/test/dealer.test.js +++ b/packages/filecoin-client/test/dealer.test.js @@ -4,40 +4,34 @@ import * as Client from '@ucanto/client' import * as Server from '@ucanto/server' import * as CAR from '@ucanto/transport/car' import { CBOR } from '@ucanto/core' -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import { dealQueue, dealAdd } from '../src/dealer.js' - +import * as DealerCaps from '@web3-storage/capabilities/filecoin/dealer' +import * as dagJSON from '@ipld/dag-json' +import { aggregateOffer, aggregateAccept } from '../src/dealer.js' import { randomAggregate } from './helpers/random.js' import { mockService } from './helpers/mocks.js' -import { OperationFailed, OperationErrorName } from './helpers/errors.js' import { serviceProvider as dealerService } from './fixtures.js' -describe('dealer.add', () => { - it('aggregator adds an aggregate piece to the dealer, getting the piece queued', async () => { - const { aggregator, storefront: storefrontSigner } = await getContext() - - // generate aggregate to add +describe('dealer', () => { + it('aggregator offers an aggregate', async () => { + const { aggregator } = await getContext() const { pieces, aggregate } = await randomAggregate(100, 100) const offer = pieces.map((p) => p.link) const piecesBlock = await CBOR.write(offer) - const storefront = storefrontSigner.did() - const label = 'label' - /** @type {import('@web3-storage/capabilities/types').DealAddSuccess} */ - const dealAddResponse = { + /** @type {import('@web3-storage/capabilities/types').AggregateOfferSuccess} */ + const aggregateOfferResponse = { aggregate: aggregate.link, } // Create Ucanto service const service = mockService({ - deal: { - queue: Server.provideAdvanced({ - capability: FilecoinCapabilities.dealQueue, + aggregate: { + offer: Server.provideAdvanced({ + capability: DealerCaps.aggregateOffer, handler: async ({ invocation, context }) => { assert.strictEqual(invocation.issuer.did(), aggregator.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual(invCap.can, FilecoinCapabilities.dealQueue.can) + assert.strictEqual(invCap.can, DealerCaps.aggregateOffer.can) assert.equal(invCap.with, invocation.issuer.did()) assert.ok(invCap.nb) @@ -51,7 +45,7 @@ describe('dealer.add', () => { ) // Create effect for receipt with self signed queued operation - const fx = await FilecoinCapabilities.dealAdd + const fx = await DealerCaps.aggregateAccept .invoke({ issuer: context.id, audience: context.id, @@ -60,60 +54,63 @@ describe('dealer.add', () => { }) .delegate() - return Server.ok(dealAddResponse).join(fx.link()) + return Server.ok(aggregateOfferResponse).join(fx.link()) }, }), - add: () => { - throw new Error('not implemented') - }, }, }) - // invoke piece add from storefront - const res = await dealQueue( + // invoke aggregate/offer from aggregator + const res = await aggregateOffer( { issuer: aggregator, with: aggregator.did(), audience: dealerService, }, - aggregate.link.link(), + aggregate.link, offer, - storefront, - label, { connection: getConnection(service).connection } ) assert.ok(res.out.ok) - assert.ok(res.out.ok.aggregate?.equals(dealAddResponse.aggregate)) + assert.ok(res.out.ok.aggregate?.equals(aggregateOfferResponse.aggregate)) // includes effect fx in receipt assert.ok(res.fx.join) }) - it('dealer self invokes add an aggregate piece to accept the piece queued', async () => { - const { storefront: storefrontSigner } = await getContext() - - // generate aggregate to add + it('dealer accepts an aggregate', async () => { const { pieces, aggregate } = await randomAggregate(100, 100) const offer = pieces.map((p) => p.link) const piecesBlock = await CBOR.write(offer) - const storefront = storefrontSigner.did() - const label = 'label' - /** @type {import('@web3-storage/capabilities/types').DealAddSuccess} */ - const dealAddResponse = { - aggregate: aggregate.link, + /** @type {import('@web3-storage/capabilities/types').AggregateAcceptSuccess} */ + const aggregateAcceptResponse = { + inclusion: { + subtree: { + path: [], + index: 0n, + }, + index: { + path: [], + index: 0n, + }, + }, + auxDataType: 0n, + auxDataSource: { + dealID: 1138n + } } // Create Ucanto service const service = mockService({ - deal: { - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.dealAdd, + aggregate: { + accept: Server.provideAdvanced({ + capability: DealerCaps.aggregateAccept, handler: async ({ invocation }) => { assert.strictEqual(invocation.issuer.did(), dealerService.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual(invCap.can, FilecoinCapabilities.dealAdd.can) + assert.strictEqual(invCap.can, DealerCaps.aggregateAccept.can) assert.equal(invCap.with, invocation.issuer.did()) assert.ok(invCap.nb) @@ -126,17 +123,14 @@ describe('dealer.add', () => { invocationBlocks.find((b) => b.cid.equals(piecesBlock.cid)) ) - return Server.ok(dealAddResponse) + return Server.ok(aggregateAcceptResponse) }, }), - queue: () => { - throw new Error('not implemented') - }, }, }) - // invoke piece add from storefront - const res = await dealAdd( + // invoke aggregate accept from dealer + const res = await aggregateAccept( { issuer: dealerService, with: dealerService.did(), @@ -144,43 +138,41 @@ describe('dealer.add', () => { }, aggregate.link.link(), offer, - storefront, - label, { connection: getConnection(service).connection } ) assert.ok(res.out.ok) - assert.ok(res.out.ok.aggregate?.equals(dealAddResponse.aggregate)) + assert.deepEqual(res.out.ok, aggregateAcceptResponse) // does not include effect fx in receipt assert.ok(!res.fx.join) }) - it('dealer self invokes add an aggregate piece to reject the piece queued', async () => { - const { storefront: storefrontSigner } = await getContext() - - // generate aggregate to add + it('dealer rejects an aggregate', async () => { const { pieces, aggregate } = await randomAggregate(100, 100) const offer = pieces.map((p) => p.link) const piecesBlock = await CBOR.write(offer) - const storefront = storefrontSigner.did() - const label = 'label' - /** @type {import('@web3-storage/capabilities/types').DealAddFailure} */ - const dealAddResponse = new OperationFailed( - 'failed to add to aggregate', - aggregate.link - ) + /** @type {import('@web3-storage/capabilities/types').AggregateAcceptFailure} */ + const aggregateAcceptResponse = { + name: 'InvalidPiece', + message: 'Aggregate is not a valid piece.', + // piece 1 was a bad + cause: [{ + name: 'InvalidPieceCID', + piece: pieces[1].link + }] + } // Create Ucanto service const service = mockService({ - deal: { - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.dealAdd, - handler: async ({ invocation, context }) => { + aggregate: { + accept: Server.provideAdvanced({ + capability: DealerCaps.aggregateAccept, + handler: async ({ invocation }) => { assert.strictEqual(invocation.issuer.did(), dealerService.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual(invCap.can, FilecoinCapabilities.dealAdd.can) + assert.strictEqual(invCap.can, DealerCaps.aggregateAccept.can) assert.equal(invCap.with, invocation.issuer.did()) assert.ok(invCap.nb) @@ -194,18 +186,15 @@ describe('dealer.add', () => { ) return { - error: dealAddResponse, + error: aggregateAcceptResponse, } }, }), - queue: () => { - throw new Error('not implemented') - }, }, }) - // invoke piece add from storefront - const res = await dealAdd( + // invoke aggregate accept from dealer + const res = await aggregateAccept( { issuer: dealerService, with: dealerService.did(), @@ -213,13 +202,11 @@ describe('dealer.add', () => { }, aggregate.link.link(), offer, - storefront, - label, { connection: getConnection(service).connection } ) assert.ok(res.out.error) - assert.deepEqual(res.out.error.name, OperationErrorName) + assert.equal(dagJSON.stringify(res.out.error), dagJSON.stringify(aggregateAcceptResponse)) // does not include effect fx in receipt assert.ok(!res.fx.join) }) @@ -227,9 +214,8 @@ describe('dealer.add', () => { async function getContext() { const aggregator = await Signer.generate() - const storefront = await Signer.generate() - return { aggregator, storefront } + return { aggregator } } /** diff --git a/packages/filecoin-client/test/helpers/mocks.js b/packages/filecoin-client/test/helpers/mocks.js index 5a71c785c..a0c652847 100644 --- a/packages/filecoin-client/test/helpers/mocks.js +++ b/packages/filecoin-client/test/helpers/mocks.js @@ -5,29 +5,30 @@ const notImplemented = () => { } /** - * @param {Partial< - * import('../../src/types').StorefrontService & - * import('../../src/types').AggregatorService & - * import('../../src/types').DealerService & - * import('../../src/types').ChainTrackerService - * >} impl + * @param {Partial<{ + * filecoin: Partial + * piece: Partial + * aggregate: Partial + * deal: Partial + * }>} impl */ export function mockService(impl) { return { filecoin: { - add: withCallCount(impl.filecoin?.add ?? notImplemented), - queue: withCallCount(impl.filecoin?.queue ?? notImplemented), + offer: withCallCount(impl.filecoin?.offer ?? notImplemented), + submit: withCallCount(impl.filecoin?.submit ?? notImplemented), + accept: withCallCount(impl.filecoin?.accept ?? notImplemented), + }, + piece: { + offer: withCallCount(impl.piece?.offer ?? notImplemented), + accept: withCallCount(impl.piece?.accept ?? notImplemented), }, aggregate: { - add: withCallCount(impl.aggregate?.add ?? notImplemented), - queue: withCallCount(impl.aggregate?.queue ?? notImplemented), + offer: withCallCount(impl.aggregate?.offer ?? notImplemented), + accept: withCallCount(impl.aggregate?.accept ?? notImplemented), }, deal: { - add: withCallCount(impl.deal?.add ?? notImplemented), - queue: withCallCount(impl.deal?.queue ?? notImplemented), - }, - 'chain-tracker': { - info: withCallCount(impl['chain-tracker']?.info ?? notImplemented), + info: withCallCount(impl.deal?.info ?? notImplemented), }, } } diff --git a/packages/filecoin-client/test/storefront.test.js b/packages/filecoin-client/test/storefront.test.js index eab3d8df9..4058cbdad 100644 --- a/packages/filecoin-client/test/storefront.test.js +++ b/packages/filecoin-client/test/storefront.test.js @@ -3,40 +3,33 @@ import * as Signer from '@ucanto/principal/ed25519' import * as Client from '@ucanto/client' import * as Server from '@ucanto/server' import * as CAR from '@ucanto/transport/car' -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import { filecoinQueue, filecoinAdd } from '../src/storefront.js' - +import * as StorefrontCaps from '@web3-storage/capabilities/filecoin/storefront' +import * as dagJSON from '@ipld/dag-json' +import { filecoinOffer, filecoinSubmit, filecoinAccept } from '../src/storefront.js' import { randomCargo } from './helpers/random.js' import { mockService } from './helpers/mocks.js' -import { OperationFailed, OperationErrorName } from './helpers/errors.js' import { serviceProvider as storefrontService } from './fixtures.js' -describe('filecoin/add', () => { - it('agent queues a filecoin piece for storefront to handle', async () => { +describe('storefront', () => { + it('agent offers a filecoin piece', async () => { const { agent } = await getContext() - - // Generate cargo to add const [cargo] = await randomCargo(1, 100) - /** @type {import('@web3-storage/capabilities/types').FilecoinAddSuccess} */ - const filecoinAddResponse = { + /** @type {import('@web3-storage/capabilities/types').FilecoinOfferSuccess} */ + const filecoinOfferResponse = { piece: cargo.link, } // Create Ucanto service const service = mockService({ filecoin: { - queue: Server.provideAdvanced({ - capability: FilecoinCapabilities.filecoinQueue, + offer: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinOffer, handler: async ({ invocation, context }) => { assert.strictEqual(invocation.issuer.did(), agent.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual( - invCap.can, - FilecoinCapabilities.filecoinQueue.can - ) + assert.strictEqual(invCap.can, StorefrontCaps.filecoinOffer.can) assert.equal(invCap.with, invocation.issuer.did()) assert.ok(invCap.nb) // piece link @@ -45,7 +38,16 @@ describe('filecoin/add', () => { assert.ok(invCap.nb.content.equals(cargo.content.link())) // Create effect for receipt with self signed queued operation - const fx = await FilecoinCapabilities.filecoinAdd + const submitfx = await StorefrontCaps.filecoinSubmit + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: invCap.nb, + }) + .delegate() + + const acceptfx = await StorefrontCaps.filecoinAccept .invoke({ issuer: context.id, audience: context.id, @@ -54,51 +56,105 @@ describe('filecoin/add', () => { }) .delegate() - return Server.ok(filecoinAddResponse).join(fx.link()) + return Server.ok(filecoinOfferResponse).fork(submitfx.link()).join(acceptfx.link()) }, }), - add: () => { - throw new Error('not implemented') - }, }, }) - const res = await filecoinQueue( + const res = await filecoinOffer( { issuer: agent, with: agent.did(), audience: storefrontService, }, - cargo.link.link(), - cargo.content.link(), + cargo.content, + cargo.link, { connection: getConnection(service).connection } ) assert.ok(res.out.ok) - assert.ok(res.out.ok.piece.equals(filecoinAddResponse.piece)) + assert.ok(res.out.ok.piece.equals(filecoinOfferResponse.piece)) // includes effect fx in receipt + assert.equal(res.fx.fork.length, 1) assert.ok(res.fx.join) }) - it('storefront self invokes add a filecoin piece to accept the piece queued', async () => { - // Generate cargo to add + it('storefront submits a filecoin piece', async () => { const [cargo] = await randomCargo(1, 100) - /** @type {import('@web3-storage/capabilities/types').FilecoinAddSuccess} */ - const filecoinAddResponse = { - piece: cargo.link, + /** @type {import('@web3-storage/capabilities/types').FilecoinSubmitSuccess} */ + const filecoinSubmitResponse = { + piece: cargo.link } // Create Ucanto service const service = mockService({ filecoin: { - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.filecoinAdd, + submit: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinSubmit, + handler: async ({ invocation, capability }) => { + assert.equal(invocation.issuer.did(), storefrontService.did()) + assert.equal(invocation.capabilities.length, 1) + assert.equal(capability.can, StorefrontCaps.filecoinSubmit.can) + assert.equal(capability.with, invocation.issuer.did()) + assert(capability.nb) + assert(capability.nb.piece.equals(cargo.link)) + assert(capability.nb.content.equals(cargo.content)) + return Server.ok(filecoinSubmitResponse) + }, + }), + }, + }) + + // self invoke filecoin/add from storefront + const res = await filecoinSubmit( + { + issuer: storefrontService, + with: storefrontService.did(), + audience: storefrontService, + }, + cargo.content, + cargo.link, + { connection: getConnection(service).connection } + ) + + assert.ok(res.out.ok) + assert(res.out.ok.piece.equals(filecoinSubmitResponse.piece)) + assert(!res.fx.join) // although IRL this would have a fx.join to piece/offer + }) + + it('storefront accepts a filecoin piece', async () => { + const [cargo] = await randomCargo(1, 100) + + /** @type {import('@web3-storage/capabilities/types').FilecoinAcceptSuccess} */ + const filecoinAcceptResponse = { + inclusion: { + subtree: { + path: [], + index: 0n, + }, + index: { + path: [], + index: 0n, + }, + }, + auxDataType: 0n, + auxDataSource: { + dealID: 1138n + } + } + + // Create Ucanto service + const service = mockService({ + filecoin: { + accept: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinAccept, handler: async ({ invocation, context }) => { assert.strictEqual(invocation.issuer.did(), storefrontService.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual(invCap.can, FilecoinCapabilities.filecoinAdd.can) + assert.strictEqual(invCap.can, StorefrontCaps.filecoinAccept.can) assert.equal(invCap.with, invocation.issuer.did()) assert.ok(invCap.nb) // piece link @@ -106,53 +162,50 @@ describe('filecoin/add', () => { // content assert.ok(invCap.nb.content.equals(cargo.content.link())) - return Server.ok(filecoinAddResponse) + return Server.ok(filecoinAcceptResponse) }, }), - queue: () => { - throw new Error('not implemented') - }, }, }) // self invoke filecoin/add from storefront - const res = await filecoinAdd( + const res = await filecoinAccept( { issuer: storefrontService, with: storefrontService.did(), audience: storefrontService, }, - cargo.link.link(), - cargo.content.link(), + cargo.content, + cargo.link, { connection: getConnection(service).connection } ) assert.ok(res.out.ok) - assert.ok(res.out.ok.piece.equals(filecoinAddResponse.piece)) + assert.deepEqual(res.out.ok, filecoinAcceptResponse) // does not include effect fx in receipt assert.ok(!res.fx.join) }) - it('storefront self invokes add a filecoin piece to reject the piece queued', async () => { - // Generate cargo to add + it('storefront rejects a filecoin piece', async () => { const [cargo] = await randomCargo(1, 100) - /** @type {import('@web3-storage/capabilities/types').FilecoinAddFailure} */ - const filecoinAddResponse = new OperationFailed( - 'failed to verify piece', - cargo.link - ) + /** @type {import('@web3-storage/capabilities/types').FilecoinAcceptFailure} */ + const filecoinAcceptResponse = { + name: 'InvalidContentPiece', + message: 'Piece is a bad one.', + content: cargo.link + } // Create Ucanto service const service = mockService({ filecoin: { - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.filecoinAdd, - handler: async ({ invocation, context }) => { + accept: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinAccept, + handler: async ({ invocation }) => { assert.strictEqual(invocation.issuer.did(), storefrontService.did()) assert.strictEqual(invocation.capabilities.length, 1) const invCap = invocation.capabilities[0] - assert.strictEqual(invCap.can, FilecoinCapabilities.filecoinAdd.can) + assert.strictEqual(invCap.can, StorefrontCaps.filecoinAccept.can) assert.equal(invCap.with, invocation.issuer.did()) assert.ok(invCap.nb) // piece link @@ -161,30 +214,27 @@ describe('filecoin/add', () => { assert.ok(invCap.nb.content.equals(cargo.content.link())) return { - error: filecoinAddResponse, + error: filecoinAcceptResponse, } }, }), - queue: () => { - throw new Error('not implemented') - }, }, }) // self invoke filecoin/add from storefront - const res = await filecoinAdd( + const res = await filecoinAccept( { issuer: storefrontService, with: storefrontService.did(), audience: storefrontService, }, - cargo.link.link(), - cargo.content.link(), + cargo.content, + cargo.link, { connection: getConnection(service).connection } ) assert.ok(res.out.error) - assert.deepEqual(res.out.error.name, OperationErrorName) + assert.equal(dagJSON.stringify(res.out.error), dagJSON.stringify(filecoinAcceptResponse)) // does not include effect fx in receipt assert.ok(!res.fx.join) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d68b89e59..655db93fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -310,6 +310,9 @@ importers: '@ipld/car': specifier: ^5.1.1 version: 5.1.1 + '@ipld/dag-json': + specifier: ^10.1.4 + version: 10.1.4 '@types/assert': specifier: ^1.5.6 version: 1.5.6 @@ -2600,11 +2603,11 @@ packages: cborg: 2.0.5 multiformats: 12.1.1 - /@ipld/dag-json@10.1.3: - resolution: {integrity: sha512-c0PJE+cDnsYfyuWMdTTwkzMvxjthEwWD+0u6ApQfXrs+GTcxeYh+Hefd0xE9L4fb2Trr1DGA1a/iia4PAS3Mpg==} + /@ipld/dag-json@10.1.4: + resolution: {integrity: sha512-Vgm739qPQ7P8cstna60oYx19tzJzep+Uy7yWi80dzIOygibfVaaRZ07M6qbHP+C9BJl81GNFaXy2Plr0y7poBA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - cborg: 2.0.3 + cborg: 4.0.3 multiformats: 12.1.1 /@ipld/dag-pb@4.0.5: @@ -2617,7 +2620,7 @@ packages: resolution: {integrity: sha512-EhuOrAfnudsVYIbzEIgi3itHAEo3WZNOt1VNPsYhxKBhOzDMeoTXh6/IHc7ZKBW1T2vDQHdgj4m1r64z6MssGA==} dependencies: '@ipld/dag-cbor': 9.0.4 - '@ipld/dag-json': 10.1.3 + '@ipld/dag-json': 10.1.4 multiformats: 11.0.2 /@ipld/unixfs@2.1.1: @@ -4509,14 +4512,14 @@ packages: resolution: {integrity: sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==} hasBin: true - /cborg@2.0.3: - resolution: {integrity: sha512-f1IbyqgRLQK4ruNM+V3WikfYfXQg/f/zC1oneOw1P7F/Dn2OJX6MaXIdei3JMpz361IjY7OENBKcE53nkJFVCQ==} - hasBin: true - /cborg@2.0.5: resolution: {integrity: sha512-xVW1rSIw1ZXbkwl2XhJ7o/jAv0vnVoQv/QlfQxV8a7V5PlA4UU/AcIiXqmpyybwNWy/GPQU1m/aBVNIWr7/T0w==} hasBin: true + /cborg@4.0.3: + resolution: {integrity: sha512-poLvpK30KT5KI8gzDx3J/IuVCbsLqMT2fEbOrOuX0H7Hyj8yg5LezeWhRh9aLa5Z6MfPC5sriW3HVJF328M8LQ==} + hasBin: true + /ccount@1.1.0: resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} dev: true @@ -5793,9 +5796,9 @@ packages: eslint-plugin-promise: ^6.0.0 dependencies: eslint: 8.46.0 - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0) - eslint-plugin-n: 15.7.0(eslint@8.46.0) - eslint-plugin-promise: 6.1.1(eslint@8.46.0) + eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.49.0) + eslint-plugin-n: 15.7.0(eslint@8.49.0) + eslint-plugin-promise: 6.1.1(eslint@8.49.0) dev: true /eslint-config-standard@17.1.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0): From c18abc8c55d65606cae52469dc2f810ffca9bbdf Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Mon, 23 Oct 2023 22:38:29 +0200 Subject: [PATCH 2/2] refactor!: filecoin api services events and tests (#974) Implements `filecoin-api` services, events and tests all over the place. BREAKING CHANGE: see latest specs https://github.com/web3-storage/specs/blob/cbdb706f18567900c5c24d7fb16ccbaf93d0d023/w3-filecoin.md --- packages/capabilities/src/filecoin/dealer.js | 2 +- packages/capabilities/src/filecoin/index.js | 4 +- packages/capabilities/src/filecoin/lib.js | 16 +- packages/capabilities/src/types.ts | 51 +- packages/filecoin-api/package.json | 16 +- packages/filecoin-api/src/aggregator.js | 118 - packages/filecoin-api/src/aggregator/api.js | 1 + packages/filecoin-api/src/aggregator/api.ts | 302 ++ .../src/aggregator/buffer-reducing.js | 248 ++ .../filecoin-api/src/aggregator/events.js | 358 ++ .../filecoin-api/src/aggregator/service.js | 151 + packages/filecoin-api/src/deal-tracker/api.js | 1 + packages/filecoin-api/src/deal-tracker/api.ts | 34 + .../filecoin-api/src/deal-tracker/service.js | 77 + packages/filecoin-api/src/dealer.js | 156 - packages/filecoin-api/src/dealer/api.js | 1 + packages/filecoin-api/src/dealer/api.ts | 116 + .../filecoin-api/src/dealer/deal-track.js | 0 packages/filecoin-api/src/dealer/events.js | 165 + packages/filecoin-api/src/dealer/service.js | 187 + packages/filecoin-api/src/errors.js | 52 +- packages/filecoin-api/src/lib.js | 6 +- packages/filecoin-api/src/storefront.js | 113 - packages/filecoin-api/src/storefront/api.js | 1 + packages/filecoin-api/src/storefront/api.ts | 158 + .../filecoin-api/src/storefront/events.js | 264 ++ .../filecoin-api/src/storefront/service.js | 290 ++ packages/filecoin-api/src/types.ts | 159 +- packages/filecoin-api/test/aggregator.spec.js | 236 +- packages/filecoin-api/test/context/mocks.js | 54 + packages/filecoin-api/test/context/queue.js | 17 + .../filecoin-api/test/context/receipts.js | 145 + packages/filecoin-api/test/context/service.js | 231 ++ .../test/context/store-implementations.js | 242 ++ packages/filecoin-api/test/context/store.js | 171 +- packages/filecoin-api/test/context/types.ts | 8 + .../filecoin-api/test/deal-tracker.spec.js | 51 + packages/filecoin-api/test/dealer.spec.js | 158 +- .../filecoin-api/test/events/aggregator.js | 1238 ++++++ packages/filecoin-api/test/events/dealer.js | 448 +++ .../filecoin-api/test/events/storefront.js | 496 +++ packages/filecoin-api/test/lib.js | 18 +- .../filecoin-api/test/services/aggregator.js | 462 ++- .../test/services/deal-tracker.js | 143 + packages/filecoin-api/test/services/dealer.js | 315 +- .../filecoin-api/test/services/storefront.js | 514 ++- packages/filecoin-api/test/storefront.spec.js | 177 +- packages/filecoin-api/test/types.js | 1 + packages/filecoin-api/test/types.ts | 50 + packages/filecoin-client/package.json | 2 +- packages/filecoin-client/src/dealer.js | 9 +- packages/filecoin-client/src/types.ts | 32 +- .../filecoin-client/test/aggregator.test.js | 25 +- .../filecoin-client/test/deal-tracker.test.js | 8 +- packages/filecoin-client/test/dealer.test.js | 54 +- .../filecoin-client/test/storefront.test.js | 58 +- .../test/handlers/admin/store/inspect.js | 5 +- .../test/handlers/admin/upload/inspect.js | 6 +- pnpm-lock.yaml | 3475 ++++++++--------- 59 files changed, 8949 insertions(+), 2947 deletions(-) delete mode 100644 packages/filecoin-api/src/aggregator.js create mode 100644 packages/filecoin-api/src/aggregator/api.js create mode 100644 packages/filecoin-api/src/aggregator/api.ts create mode 100644 packages/filecoin-api/src/aggregator/buffer-reducing.js create mode 100644 packages/filecoin-api/src/aggregator/events.js create mode 100644 packages/filecoin-api/src/aggregator/service.js create mode 100644 packages/filecoin-api/src/deal-tracker/api.js create mode 100644 packages/filecoin-api/src/deal-tracker/api.ts create mode 100644 packages/filecoin-api/src/deal-tracker/service.js delete mode 100644 packages/filecoin-api/src/dealer.js create mode 100644 packages/filecoin-api/src/dealer/api.js create mode 100644 packages/filecoin-api/src/dealer/api.ts create mode 100644 packages/filecoin-api/src/dealer/deal-track.js create mode 100644 packages/filecoin-api/src/dealer/events.js create mode 100644 packages/filecoin-api/src/dealer/service.js delete mode 100644 packages/filecoin-api/src/storefront.js create mode 100644 packages/filecoin-api/src/storefront/api.js create mode 100644 packages/filecoin-api/src/storefront/api.ts create mode 100644 packages/filecoin-api/src/storefront/events.js create mode 100644 packages/filecoin-api/src/storefront/service.js create mode 100644 packages/filecoin-api/test/context/mocks.js create mode 100644 packages/filecoin-api/test/context/receipts.js create mode 100644 packages/filecoin-api/test/context/service.js create mode 100644 packages/filecoin-api/test/context/store-implementations.js create mode 100644 packages/filecoin-api/test/context/types.ts create mode 100644 packages/filecoin-api/test/deal-tracker.spec.js create mode 100644 packages/filecoin-api/test/events/aggregator.js create mode 100644 packages/filecoin-api/test/events/dealer.js create mode 100644 packages/filecoin-api/test/events/storefront.js create mode 100644 packages/filecoin-api/test/services/deal-tracker.js create mode 100644 packages/filecoin-api/test/types.js create mode 100644 packages/filecoin-api/test/types.ts diff --git a/packages/capabilities/src/filecoin/dealer.js b/packages/capabilities/src/filecoin/dealer.js index 8b7b8c822..ca456fe65 100644 --- a/packages/capabilities/src/filecoin/dealer.js +++ b/packages/capabilities/src/filecoin/dealer.js @@ -32,7 +32,7 @@ export const aggregateOffer = capability({ * CID of the DAG-CBOR encoded block with offer details. * Service will queue given offer to be validated and handled. */ - pieces: Schema.link(), + pieces: Schema.link({ version: 1 }), }), derives: (claim, from) => { return ( diff --git a/packages/capabilities/src/filecoin/index.js b/packages/capabilities/src/filecoin/index.js index 94c6db376..d7675e0fd 100644 --- a/packages/capabilities/src/filecoin/index.js +++ b/packages/capabilities/src/filecoin/index.js @@ -1,6 +1,6 @@ /** * Filecoin Capabilities - * + * * These capabilities are the entrypoint to the filecoin pipeline and are * aliases for the filecoin storefront capabilities. * @@ -15,5 +15,5 @@ export { filecoinOffer as offer, filecoinSubmit as submit, - filecoinAccept as accept + filecoinAccept as accept, } from './storefront.js' diff --git a/packages/capabilities/src/filecoin/lib.js b/packages/capabilities/src/filecoin/lib.js index 9e9f299d3..366e5692d 100644 --- a/packages/capabilities/src/filecoin/lib.js +++ b/packages/capabilities/src/filecoin/lib.js @@ -9,10 +9,12 @@ const FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE = /** @type {const} */ (0x1011) */ const RAW_CODE = /** @type {const} */ (0x55) -export const PieceLink = /** @type {import('../types').PieceLinkSchema} */ (Schema.link({ - code: RAW_CODE, - version: 1, - multihash: { - code: FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE, - }, -})) +export const PieceLink = /** @type {import('../types').PieceLinkSchema} */ ( + Schema.link({ + code: RAW_CODE, + version: 1, + multihash: { + code: FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE, + }, + }) +) diff --git a/packages/capabilities/src/types.ts b/packages/capabilities/src/types.ts index 655dd6a2d..1b18f7d45 100644 --- a/packages/capabilities/src/types.ts +++ b/packages/capabilities/src/types.ts @@ -1,8 +1,19 @@ import type { TupleToUnion } from 'type-fest' import * as Ucanto from '@ucanto/interface' import type { Schema } from '@ucanto/core' -import { InferInvokedCapability, Unit, DID, DIDKey, Link } from '@ucanto/interface' -import { Phantom, PieceLink, ProofData, uint64 } from '@web3-storage/data-segment' +import { + InferInvokedCapability, + Unit, + DID, + DIDKey, + Link, +} from '@ucanto/interface' +import { + Phantom, + PieceLink, + ProofData, + uint64, +} from '@web3-storage/data-segment' import { space, info } from './space.js' import * as provider from './provider.js' import { top } from './top.js' @@ -161,14 +172,20 @@ export type Space = InferInvokedCapability export type SpaceInfo = InferInvokedCapability // filecoin +export interface DealMetadata { + dataType: uint64 + dataSource: SingletonMarketSource +} /** @see https://github.com/filecoin-project/go-data-segment/blob/e3257b64fa2c84e0df95df35de409cfed7a38438/datasegment/verifier.go#L8-L14 */ export interface DataAggregationProof { /** * Proof the piece is included in the aggregate. */ inclusion: InclusionProof - auxDataType: uint64 - auxDataSource: SingletonMarketSource + /** + * Filecoin deal metadata. + */ + aux: DealMetadata } /** @see https://github.com/filecoin-project/go-data-segment/blob/e3257b64fa2c84e0df95df35de409cfed7a38438/datasegment/inclusion.go#L30-L39 */ export interface InclusionProof { @@ -213,15 +230,25 @@ export interface FilecoinSubmitSuccess { export type FilecoinSubmitFailure = InvalidPieceCID | Ucanto.Failure -export type FilecoinAcceptSuccess = DataAggregationProof +export interface FilecoinAcceptSuccess extends DataAggregationProof { + aggregate: PieceLink + piece: PieceLink +} -export type FilecoinAcceptFailure = InvalidContentPiece | Ucanto.Failure +export type FilecoinAcceptFailure = + | InvalidContentPiece + | ProofNotFound + | Ucanto.Failure export interface InvalidContentPiece extends Ucanto.Failure { name: 'InvalidContentPiece' content: PieceLink } +export interface ProofNotFound extends Ucanto.Failure { + name: 'ProofNotFound' +} + // filecoin aggregator export interface PieceOfferSuccess { /** @@ -256,7 +283,9 @@ export interface AggregateOfferSuccess { } export type AggregateOfferFailure = Ucanto.Failure -export type AggregateAcceptSuccess = DataAggregationProof +export interface AggregateAcceptSuccess extends DealMetadata { + aggregate: PieceLink +} export type AggregateAcceptFailure = InvalidPiece | Ucanto.Failure export interface InvalidPiece extends Ucanto.Failure { @@ -269,7 +298,7 @@ export interface InvalidPiece extends Ucanto.Failure { } export interface InvalidPieceCID extends Ucanto.Failure { - name: 'InvalidPieceCID', + name: 'InvalidPieceCID' piece: PieceLink } @@ -283,7 +312,7 @@ export interface DealDetails { // TODO: start/end epoch? etc. } -export type FilecoinAddress = `f${string}` +export type FilecoinAddress = string export type DealInfoFailure = DealNotFound | Ucanto.Failure @@ -343,9 +372,7 @@ export type AggregateOffer = InferInvokedCapability< export type AggregateAccept = InferInvokedCapability< typeof DealerCaps.aggregateAccept > -export type DealInfo = InferInvokedCapability< - typeof DealTrackerCaps.dealInfo -> +export type DealInfo = InferInvokedCapability // Top export type Top = InferInvokedCapability diff --git a/packages/filecoin-api/package.json b/packages/filecoin-api/package.json index f9b66fc63..2594ea5d0 100644 --- a/packages/filecoin-api/package.json +++ b/packages/filecoin-api/package.json @@ -20,8 +20,8 @@ "dealer": [ "dist/src/dealer.d.ts" ], - "chain-tracker": [ - "dist/src/chain-tracker.d.ts" + "deal-tracker": [ + "dist/src/deal-tracker.d.ts" ], "errors": [ "dist/src/errors.d.ts" @@ -54,9 +54,9 @@ "types": "./dist/src/dealer.d.ts", "import": "./src/dealer.js" }, - "./chain-tracker": { - "types": "./dist/src/chain-tracker.d.ts", - "import": "./src/chain-tracker.js" + "./deal-tracker": { + "types": "./dist/src/deal-tracker.d.ts", + "import": "./src/deal-tracker.js" }, "./storefront": { "types": "./dist/src/storefront.d.ts", @@ -86,7 +86,7 @@ "@ucanto/server": "^8.0.0", "@ucanto/transport": "^8.0.0", "@web3-storage/capabilities": "workspace:^", - "@web3-storage/data-segment": "^3.0.1" + "@web3-storage/data-segment": "^3.2.0" }, "devDependencies": { "@ipld/car": "^5.1.1", @@ -108,7 +108,9 @@ "project": "./tsconfig.json" }, "rules": { - "unicorn/expiring-todo-comments": "off" + "unicorn/expiring-todo-comments": "off", + "unicorn/prefer-number-properties": "off", + "jsdoc/check-indentation": "off" }, "env": { "mocha": true diff --git a/packages/filecoin-api/src/aggregator.js b/packages/filecoin-api/src/aggregator.js deleted file mode 100644 index 5f6e99292..000000000 --- a/packages/filecoin-api/src/aggregator.js +++ /dev/null @@ -1,118 +0,0 @@ -import * as Server from '@ucanto/server' -import * as Client from '@ucanto/client' -import * as CAR from '@ucanto/transport/car' -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import * as API from './types.js' -import { QueueOperationFailed, StoreOperationFailed } from './errors.js' - -/** - * @param {API.Input} input - * @param {API.AggregatorServiceContext} context - * @returns {Promise | API.UcantoInterface.JoinBuilder>} - */ -export const add = async ({ capability }, context) => { - const { piece, storefront, group } = capability.nb - - // Store piece into the store. Store events MAY be used to propagate piece over - const put = await context.pieceStore.put({ - piece, - storefront, - group, - insertedAt: Date.now(), - }) - - if (put.error) { - return { - error: new StoreOperationFailed(put.error.message), - } - } - - return { - ok: { - piece, - }, - } -} - -/** - * @param {API.Input} input - * @param {API.AggregatorServiceContext} context - * @returns {Promise | API.UcantoInterface.JoinBuilder>} - */ -export const queue = async ({ capability }, context) => { - const { piece, group } = capability.nb - const storefront = capability.with - - const queued = await context.addQueue.add({ - piece, - storefront, - group, - insertedAt: Date.now(), - }) - if (queued.error) { - return { - error: new QueueOperationFailed(queued.error.message), - } - } - - // Create effect for receipt - const fx = await FilecoinCapabilities.aggregateAdd - .invoke({ - issuer: context.id, - audience: context.id, - with: context.id.did(), - nb: { - piece, - storefront, - group, - }, - }) - .delegate() - - return Server.ok({ - piece, - }).join(fx.link()) -} - -/** - * @param {API.AggregatorServiceContext} context - */ -export function createService(context) { - return { - aggregate: { - queue: Server.provideAdvanced({ - capability: FilecoinCapabilities.aggregateQueue, - handler: (input) => queue(input, context), - }), - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.aggregateAdd, - handler: (input) => add(input, context), - }), - }, - } -} - -/** - * @param {API.UcantoServerContext & API.AggregatorServiceContext} context - */ -export const createServer = (context) => - Server.create({ - id: context.id, - codec: context.codec || CAR.inbound, - service: createService(context), - catch: (error) => context.errorReporter.catch(error), - }) - -/** - * @param {object} options - * @param {API.UcantoInterface.Principal} options.id - * @param {API.UcantoInterface.Transport.Channel} options.channel - * @param {API.UcantoInterface.OutboundCodec} [options.codec] - */ -export const connect = ({ id, channel, codec = CAR.outbound }) => - Client.connect({ - id, - channel, - codec, - }) diff --git a/packages/filecoin-api/src/aggregator/api.js b/packages/filecoin-api/src/aggregator/api.js new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/filecoin-api/src/aggregator/api.js @@ -0,0 +1 @@ +export {} diff --git a/packages/filecoin-api/src/aggregator/api.ts b/packages/filecoin-api/src/aggregator/api.ts new file mode 100644 index 000000000..50335472b --- /dev/null +++ b/packages/filecoin-api/src/aggregator/api.ts @@ -0,0 +1,302 @@ +import type { Signer, Principal, Link } from '@ucanto/interface' +import { InclusionProof } from '@web3-storage/capabilities/types' +import { PieceLink } from '@web3-storage/data-segment' +import { + AggregatorService, + DealerService, +} from '@web3-storage/filecoin-client/types' +import { + Store, + UpdatableStore, + QueryableStore, + Queue, + ServiceConfig, +} from '../types.js' + +export interface ServiceContext { + /** + * Service signer + */ + id: Signer + /** + * Principal for dealer service + */ + dealerId: Principal + /** + * Stores pieces that have been offered to the aggregator. + */ + pieceStore: UpdatableStore + /** + * Queues pieces being buffered into an aggregate. + */ + pieceQueue: Queue + /** + * Queues pieces being buffered into an aggregate. + */ + bufferQueue: Queue + /** + * Store of CID => Buffer Record + */ + bufferStore: Store + /** + * Stores fully buffered aggregates. + */ + aggregateStore: Store + /** + * Queues pieces, their aggregate and their inclusion proofs. + */ + pieceAcceptQueue: Queue + /** + * Stores inclusion proofs for pieces included in an aggregate. + */ + inclusionStore: QueryableStore< + InclusionRecordKey, + InclusionRecord, + InclusionRecordQueryByGroup + > + /** + * Queues buffered aggregates to be offered to the Dealer. + */ + aggregateOfferQueue: Queue +} + +export interface PieceMessageContext + extends Pick {} + +export interface PieceAcceptMessageContext + extends Pick {} + +export interface AggregateOfferMessageContext + extends Pick {} + +export interface PieceInsertEventContext + extends Pick {} + +export interface InclusionInsertEventToUpdateState + extends Pick {} + +export interface InclusionInsertEventToIssuePieceAccept { + /** + * Aggregator connection to moves pieces into the pipeline. + */ + aggregatorService: ServiceConfig +} + +export interface AggregateInsertEventToPieceAcceptQueueContext + extends Pick { + /** + * Buffer configuration for aggregation. + */ + config: AggregateConfig +} + +export interface AggregateInsertEventToAggregateOfferContext + extends Pick { + /** + * Dealer connection to moves pieces into the pipeline. + */ + dealerService: ServiceConfig +} + +export interface BufferMessageContext + extends Pick< + ServiceContext, + 'bufferStore' | 'bufferQueue' | 'aggregateOfferQueue' + > { + /** + * Buffer configuration for aggregation. + */ + config: AggregateConfig +} + +export interface PieceRecord { + /** + * Piece CID for the content. + */ + piece: PieceLink + /** + * Grouping information for submitted piece. + */ + group: string + /** + * Status of the offered piece. + * - offered = acknowledged received for aggregation. + * - accepted = accepted into an aggregate and offered for inclusion in filecoin deal(s). + */ + status: 'offered' | 'accepted' + /** + * Insertion date ISO string. + */ + insertedAt: string + /** + * Update date ISO string. + */ + updatedAt: string +} + +export interface PieceRecordKey extends Pick {} + +export interface PieceMessage { + /** + * Piece CID for the content. + */ + piece: PieceLink + /** + * Grouping information for submitted piece. + */ + group: string +} + +export interface AggregateRecord { + /** + * `bagy...aggregate` Piece CID of an aggregate + */ + aggregate: PieceLink + /** + * `bafy...cbor` as CID of dag-cbor block with list of pieces in an aggregate. + */ + pieces: Link + /** + * Grouping information for submitted piece. + */ + group: string + /** + * Insertion date ISO string. + */ + insertedAt: string +} + +// TODO: probably group should also be key! +export interface AggregateRecordKey + extends Pick {} + +export interface InclusionRecord { + /** + * Piece CID for the content. + */ + piece: PieceLink + /** + * Piece CID of an aggregate. + */ + aggregate: PieceLink + /** + * Grouping information for submitted piece. + */ + group: string + /** + * Proof that the piece is included in the aggregate. + */ + inclusion: InclusionProof + /** + * Insertion date ISO string. + */ + insertedAt: string +} + +export interface InclusionRecordKey + extends Pick {} + +export interface InclusionRecordQueryByGroup + extends Pick {} + +export type BufferedPiece = { + /** + * Piece CID for the content. + */ + piece: PieceLink + /** + * Policies that this piece is under + */ + policy: PiecePolicy + /** + * Insertion date ISO string. + */ + insertedAt: string +} + +export interface Buffer { + /** + * `bagy...aggregate` Piece CID of an aggregate + */ + aggregate?: PieceLink + /** + * Pieces inside the buffer record. + */ + pieces: BufferedPiece[] + /** + * Grouping information for submitted buffer. + */ + group: string +} + +export interface BufferRecord { + /** + * Buffer with a set of Filecoin pieces pending aggregation. + */ + buffer: Buffer + /** + * `bafy...cbor` as CID of dag-cbor block with list of pieces in an aggregate. + */ + block: Link +} + +export interface BufferMessage { + /** + * `bagy...aggregate` Piece CID of an aggregate + */ + aggregate?: PieceLink + /** + * `bafy...cbor` as CID of dag-cbor block with Buffer + */ + pieces: Link + /** + * Grouping information for submitted buffer. + */ + group: string +} + +export interface PieceAcceptMessage { + /** + * Piece CID. + */ + piece: PieceLink + /** + * Piece CID of an aggregate + */ + aggregate: PieceLink + /** + * Grouping information for submitted piece. + */ + group: string + /** + * Proof that the piece is included in the aggregate. + */ + inclusion: InclusionProof +} + +export interface AggregateOfferMessage { + /** + * Piece CID of an aggregate. + */ + aggregate: PieceLink + /** + * List of pieces in an aggregate. + */ + pieces: Link + /** + * Grouping information for submitted piece. + */ + group: string +} + +export interface AggregateConfig { + maxAggregateSize: number + minAggregateSize: number + minUtilizationFactor: number +} + +// Enums +export type PiecePolicy = NORMAL | RETRY + +type NORMAL = 0 +type RETRY = 1 diff --git a/packages/filecoin-api/src/aggregator/buffer-reducing.js b/packages/filecoin-api/src/aggregator/buffer-reducing.js new file mode 100644 index 000000000..091928f29 --- /dev/null +++ b/packages/filecoin-api/src/aggregator/buffer-reducing.js @@ -0,0 +1,248 @@ +import { Aggregate, Piece, NODE_SIZE, Index } from '@web3-storage/data-segment' +import { CBOR } from '@ucanto/core' + +import { UnexpectedState } from '../errors.js' + +/** + * @typedef {import('@ucanto/interface').Link} Link + * @typedef {import('@web3-storage/data-segment').AggregateView} AggregateView + * + * @typedef {import('./api').BufferedPiece} BufferedPiece + * @typedef {import('./api').BufferRecord} BufferRecord + * @typedef {import('./api').BufferMessage} BufferMessage + * @typedef {import('./api').AggregateOfferMessage} AggregateOfferMessage + * @typedef {import('../types').StoreGetError} StoreGetError + * @typedef {{ bufferedPieces: BufferedPiece[], group: string }} GetBufferedPieces + * @typedef {import('../types.js').Result} GetBufferedPiecesResult + * + * @typedef {object} AggregateInfo + * @property {BufferedPiece[]} addedBufferedPieces + * @property {BufferedPiece[]} remainingBufferedPieces + * @property {AggregateView} aggregate + */ + +/** + * @param {object} props + * @param {AggregateInfo} props.aggregateInfo + * @param {import('../types').Store} props.bufferStore + * @param {import('../types').Queue} props.bufferQueue + * @param {import('../types').Queue} props.aggregateOfferQueue + * @param {string} props.group + */ +export async function handleBufferReducingWithAggregate({ + aggregateInfo, + bufferStore, + bufferQueue, + aggregateOfferQueue, + group, +}) { + // If aggregate has enough space + // store buffered pieces that are part of aggregate and queue aggregate + // store remaining pieces and queue them to be reduced + /** @type {import('./api.js').Buffer} */ + const aggregateReducedBuffer = { + aggregate: aggregateInfo.aggregate.link, + pieces: aggregateInfo.addedBufferedPieces, + group, + } + const aggregateBlock = await CBOR.write(aggregateReducedBuffer) + + // Store buffered pieces for aggregate + const bufferStoreAggregatePut = await bufferStore.put({ + buffer: aggregateReducedBuffer, + block: aggregateBlock.cid, + }) + if (bufferStoreAggregatePut.error) { + return bufferStoreAggregatePut + } + + // Propagate message for aggregate offer queue + const aggregateOfferQueueAdd = await aggregateOfferQueue.add({ + aggregate: aggregateInfo.aggregate.link, + pieces: aggregateBlock.cid, + group, + }) + if (aggregateOfferQueueAdd.error) { + return aggregateOfferQueueAdd + } + + // Store remaining buffered pieces to reduce if they exist + if (!aggregateInfo.remainingBufferedPieces.length) { + return { ok: {} } + } + + const remainingReducedBuffer = { + pieces: aggregateInfo.remainingBufferedPieces, + group: group, + } + const remainingBlock = await CBOR.write(remainingReducedBuffer) + + // Store remaining buffered pieces + const bufferStoreRemainingPut = await bufferStore.put({ + buffer: remainingReducedBuffer, + block: remainingBlock.cid, + }) + if (bufferStoreRemainingPut.error) { + return bufferStoreRemainingPut + } + + // Propagate message for buffer queue + const bufferQueueAdd = await bufferQueue.add({ + pieces: remainingBlock.cid, + group: group, + }) + if (bufferQueueAdd.error) { + return bufferQueueAdd + } + + return { ok: {}, error: undefined } +} + +/** + * Store given buffer into store and queue it to further reducing. + * + * @param {object} props + * @param {import('./api.js').Buffer} props.buffer + * @param {import('../types').Store} props.bufferStore + * @param {import('../types').Queue} props.bufferQueue + */ +export async function handleBufferReducingWithoutAggregate({ + buffer, + bufferStore, + bufferQueue, +}) { + const block = await CBOR.write(buffer) + + // Store block in buffer store + const bufferStorePut = await bufferStore.put({ + buffer, + block: block.cid, + }) + if (bufferStorePut.error) { + return bufferStorePut + } + + // Propagate message + const bufferQueueAdd = await bufferQueue.add({ + pieces: block.cid, + group: buffer.group, + }) + if (bufferQueueAdd.error) { + return bufferQueueAdd + } + + return { ok: {}, error: undefined } +} + +/** + * Attempt to build an aggregate with buffered pieces within ranges. + * + * @param {BufferedPiece[]} bufferedPieces + * @param {object} sizes + * @param {number} sizes.maxAggregateSize + * @param {number} sizes.minAggregateSize + * @param {number} sizes.minUtilizationFactor + */ +export function aggregatePieces(bufferedPieces, sizes) { + // Guarantee buffered pieces total size is bigger than the minimum utilization + const bufferUtilizationSize = bufferedPieces.reduce((total, p) => { + const piece = Piece.fromLink(p.piece) + total += piece.size + return total + }, 0n) + if ( + bufferUtilizationSize < + sizes.maxAggregateSize / sizes.minUtilizationFactor + ) { + return + } + + // Create builder with maximum size and try to fill it up + const builder = Aggregate.createBuilder({ + size: Piece.PaddedSize.from(sizes.maxAggregateSize), + }) + + // add pieces to an aggregate until there is no more space, or no more pieces + /** @type {BufferedPiece[]} */ + const addedBufferedPieces = [] + /** @type {BufferedPiece[]} */ + const remainingBufferedPieces = [] + + for (const bufferedPiece of bufferedPieces) { + const p = Piece.fromLink(bufferedPiece.piece) + if (builder.estimate(p).error) { + remainingBufferedPieces.push(bufferedPiece) + continue + } + builder.write(p) + addedBufferedPieces.push(bufferedPiece) + } + const totalUsedSpace = + builder.offset * BigInt(NODE_SIZE) + + BigInt(builder.limit) * BigInt(Index.EntrySize) + + // If not enough space return undefined + if (totalUsedSpace < BigInt(sizes.minAggregateSize)) { + return + } + + const aggregate = builder.build() + + return { + addedBufferedPieces, + remainingBufferedPieces, + aggregate, + } +} + +/** + * Get buffered pieces from queue buffer records. + * + * @param {Link[]} bufferPieces + * @param {import('../types').Store} bufferStore + * @returns {Promise} + */ +export async function getBufferedPieces(bufferPieces, bufferStore) { + if (!bufferPieces.length) { + return { + error: new UnexpectedState('received buffer pieces are empty'), + } + } + + const getBufferRes = await Promise.all( + bufferPieces.map((bufferPiece) => bufferStore.get(bufferPiece)) + ) + + // Concatenate pieces and sort them by policy and size + /** @type {BufferedPiece[]} */ + let bufferedPieces = [] + for (const b of getBufferRes) { + if (b.error) return b + // eslint-disable-next-line unicorn/prefer-spread + bufferedPieces = bufferedPieces.concat(b.ok.buffer.pieces || []) + } + + bufferedPieces.sort(sortPieces) + + return { + ok: { + bufferedPieces, + // extract group from one entry + // TODO: needs to change to support multi group buffering + // @ts-expect-error typescript does not understand with find that no error and group MUST exist + group: getBufferRes[0].ok.buffer.group, + }, + } +} + +/** + * Sort given buffered pieces by policy and then by size. + * + * @param {BufferedPiece} a + * @param {BufferedPiece} b + */ +export function sortPieces(a, b) { + return a.policy !== b.policy + ? a.policy - b.policy + : Piece.fromLink(a.piece).height - Piece.fromLink(b.piece).height +} diff --git a/packages/filecoin-api/src/aggregator/events.js b/packages/filecoin-api/src/aggregator/events.js new file mode 100644 index 000000000..1d62bbdea --- /dev/null +++ b/packages/filecoin-api/src/aggregator/events.js @@ -0,0 +1,358 @@ +import { Aggregator, Dealer } from '@web3-storage/filecoin-client' +import { Aggregate, Piece } from '@web3-storage/data-segment' +import { CBOR } from '@ucanto/core' + +import { + getBufferedPieces, + aggregatePieces, + handleBufferReducingWithoutAggregate, + handleBufferReducingWithAggregate, +} from './buffer-reducing.js' +import { + StoreOperationFailed, + QueueOperationFailed, + UnexpectedState, +} from '../errors.js' + +/** + * On piece queue messages, store piece. + * + * @param {import('./api').PieceMessageContext} context + * @param {import('./api').PieceMessage} message + */ +export const handlePieceMessage = async (context, message) => { + const { piece, group } = message + + // Store piece into the store. Store events MAY be used to propagate piece over + const putRes = await context.pieceStore.put({ + piece, + group, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + if (putRes.error) { + return { + error: new StoreOperationFailed(putRes.error.message), + } + } + + return { ok: {} } +} + +/** + * On Piece store insert batch, buffer pieces together to resume buffer processing. + * + * @param {import('./api').PieceInsertEventContext} context + * @param {import('./api').PieceRecord[]} records + */ +export const handlePiecesInsert = async (context, records) => { + // TODO: separate buffers per group after MVP + const { group } = records[0] + + /** @type {import('./api.js').Buffer} */ + const buffer = { + pieces: records.map((p) => ({ + piece: p.piece, + insertedAt: p.insertedAt, + // Set policy as insertion + policy: /** @type {import('./api.js').PiecePolicy} */ (0), + })), + group, + } + const block = await CBOR.write(buffer) + + // Store block in buffer store + const bufferStorePut = await context.bufferStore.put({ + buffer, + block: block.cid, + }) + if (bufferStorePut.error) { + return bufferStorePut + } + + // Propagate message + const bufferQueueAdd = await context.bufferQueue.add({ + pieces: block.cid, + group, + }) + if (bufferQueueAdd.error) { + return { + error: new QueueOperationFailed(bufferQueueAdd.error.message), + } + } + + return { ok: {} } +} + +/** + * On buffer queue messages, reduce received buffer records into a bigger buffer. + * - If new buffer does not have enough load to build an aggregate, it is stored + * and requeued for buffer reducing + * - If new buffer has enough load to build an aggregate, it is stored and queued + * into aggregateOfferQueue. Remaining of the new buffer (in case buffer bigger + * than maximum aggregate size) is re-queued into the buffer queue. + * + * @param {import('./api').BufferMessageContext} context + * @param {import('./api').BufferMessage[]} records + */ +export const handleBufferQueueMessage = async (context, records) => { + // Get reduced buffered pieces + const buffers = records.map((r) => r.pieces) + const { error: errorGetBufferedPieces, ok: okGetBufferedPieces } = + await getBufferedPieces(buffers, context.bufferStore) + if (errorGetBufferedPieces) { + return { error: errorGetBufferedPieces } + } + + const { bufferedPieces, group } = okGetBufferedPieces + + // Attempt to aggregate buffered pieces within the ranges. + // In case it is possible, included pieces and remaining pieces are returned + // so that they can be propagated to respective stores/queues. + const aggregateInfo = aggregatePieces(bufferedPieces, { + maxAggregateSize: context.config.maxAggregateSize, + minAggregateSize: context.config.minAggregateSize, + minUtilizationFactor: context.config.minUtilizationFactor, + }) + + // Store buffered pieces if not enough to do aggregate and re-queue them + if (!aggregateInfo) { + const { error: errorHandleBufferReducingWithoutAggregate } = + await handleBufferReducingWithoutAggregate({ + buffer: { + pieces: bufferedPieces, + group, + }, + bufferStore: context.bufferStore, + bufferQueue: context.bufferQueue, + }) + + if (errorHandleBufferReducingWithoutAggregate) { + return { error: errorHandleBufferReducingWithoutAggregate } + } + + // No pieces were aggregate + return { + ok: { + aggregatedPieces: 0, + }, + } + } + + // Store buffered pieces to do aggregate and re-queue remaining ones + const { error: errorHandleBufferReducingWithAggregate } = + await handleBufferReducingWithAggregate({ + aggregateInfo, + bufferStore: context.bufferStore, + bufferQueue: context.bufferQueue, + aggregateOfferQueue: context.aggregateOfferQueue, + group, + }) + + if (errorHandleBufferReducingWithAggregate) { + return { error: errorHandleBufferReducingWithAggregate } + } + + return { + ok: { + aggregatedPieces: aggregateInfo.addedBufferedPieces.length, + }, + } +} + +/** + * On aggregate offer queue message, store aggregate record in store. + * + * @param {import('./api').AggregateOfferMessageContext} context + * @param {import('./api').AggregateOfferMessage} message + */ +export const handleAggregateOfferMessage = async (context, message) => { + const { pieces, aggregate, group } = message + + // Store aggregate information into the store. Store events MAY be used to propagate aggregate over + const putRes = await context.aggregateStore.put({ + pieces, + aggregate, + group, + insertedAt: new Date().toISOString(), + }) + + // TODO: should we ignore error already there? + if (putRes.error) { + return putRes + } + + return { ok: {}, error: undefined } +} + +/** + * On Aggregate store insert, offer inserted aggregate for deal. + * + * @param {import('./api').AggregateInsertEventToPieceAcceptQueueContext} context + * @param {import('./api').AggregateRecord} record + */ +export const handleAggregateInsertToPieceAcceptQueue = async ( + context, + record +) => { + const bufferStoreRes = await context.bufferStore.get(record.pieces) + if (bufferStoreRes.error) { + return bufferStoreRes + } + + // Get pieces from buffer + const pieces = bufferStoreRes.ok.buffer.pieces.map((p) => + Piece.fromLink(p.piece) + ) + const aggregate = bufferStoreRes.ok.buffer.aggregate + + const aggregateBuilder = Aggregate.build({ + pieces, + size: Piece.PaddedSize.from(context.config.maxAggregateSize), + }) + + if (aggregate && !aggregateBuilder.link.equals(aggregate)) { + return { + error: new UnexpectedState( + `invalid aggregate computed for ${bufferStoreRes.ok.block.link}` + ), + } + } + + // TODO: Batch per a maximum to queue + for (const piece of pieces) { + const inclusionProof = aggregateBuilder.resolveProof(piece.link) + if (inclusionProof.error) { + return inclusionProof + } + const addMessage = await context.pieceAcceptQueue.add({ + piece: piece.link, + aggregate: aggregateBuilder.link, + group: bufferStoreRes.ok.buffer.group, + inclusion: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + }) + + if (addMessage.error) { + return addMessage + } + } + + return { + ok: {}, + } +} + +/** + * On piece accept queue message, store inclusion record in store. + * + * @param {import('./api').PieceAcceptMessageContext} context + * @param {import('./api').PieceAcceptMessage} message + */ +export const handlePieceAcceptMessage = async (context, message) => { + const { piece, aggregate, group, inclusion } = message + + // Store inclusion information into the store. Store events MAY be used to propagate inclusion over + const putRes = await context.inclusionStore.put({ + piece, + aggregate, + group, + inclusion, + insertedAt: new Date().toISOString(), + }) + + // TODO: should we ignore error already there? + if (putRes.error) { + return putRes + } + + return { ok: {}, error: undefined } +} + +/** + * On Inclusion store insert, piece table can be updated to reflect piece state. + * + * @param {import('./api').InclusionInsertEventToUpdateState} context + * @param {import('./api').InclusionRecord} record + */ +export const handleInclusionInsertToUpdateState = async (context, record) => { + const updateRes = await context.pieceStore.update( + { + piece: record.piece, + group: record.group, + }, + { + status: 'accepted', + updatedAt: new Date().toISOString(), + } + ) + if (updateRes.error) { + return updateRes + } + + return { ok: {}, error: undefined } +} + +/** + * @param {import('./api').InclusionInsertEventToIssuePieceAccept} context + * @param {import('./api').InclusionRecord} record + */ +export const handleInclusionInsertToIssuePieceAccept = async ( + context, + record +) => { + // invoke piece/accept to issue receipt + const pieceAcceptInv = await Aggregator.pieceAccept( + context.aggregatorService.invocationConfig, + record.piece, + record.group, + { connection: context.aggregatorService.connection } + ) + + if (pieceAcceptInv.out.error) { + return { + error: pieceAcceptInv.out.error, + } + } + + return { ok: {} } +} + +/** + * On Aggregate store insert, offer inserted aggregate for deal. + * + * @param {import('./api').AggregateInsertEventToAggregateOfferContext} context + * @param {import('./api').AggregateRecord} record + */ +export const handleAggregateInsertToAggregateOffer = async ( + context, + record +) => { + const bufferStoreRes = await context.bufferStore.get(record.pieces) + if (bufferStoreRes.error) { + return { + error: bufferStoreRes.error, + } + } + // Get pieces from buffer + const pieces = bufferStoreRes.ok.buffer.pieces.map((p) => p.piece) + + // invoke aggregate/offer + const aggregateOfferInv = await Dealer.aggregateOffer( + context.dealerService.invocationConfig, + record.aggregate, + pieces, + { connection: context.dealerService.connection } + ) + + if (aggregateOfferInv.out.error) { + return { + error: aggregateOfferInv.out.error, + } + } + + return { ok: {} } +} diff --git a/packages/filecoin-api/src/aggregator/service.js b/packages/filecoin-api/src/aggregator/service.js new file mode 100644 index 000000000..f8fa04986 --- /dev/null +++ b/packages/filecoin-api/src/aggregator/service.js @@ -0,0 +1,151 @@ +import * as Server from '@ucanto/server' +import * as Client from '@ucanto/client' +import * as CAR from '@ucanto/transport/car' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' +import * as DealerCaps from '@web3-storage/capabilities/filecoin/dealer' +// eslint-disable-next-line no-unused-vars +import * as API from '../types.js' +import { + QueueOperationFailed, + StoreOperationFailed, + UnexpectedState, + RecordNotFoundErrorName, +} from '../errors.js' + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise | API.UcantoInterface.JoinBuilder>} + */ +export const pieceOffer = async ({ capability }, context) => { + const { piece, group } = capability.nb + + // dedupe + const hasRes = await context.pieceStore.has({ piece, group }) + let exists = true + if (hasRes.error?.name === RecordNotFoundErrorName) { + exists = false + } else if (hasRes.error) { + return { + error: new StoreOperationFailed(hasRes.error.message), + } + } + + if (!exists) { + const addRes = await context.pieceQueue.add({ piece, group }) + if (addRes.error) { + return { + error: new QueueOperationFailed(addRes.error.message), + } + } + } + + const fx = await AggregatorCaps.pieceAccept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece, + group, + }, + expiration: Infinity, + }) + .delegate() + + /** @type {API.UcantoInterface.OkBuilder} */ + const result = Server.ok({ piece }) + return result.join(fx.link()) +} + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise | API.UcantoInterface.JoinBuilder>} + */ +export const pieceAccept = async ({ capability }, context) => { + const { piece, group } = capability.nb + + // Get inclusion proof for piece associated with this group + const getInclusionRes = await context.inclusionStore.query({ piece, group }) + if (getInclusionRes.error) { + return { + error: new StoreOperationFailed(getInclusionRes.error?.message), + } + } + if (!getInclusionRes.ok.length) { + return { + error: new UnexpectedState( + `no inclusion proof found for pair {${piece}, ${group}}` + ), + } + } + + const [{ aggregate, inclusion }] = getInclusionRes.ok + const getAggregateRes = await context.aggregateStore.get({ aggregate }) + if (getAggregateRes.error) { + return { + error: new StoreOperationFailed(getAggregateRes.error.message), + } + } + const { pieces } = getAggregateRes.ok + + // Create effect for receipt + const fx = await DealerCaps.aggregateOffer + .invoke({ + issuer: context.id, + audience: context.dealerId, + with: context.id.did(), + nb: { + aggregate, + pieces, + }, + }) + .delegate() + + /** @type {API.UcantoInterface.OkBuilder} */ + const result = Server.ok({ piece, aggregate, inclusion }) + return result.join(fx.link()) +} + +/** + * @param {import('./api').ServiceContext} context + */ +export function createService(context) { + return { + piece: { + offer: Server.provideAdvanced({ + capability: AggregatorCaps.pieceOffer, + handler: (input) => pieceOffer(input, context), + }), + accept: Server.provideAdvanced({ + capability: AggregatorCaps.pieceAccept, + handler: (input) => pieceAccept(input, context), + }), + }, + } +} + +/** + * @param {API.UcantoServerContext & import('./api').ServiceContext} context + */ +export const createServer = (context) => + Server.create({ + id: context.id, + codec: context.codec || CAR.inbound, + service: createService(context), + catch: (error) => context.errorReporter.catch(error), + }) + +/** + * @param {object} options + * @param {API.UcantoInterface.Principal} options.id + * @param {API.UcantoInterface.Transport.Channel} options.channel + * @param {API.UcantoInterface.OutboundCodec} [options.codec] + */ +export const connect = ({ id, channel, codec = CAR.outbound }) => + Client.connect({ + id, + channel, + codec, + }) diff --git a/packages/filecoin-api/src/deal-tracker/api.js b/packages/filecoin-api/src/deal-tracker/api.js new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/filecoin-api/src/deal-tracker/api.js @@ -0,0 +1 @@ +export {} diff --git a/packages/filecoin-api/src/deal-tracker/api.ts b/packages/filecoin-api/src/deal-tracker/api.ts new file mode 100644 index 000000000..7ee1e2e4c --- /dev/null +++ b/packages/filecoin-api/src/deal-tracker/api.ts @@ -0,0 +1,34 @@ +import type { Signer } from '@ucanto/interface' +import { PieceLink } from '@web3-storage/data-segment' +import { QueryableStore } from '../types.js' + +export interface ServiceContext { + /** + * Service signer + */ + id: Signer + + /** + * Stores information about deals for a given aggregate piece CID. + */ + dealStore: QueryableStore +} + +export interface DealRecord { + // PieceCid of an Aggregate `bagy...aggregate` + piece: PieceLink + // address of the Filecoin storage provider storing deal + provider: string + // deal identifier + dealId: number + // epoch of deal expiration + expirationEpoch: number + // source of the deal information + source: string + // Date when deal was added as ISO string + insertedAt: string +} + +export interface DealRecordKey extends Pick {} + +export interface DealRecordQueryByPiece extends Pick {} diff --git a/packages/filecoin-api/src/deal-tracker/service.js b/packages/filecoin-api/src/deal-tracker/service.js new file mode 100644 index 000000000..4507d2ad5 --- /dev/null +++ b/packages/filecoin-api/src/deal-tracker/service.js @@ -0,0 +1,77 @@ +import * as Server from '@ucanto/server' +import * as Client from '@ucanto/client' +import * as CAR from '@ucanto/transport/car' +import * as DealTrackerCaps from '@web3-storage/capabilities/filecoin/deal-tracker' + +// eslint-disable-next-line no-unused-vars +import * as API from '../types.js' +import { StoreOperationFailed } from '../errors.js' + +/** + * @typedef {import('@web3-storage/capabilities/types.js').DealDetails} DealDetails + */ + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise>} + */ +export const dealInfo = async ({ capability }, context) => { + const { piece } = capability.nb + + const storeGet = await context.dealStore.query({ piece }) + if (storeGet.error) { + return { + error: new StoreOperationFailed(storeGet.error.message), + } + } + + return { + ok: { + deals: storeGet.ok.reduce((acc, curr) => { + acc[`${curr.dealId}`] = { + provider: curr.provider, + } + + return acc + }, /** @type {Record} */ ({})), + }, + } +} + +/** + * @param {import('./api').ServiceContext} context + */ +export function createService(context) { + return { + deal: { + info: Server.provide(DealTrackerCaps.dealInfo, (input) => + dealInfo(input, context) + ), + }, + } +} + +/** + * @param {API.UcantoServerContext & import('./api').ServiceContext} context + */ +export const createServer = (context) => + Server.create({ + id: context.id, + codec: context.codec || CAR.inbound, + service: createService(context), + catch: (error) => context.errorReporter.catch(error), + }) + +/** + * @param {object} options + * @param {API.UcantoInterface.Principal} options.id + * @param {API.UcantoInterface.Transport.Channel} options.channel + * @param {API.UcantoInterface.OutboundCodec} [options.codec] + */ +export const connect = ({ id, channel, codec = CAR.outbound }) => + Client.connect({ + id, + channel, + codec, + }) diff --git a/packages/filecoin-api/src/dealer.js b/packages/filecoin-api/src/dealer.js deleted file mode 100644 index 999045ae3..000000000 --- a/packages/filecoin-api/src/dealer.js +++ /dev/null @@ -1,156 +0,0 @@ -import * as Server from '@ucanto/server' -import * as Client from '@ucanto/client' -import * as CAR from '@ucanto/transport/car' -import { CBOR } from '@ucanto/core' -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import * as API from './types.js' -import { - QueueOperationFailed, - StoreOperationFailed, - DecodeBlockOperationFailed, -} from './errors.js' - -/** - * @param {API.Input} input - * @param {API.DealerServiceContext} context - * @returns {Promise | API.UcantoInterface.JoinBuilder>} - */ -export const queue = async ({ capability, invocation }, context) => { - const { aggregate, pieces: offerCid, storefront, label } = capability.nb - const pieces = getOfferBlock(offerCid, invocation.iterateIPLDBlocks()) - - if (!pieces) { - return { - error: new DecodeBlockOperationFailed( - `missing offer block in invocation: ${offerCid.toString()}` - ), - } - } - - const queued = await context.addQueue.add({ - aggregate, - pieces, // add queue can opt to store offers in separate datastore - storefront, - label, - insertedAt: Date.now(), - }) - if (queued.error) { - return { - error: new QueueOperationFailed(queued.error.message), - } - } - - // Create effect for receipt - const fx = await FilecoinCapabilities.dealAdd - .invoke({ - issuer: context.id, - audience: context.id, - with: context.id.did(), - nb: { - aggregate, - pieces: offerCid, - storefront, - label, - }, - }) - .delegate() - - return Server.ok({ - aggregate, - }).join(fx.link()) -} - -/** - * @param {API.Input} input - * @param {API.DealerServiceContext} context - * @returns {Promise | API.UcantoInterface.JoinBuilder>} - */ -export const add = async ({ capability, invocation }, context) => { - const { aggregate, pieces: offerCid, storefront } = capability.nb - const pieces = getOfferBlock(offerCid, invocation.iterateIPLDBlocks()) - - if (!pieces) { - return { - error: new DecodeBlockOperationFailed( - `missing offer block in invocation: ${offerCid.toString()}` - ), - } - } - - // Get deal status from the store. - const get = await context.dealStore.get({ - aggregate, - storefront, - }) - if (get.error) { - return { - error: new StoreOperationFailed(get.error.message), - } - } - - return { - ok: { - aggregate, - }, - } -} - -/** - * @param {Server.API.Link} offerCid - * @param {IterableIterator>} blockIterator - */ -function getOfferBlock(offerCid, blockIterator) { - for (const block of blockIterator) { - if (block.cid.equals(offerCid)) { - const decoded = - /** @type {import('@web3-storage/data-segment').PieceLink[]} */ ( - CBOR.decode(block.bytes) - ) - return decoded - // TODO: Validate with schema - } - } -} - -/** - * @param {API.DealerServiceContext} context - */ -export function createService(context) { - return { - deal: { - queue: Server.provideAdvanced({ - capability: FilecoinCapabilities.dealQueue, - handler: (input) => queue(input, context), - }), - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.dealAdd, - handler: (input) => add(input, context), - }), - }, - } -} - -/** - * @param {API.UcantoServerContext & API.DealerServiceContext} context - */ -export const createServer = (context) => - Server.create({ - id: context.id, - codec: context.codec || CAR.inbound, - service: createService(context), - catch: (error) => context.errorReporter.catch(error), - }) - -/** - * @param {object} options - * @param {API.UcantoInterface.Principal} options.id - * @param {API.UcantoInterface.Transport.Channel} options.channel - * @param {API.UcantoInterface.OutboundCodec} [options.codec] - */ -export const connect = ({ id, channel, codec = CAR.outbound }) => - Client.connect({ - id, - channel, - codec, - }) diff --git a/packages/filecoin-api/src/dealer/api.js b/packages/filecoin-api/src/dealer/api.js new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/filecoin-api/src/dealer/api.js @@ -0,0 +1 @@ +export {} diff --git a/packages/filecoin-api/src/dealer/api.ts b/packages/filecoin-api/src/dealer/api.ts new file mode 100644 index 000000000..e0c6f1e74 --- /dev/null +++ b/packages/filecoin-api/src/dealer/api.ts @@ -0,0 +1,116 @@ +import type { Signer, Link } from '@ucanto/interface' +import { DealMetadata } from '@web3-storage/capabilities/types' +import { PieceLink } from '@web3-storage/data-segment' +import { + DealerService, + DealTrackerService, +} from '@web3-storage/filecoin-client/types' +import { + UpdatableStore, + UpdatableAndQueryableStore, + ServiceConfig, +} from '../types.js' + +export type OfferStore = UpdatableStore +export type AggregateStore = UpdatableAndQueryableStore< + AggregateRecordKey, + AggregateRecord, + Pick +> + +export interface ServiceContext { + id: Signer + /** + * Stores serialized broker specific offer document containing details of the + * aggregate and it's pieces. + */ + offerStore: OfferStore + /** + * Stores aggregates and their deal proofs. + */ + aggregateStore: AggregateStore +} + +export interface AggregateInsertEventContext + extends Pick {} + +export interface AggregateUpdatedStatusEventContext { + /** + * Dealer connection to offer aggregates for deals. + */ + dealerService: ServiceConfig +} + +export interface CronContext extends Pick { + /** + * Deal tracker connection to find out available deals for an aggregate. + */ + dealTrackerService: ServiceConfig +} + +export interface AggregateRecord { + /** + * Piece CID of an aggregate. + */ + aggregate: PieceLink + /** + * List of pieces in an aggregate. + */ + pieces: Link + /** + * Filecoin deal where aggregate is present. + */ + deal?: DealMetadata + /** + * Status of the offered aggregate piece. + * - offered = acknowledged received for inclusion in filecoin deals. + * - accepted = accepted and included a filecoin deal(s). + * - invalid = not valid for storage. + */ + status: 'offered' | 'accepted' | 'invalid' + /** + * Insertion date ISO string. + */ + insertedAt: string + /** + * Update date ISO string. + */ + updatedAt: string +} + +export interface AggregateRecordKey { + /** + * Piece CID of an aggregate. + */ + aggregate: PieceLink + /** + * Filecoin deal where aggregate is present. + */ + deal?: DealMetadata +} + +export interface OfferDocument { + /** + * Key of the offer document + */ + key: string + /** + * Value of the offer document + */ + value: OfferValue +} + +export interface OfferValue { + /** + * Issuer of the aggregate offer. + */ + issuer: `did:${string}:${string}` + /** + * Piece CID of an aggregate. + */ + aggregate: PieceLink + /** + * Pieces part of the aggregate + */ + pieces: PieceLink[] +} diff --git a/packages/filecoin-api/src/dealer/deal-track.js b/packages/filecoin-api/src/dealer/deal-track.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/filecoin-api/src/dealer/events.js b/packages/filecoin-api/src/dealer/events.js new file mode 100644 index 000000000..cff4f0de1 --- /dev/null +++ b/packages/filecoin-api/src/dealer/events.js @@ -0,0 +1,165 @@ +import { Dealer, DealTracker } from '@web3-storage/filecoin-client' + +import { StoreOperationFailed } from '../errors.js' + +/** + * @typedef {import('./api').AggregateRecord} AggregateRecord + * @typedef {import('./api').AggregateRecordKey} AggregateRecordKey + */ + +/** + * On aggregate insert event, update offer key with date to be retrievable by broker. + * + * @param {import('./api').AggregateInsertEventContext} context + * @param {AggregateRecord} record + */ +export const handleAggregateInsert = async (context, record) => { + const updateRes = await context.offerStore.update(record.pieces.toString(), { + key: `${new Date( + record.insertedAt + ).toISOString()} ${record.aggregate.toString()}.json`, + }) + if (updateRes.error) { + return { error: new StoreOperationFailed(updateRes.error.message) } + } + + return { ok: {} } +} + +/** + * On Aggregate update status event, issue aggregate accept receipt. + * + * @param {import('./api').AggregateUpdatedStatusEventContext} context + * @param {AggregateRecord} record + */ +export const handleAggregatUpdatedStatus = async (context, record) => { + const aggregateAcceptInv = await Dealer.aggregateAccept( + context.dealerService.invocationConfig, + record.aggregate, + record.pieces, + { connection: context.dealerService.connection } + ) + if (aggregateAcceptInv.out.error) { + return { + error: aggregateAcceptInv.out.error, + } + } + + return { ok: {} } +} + +/** + * On cron tick event, get aggregates without deals, and verify if there are updates on them. + * If there are deals for pending aggregates, their state can be updated. + * + * @param {import('./api').CronContext} context + */ +export const handleCronTick = async (context) => { + // Get offered deals pending approval/rejection + const offeredDeals = await context.aggregateStore.query({ + status: 'offered', + }) + if (offeredDeals.error) { + return { + error: offeredDeals.error, + } + } + + // Update approved deals from the ones resolved + const updatedResponses = await Promise.all( + offeredDeals.ok.map((deal) => + updateApprovedDeals({ + deal, + aggregateStore: context.aggregateStore, + dealTrackerServiceConnection: context.dealTrackerService.connection, + dealTrackerInvocationConfig: + context.dealTrackerService.invocationConfig, + }) + ) + ) + + // Fail if one or more update operations did not succeed. + // The successful ones are still valid, but we should keep track of errors for monitoring/alerting. + const updateErrorResponse = updatedResponses.find((r) => r.error) + if (updateErrorResponse) { + return { + error: updateErrorResponse.error, + } + } + + // Return successful update operation + // Include in response the ones that were Updated, and the ones still pending response. + const updatedDealsCount = updatedResponses.filter((r) => r.ok?.updated).length + return { + ok: { + updatedCount: updatedDealsCount, + pendingCount: updatedResponses.length - updatedDealsCount, + }, + } +} + +/** + * Find out if deal is on chain. When on chain, updates its status in store. + * + * @param {object} context + * @param {AggregateRecord} context.deal + * @param {import('../types.js').UpdatableAndQueryableStore>} context.aggregateStore + * @param {import('@ucanto/interface').ConnectionView} context.dealTrackerServiceConnection + * @param {import('@web3-storage/filecoin-client/types').InvocationConfig} context.dealTrackerInvocationConfig + */ +async function updateApprovedDeals({ + deal, + aggregateStore, + dealTrackerServiceConnection, + dealTrackerInvocationConfig, +}) { + // Query current state + const info = await DealTracker.dealInfo( + dealTrackerInvocationConfig, + deal.aggregate, + { connection: dealTrackerServiceConnection } + ) + + if (info.out.error) { + return { + error: info.out.error, + } + } + + // If there are no deals for it, we can skip + const deals = Object.keys(info.out.ok.deals || {}) + if (!deals.length) { + return { + ok: { + updated: false, + }, + } + } + + // Update status and deal information + const updateAggregate = await aggregateStore.update( + { aggregate: deal.aggregate }, + { + status: 'accepted', + updatedAt: new Date().toISOString(), + deal: { + dataType: 0n, + dataSource: { + dealID: BigInt(deals[0]), + }, + }, + } + ) + + if (updateAggregate.error) { + return { + error: updateAggregate.error, + } + } + + return { + ok: { + updated: true, + }, + } +} diff --git a/packages/filecoin-api/src/dealer/service.js b/packages/filecoin-api/src/dealer/service.js new file mode 100644 index 000000000..56e4debc1 --- /dev/null +++ b/packages/filecoin-api/src/dealer/service.js @@ -0,0 +1,187 @@ +import * as Server from '@ucanto/server' +import * as Client from '@ucanto/client' +import * as CAR from '@ucanto/transport/car' +import { CBOR } from '@ucanto/core' +import { sha256 } from 'multiformats/hashes/sha2' +import * as Block from 'multiformats/block' +import * as DealerCaps from '@web3-storage/capabilities/filecoin/dealer' +// eslint-disable-next-line no-unused-vars +import * as API from '../types.js' +import { + StoreOperationFailed, + DecodeBlockOperationFailed, + RecordNotFoundErrorName, +} from '../errors.js' + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise | API.UcantoInterface.JoinBuilder>} + */ +export const aggregateOffer = async ({ capability, invocation }, context) => { + const issuer = invocation.issuer.did() + const { aggregate, pieces } = capability.nb + + const hasRes = await context.aggregateStore.has({ aggregate }) + let exists = true + if (hasRes.error?.name === RecordNotFoundErrorName) { + exists = false + } else if (hasRes.error) { + return { + error: new StoreOperationFailed(hasRes.error.message), + } + } + + if (!exists) { + const piecesBlockRes = await findCBORBlock( + pieces, + invocation.iterateIPLDBlocks() + ) + if (piecesBlockRes.error) { + return piecesBlockRes + } + + // Write Spade formatted doc to offerStore before putting aggregate for tracking + const putOfferRes = await context.offerStore.put({ + key: piecesBlockRes.ok.cid.toString(), + value: { + issuer, + aggregate, + pieces: piecesBlockRes.ok.value, + }, + }) + if (putOfferRes.error) { + return { + error: new StoreOperationFailed(putOfferRes.error.message), + } + } + + // Put aggregate offered into store + const putRes = await context.aggregateStore.put({ + aggregate, + pieces, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + if (putRes.error) { + return { + error: new StoreOperationFailed(putRes.error.message), + } + } + } + + // Effect + const fx = await DealerCaps.aggregateAccept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + aggregate, + pieces, + }, + expiration: Infinity, + }) + .delegate() + + /** @type {API.UcantoInterface.OkBuilder} */ + const result = Server.ok({ aggregate }) + return result.join(fx.link()) +} + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise>} + */ +export const aggregateAccept = async ({ capability }, context) => { + const { aggregate } = capability.nb + + // Get deal status from the store. + const get = await context.aggregateStore.get({ + aggregate, + }) + if (get.error) { + return { + error: new StoreOperationFailed(get.error.message), + } + } + if (!get.ok.deal) { + return { + error: new StoreOperationFailed('no deal available'), + } + } + + return { + ok: { + aggregate, + dataSource: get.ok.deal.dataSource, + dataType: get.ok.deal.dataType, + }, + } +} + +/** + * @param {import('multiformats').Link} cid + * @param {IterableIterator>} blocks + * @returns {Promise>} + */ +const findCBORBlock = async (cid, blocks) => { + let bytes + for (const b of blocks) { + if (b.cid.equals(cid)) { + bytes = b.bytes + } + } + if (!bytes) { + return { + error: new DecodeBlockOperationFailed(`missing block: ${cid}`), + } + } + return { + ok: await Block.create({ cid, bytes, codec: CBOR, hasher: sha256 }), + } +} + +/** + * @param {import('./api').ServiceContext} context + */ +export function createService(context) { + return { + aggregate: { + offer: Server.provideAdvanced({ + capability: DealerCaps.aggregateOffer, + handler: (input) => aggregateOffer(input, context), + }), + accept: Server.provideAdvanced({ + capability: DealerCaps.aggregateAccept, + handler: (input) => aggregateAccept(input, context), + }), + }, + } +} + +/** + * @param {API.UcantoServerContext & import('./api').ServiceContext} context + */ +export const createServer = (context) => + Server.create({ + id: context.id, + codec: context.codec || CAR.inbound, + service: createService(context), + catch: (error) => context.errorReporter.catch(error), + }) + +/** + * @param {object} options + * @param {API.UcantoInterface.Principal} options.id + * @param {API.UcantoInterface.Transport.Channel} options.channel + * @param {API.UcantoInterface.OutboundCodec} [options.codec] + */ +export const connect = ({ id, channel, codec = CAR.outbound }) => + Client.connect({ + id, + channel, + codec, + }) diff --git a/packages/filecoin-api/src/errors.js b/packages/filecoin-api/src/errors.js index a1f627ee9..94f27277e 100644 --- a/packages/filecoin-api/src/errors.js +++ b/packages/filecoin-api/src/errors.js @@ -1,18 +1,24 @@ import * as Server from '@ucanto/server' +export const UnexpectedStateErrorName = /** @type {const} */ ('UnexpectedState') +export class UnexpectedState extends Server.Failure { + get reason() { + return this.message + } + + get name() { + return UnexpectedStateErrorName + } +} + export const QueueOperationErrorName = /** @type {const} */ ( 'QueueOperationFailed' ) export class QueueOperationFailed extends Server.Failure { - /** - * @param {string} message - */ - constructor(message) { - super(message) - } get reason() { return this.message } + get name() { return QueueOperationErrorName } @@ -22,47 +28,32 @@ export const StoreOperationErrorName = /** @type {const} */ ( 'StoreOperationFailed' ) export class StoreOperationFailed extends Server.Failure { - /** - * @param {string} message - */ - constructor(message) { - super(message) - } get reason() { return this.message } + get name() { return StoreOperationErrorName } } -export const StoreNotFoundErrorName = /** @type {const} */ ('StoreNotFound') -export class StoreNotFound extends Server.Failure { - /** - * @param {string} message - */ - constructor(message) { - super(message) - } +export const RecordNotFoundErrorName = /** @type {const} */ ('RecordNotFound') +export class RecordNotFound extends Server.Failure { get reason() { return this.message } + get name() { - return StoreNotFoundErrorName + return RecordNotFoundErrorName } } export const EncodeRecordErrorName = /** @type {const} */ ('EncodeRecordFailed') export class EncodeRecordFailed extends Server.Failure { - /** - * @param {string} message - */ - constructor(message) { - super(message) - } get reason() { return this.message } + get name() { return EncodeRecordErrorName } @@ -72,15 +63,10 @@ export const DecodeBlockOperationErrorName = /** @type {const} */ ( 'DecodeBlockOperationFailed' ) export class DecodeBlockOperationFailed extends Server.Failure { - /** - * @param {string} message - */ - constructor(message) { - super(message) - } get reason() { return this.message } + get name() { return DecodeBlockOperationErrorName } diff --git a/packages/filecoin-api/src/lib.js b/packages/filecoin-api/src/lib.js index 6401e13e8..ad09c8eae 100644 --- a/packages/filecoin-api/src/lib.js +++ b/packages/filecoin-api/src/lib.js @@ -1,3 +1,3 @@ -export * as Storefront from './storefront.js' -export * as Aggregator from './aggregator.js' -export * as Dealer from './dealer.js' +export * as Storefront from './storefront/service.js' +export * as Aggregator from './aggregator/service.js' +export * as Dealer from './dealer/service.js' diff --git a/packages/filecoin-api/src/storefront.js b/packages/filecoin-api/src/storefront.js deleted file mode 100644 index 05ea5149d..000000000 --- a/packages/filecoin-api/src/storefront.js +++ /dev/null @@ -1,113 +0,0 @@ -import * as Server from '@ucanto/server' -import * as Client from '@ucanto/client' -import * as CAR from '@ucanto/transport/car' -import { Filecoin as FilecoinCapabilities } from '@web3-storage/capabilities' - -import * as API from './types.js' -import { QueueOperationFailed, StoreOperationFailed } from './errors.js' - -/** - * @param {API.Input} input - * @param {API.StorefrontServiceContext} context - * @returns {Promise | API.UcantoInterface.JoinBuilder>} - */ -export const add = async ({ capability }, context) => { - const { piece, content } = capability.nb - - /// Store piece into the store. Store events MAY be used to propagate piece over - const put = await context.pieceStore.put({ - content, - piece, - insertedAt: Date.now(), - }) - if (put.error) { - return { - error: new StoreOperationFailed(put.error.message), - } - } - - return { - ok: { - piece, - }, - } -} - -/** - * @param {API.Input} input - * @param {API.StorefrontServiceContext} context - * @returns {Promise | API.UcantoInterface.JoinBuilder>} - */ -export const queue = async ({ capability }, context) => { - const { piece, content } = capability.nb - - const queued = await context.addQueue.add({ - piece, - content, - insertedAt: Date.now(), - }) - if (queued.error) { - return { - error: new QueueOperationFailed(queued.error.message), - } - } - - // Create effect for receipt - const fx = await FilecoinCapabilities.filecoinAdd - .invoke({ - issuer: context.id, - audience: context.id, - with: context.id.did(), - nb: { - piece, - content, - }, - }) - .delegate() - - return Server.ok({ - piece, - }).join(fx.link()) -} - -/** - * @param {API.StorefrontServiceContext} context - */ -export function createService(context) { - return { - filecoin: { - queue: Server.provideAdvanced({ - capability: FilecoinCapabilities.filecoinQueue, - handler: (input) => queue(input, context), - }), - add: Server.provideAdvanced({ - capability: FilecoinCapabilities.filecoinAdd, - handler: (input) => add(input, context), - }), - }, - } -} - -/** - * @param {API.UcantoServerContext & API.StorefrontServiceContext} context - */ -export const createServer = (context) => - Server.create({ - id: context.id, - codec: context.codec || CAR.inbound, - service: createService(context), - catch: (error) => context.errorReporter.catch(error), - }) - -/** - * @param {object} options - * @param {API.UcantoInterface.Principal} options.id - * @param {API.UcantoInterface.Transport.Channel} options.channel - * @param {API.UcantoInterface.OutboundCodec} [options.codec] - */ -export const connect = ({ id, channel, codec = CAR.outbound }) => - Client.connect({ - id, - channel, - codec, - }) diff --git a/packages/filecoin-api/src/storefront/api.js b/packages/filecoin-api/src/storefront/api.js new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/filecoin-api/src/storefront/api.js @@ -0,0 +1 @@ +export {} diff --git a/packages/filecoin-api/src/storefront/api.ts b/packages/filecoin-api/src/storefront/api.ts new file mode 100644 index 000000000..c9c2ecc6a --- /dev/null +++ b/packages/filecoin-api/src/storefront/api.ts @@ -0,0 +1,158 @@ +import type { + Signer, + Principal, + UnknownLink, + Receipt, + Invocation, + Failure, +} from '@ucanto/interface' +import { PieceLink } from '@web3-storage/data-segment' +import { + AggregatorService, + StorefrontService, +} from '@web3-storage/filecoin-client/types' +import { + Store, + UpdatableAndQueryableStore, + Queue, + ServiceConfig, +} from '../types.js' + +export interface ServiceOptions { + /** + * Implementer MAY handle submission without user request. + */ + skipFilecoinSubmitQueue?: boolean +} + +export interface ServiceContext { + /** + * Service signer + */ + id: Signer + /** + * Principal for aggregator service + */ + aggregatorId: Principal + /** + * Stores pieces that have been offered to the Storefront. + */ + pieceStore: UpdatableAndQueryableStore< + PieceRecordKey, + PieceRecord, + Pick + > + /** + * Queues pieces for verification. + */ + filecoinSubmitQueue: Queue + /** + * Queues pieces for offering to an Aggregator. + */ + pieceOfferQueue: Queue + /** + * Stores task invocations. + */ + taskStore: Store + /** + * Stores receipts for tasks. + */ + receiptStore: Store + /** + * Service options. + */ + options?: ServiceOptions +} + +export interface FilecoinSubmitMessageContext + extends Pick {} + +export interface PieceOfferMessageContext { + /** + * Aggregator connection to moves pieces into the pipeline. + */ + aggregatorService: ServiceConfig +} + +export interface StorefrontClientContext { + /** + * Storefront own connection to issue receipts. + */ + storefrontService: ServiceConfig +} + +export interface CronContext + extends Pick< + ServiceContext, + 'id' | 'pieceStore' | 'receiptStore' | 'taskStore' + > { + /** + * Principal for aggregator service + */ + aggregatorId: Signer +} + +export interface PieceRecord { + /** + * Piece CID for the content. + */ + piece: PieceLink + /** + * CAR shard CID. + */ + content: UnknownLink + /** + * Grouping information for submitted piece. + */ + group: string + /** + * Status of the offered filecoin piece. + * - submitted = verified valid piece and submitted to the aggregation pipeline + * - accepted = accepted and included in filecoin deal(s) + * - invalid = content/piece CID mismatch + */ + status: 'submitted' | 'accepted' | 'invalid' + /** + * Insertion date ISO string. + */ + insertedAt: string + /** + * Update date ISO string. + */ + updatedAt: string +} +export interface PieceRecordKey extends Pick {} + +export interface FilecoinSubmitMessage { + /** + * Piece CID for the content. + */ + piece: PieceLink + /** + * CAR shard CID. + */ + content: UnknownLink + /** + * Grouping information for submitted piece. + */ + group: string +} + +export interface PieceOfferMessage { + /** + * Piece CID. + */ + piece: PieceLink + /** + * CAR shard CID. + */ + content: UnknownLink + /** + * Grouping information for submitted piece. + */ + group: string +} + +export interface DataAggregationProofNotFound extends Failure { + name: 'DataAggregationProofNotFound' +} diff --git a/packages/filecoin-api/src/storefront/events.js b/packages/filecoin-api/src/storefront/events.js new file mode 100644 index 000000000..5d05777fe --- /dev/null +++ b/packages/filecoin-api/src/storefront/events.js @@ -0,0 +1,264 @@ +import { Storefront, Aggregator } from '@web3-storage/filecoin-client' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' + +// eslint-disable-next-line no-unused-vars +import * as API from '../types.js' +import { + RecordNotFoundErrorName, + StoreOperationFailed, + UnexpectedState, +} from '../errors.js' + +/** + * @typedef {import('./api').PieceRecord} PieceRecord + * @typedef {import('./api').PieceRecordKey} PieceRecordKey + * @typedef {import('../types.js').UpdatableAndQueryableStore>} PieceStore + */ + +/** + * On filecoin submit queue messages, validate piece for given content and store it in store. + * + * @param {import('./api').FilecoinSubmitMessageContext} context + * @param {import('./api').FilecoinSubmitMessage} message + */ +export const handleFilecoinSubmitMessage = async (context, message) => { + // dedupe concurrent writes + const hasRes = await context.pieceStore.has({ piece: message.piece }) + if (hasRes.error && hasRes.error.name !== RecordNotFoundErrorName) { + return { error: new StoreOperationFailed(hasRes.error.message) } + } + + // TODO: verify piece + + const putRes = await context.pieceStore.put({ + piece: message.piece, + content: message.content, + group: message.group, + status: 'submitted', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + if (putRes.error) { + return { error: new StoreOperationFailed(putRes.error.message) } + } + return { ok: {} } +} + +/** + * On piece offer queue message, offer piece for aggregation. + * + * @param {import('./api').PieceOfferMessageContext} context + * @param {import('./api').PieceOfferMessage} message + */ +export const handlePieceOfferMessage = async (context, message) => { + const pieceOfferInv = await Aggregator.pieceOffer( + context.aggregatorService.invocationConfig, + message.piece, + message.group, + { connection: context.aggregatorService.connection } + ) + if (pieceOfferInv.out.error) { + return { + error: pieceOfferInv.out.error, + } + } + + return { ok: {} } +} + +/** + * On piece inserted into store, invoke submit to queue piece to be offered for aggregate. + * + * @param {import('./api').StorefrontClientContext} context + * @param {PieceRecord} record + */ +export const handlePieceInsert = async (context, record) => { + const filecoinSubmitInv = await Storefront.filecoinSubmit( + context.storefrontService.invocationConfig, + record.content, + record.piece, + { connection: context.storefrontService.connection } + ) + + if (filecoinSubmitInv.out.error) { + return { + error: filecoinSubmitInv.out.error, + } + } + + return { ok: {} } +} + +/** + * @param {import('./api').StorefrontClientContext} context + * @param {PieceRecord} record + */ +export const handlePieceStatusUpdate = async (context, record) => { + // Validate expected status + if (record.status === 'submitted') { + return { + error: new UnexpectedState( + `record status for ${record.piece} is "${record.status}"` + ), + } + } + + const filecoinAcceptInv = await Storefront.filecoinAccept( + context.storefrontService.invocationConfig, + record.content, + record.piece, + { connection: context.storefrontService.connection } + ) + + if (filecoinAcceptInv.out.error) { + return { + error: filecoinAcceptInv.out.error, + } + } + + return { ok: {} } +} + +/** + * @param {import('./api').CronContext} context + */ +export const handleCronTick = async (context) => { + const submittedPieces = await context.pieceStore.query({ + status: 'submitted', + }) + if (submittedPieces.error) { + return { + error: submittedPieces.error, + } + } + // Update approved pieces from the ones resolved + const updatedResponses = await Promise.all( + submittedPieces.ok.map((pieceRecord) => + updatePiecesWithDeal({ + id: context.id, + aggregatorId: context.aggregatorId, + pieceRecord, + pieceStore: context.pieceStore, + taskStore: context.taskStore, + receiptStore: context.receiptStore, + }) + ) + ) + + // Fail if one or more update operations did not succeed. + // The successful ones are still valid, but we should keep track of errors for monitoring/alerting. + const updateErrorResponse = updatedResponses.find((r) => r.error) + if (updateErrorResponse) { + return { + error: updateErrorResponse.error, + } + } + + // Return successful update operation + // Include in response the ones that were Updated, and the ones still pending response. + const updatedPiecesCount = updatedResponses.filter( + (r) => r.ok?.updated + ).length + return { + ok: { + updatedCount: updatedPiecesCount, + pendingCount: updatedResponses.length - updatedPiecesCount, + }, + } +} + +/** + * Read receipt chain to determine if an aggregate was accepted for the piece. + * Update its status if there is an accepted aggregate. + * + * @param {object} context + * @param {import('@ucanto/interface').Signer} context.id + * @param {import('@ucanto/interface').Principal} context.aggregatorId + * @param {PieceRecord} context.pieceRecord + * @param {PieceStore} context.pieceStore + * @param {API.Store} context.taskStore + * @param {API.Store} context.receiptStore + */ +async function updatePiecesWithDeal({ + id, + aggregatorId, + pieceRecord, + pieceStore, + taskStore, + receiptStore, +}) { + let aggregateAcceptReceipt + + let task = /** @type {API.UcantoInterface.Link} */ ( + ( + await AggregatorCaps.pieceOffer + .invoke({ + issuer: id, + audience: aggregatorId, + with: id.did(), + nb: { + piece: pieceRecord.piece, + group: pieceRecord.group, + }, + expiration: Infinity, + }) + .delegate() + ).cid + ) + + while (true) { + const [taskRes, receiptRes] = await Promise.all([ + taskStore.get(task), + receiptStore.get(task), + ]) + // Should fail if errored and not with StoreNotFound Error + if ( + (taskRes.error && taskRes.error.name !== RecordNotFoundErrorName) || + (receiptRes.error && receiptRes.error.name !== RecordNotFoundErrorName) + ) { + return { + error: taskRes.error || receiptRes.error, + } + } + // Might not be available still, as piece is in progress to get into a deal + if (taskRes.error || receiptRes.error) { + // Store not found + break + } + + // Save very last receipt - aggregate/accept + const ability = taskRes.ok.capabilities[0]?.can + if (ability === 'aggregate/accept') { + aggregateAcceptReceipt = receiptRes.ok + } + if (!receiptRes.ok.fx.join) break + task = receiptRes.ok.fx.join.link() + } + + // If there is a receipt, status can be updated + if (aggregateAcceptReceipt) { + const updateRes = await pieceStore.update( + { + piece: pieceRecord.piece, + }, + { + status: !!aggregateAcceptReceipt.out.ok ? 'accepted' : 'invalid', + updatedAt: new Date().toISOString(), + } + ) + + if (updateRes.ok) { + return { + ok: { + updated: true, + }, + } + } + } + + return { + ok: { + updated: false, + }, + } +} diff --git a/packages/filecoin-api/src/storefront/service.js b/packages/filecoin-api/src/storefront/service.js new file mode 100644 index 000000000..39f4dfaad --- /dev/null +++ b/packages/filecoin-api/src/storefront/service.js @@ -0,0 +1,290 @@ +import * as Server from '@ucanto/server' +import * as Client from '@ucanto/client' +import * as CAR from '@ucanto/transport/car' +import * as StorefrontCaps from '@web3-storage/capabilities/filecoin/storefront' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' +// eslint-disable-next-line no-unused-vars +import * as API from '../types.js' +import { + QueueOperationFailed, + RecordNotFoundErrorName, + StoreOperationFailed, +} from '../errors.js' + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise | API.UcantoInterface.JoinBuilder>} + */ +export const filecoinOffer = async ({ capability }, context) => { + const { piece, content } = capability.nb + + // Queue offer for filecoin submission + if (!context.options?.skipFilecoinSubmitQueue) { + // dedupe + const hasRes = await context.pieceStore.has({ piece }) + let exists = true + if (hasRes.error?.name === RecordNotFoundErrorName) { + exists = false + } else if (hasRes.error) { + return { error: new StoreOperationFailed(hasRes.error.message) } + } + + const group = context.id.did() + if (!exists) { + // Queue the piece for validation etc. + const queueRes = await context.filecoinSubmitQueue.add({ + piece, + content, + group, + }) + if (queueRes.error) { + return { + error: new QueueOperationFailed(queueRes.error.message), + } + } + } + } + + // Create effect for receipt + const [submitfx, acceptfx] = await Promise.all([ + StorefrontCaps.filecoinSubmit + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece, + content, + }, + expiration: Infinity, + }) + .delegate(), + StorefrontCaps.filecoinAccept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece, + content, + }, + expiration: Infinity, + }) + .delegate(), + ]) + + // TODO: receipt timestamp? + /** @type {API.UcantoInterface.OkBuilder} */ + const result = Server.ok({ piece }) + return result.fork(submitfx.link()).join(acceptfx.link()) +} + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise | API.UcantoInterface.JoinBuilder>} + */ +export const filecoinSubmit = async ({ capability }, context) => { + const { piece, content } = capability.nb + const group = context.id.did() + + // Queue `piece/offer` invocation + const res = await context.pieceOfferQueue.add({ + piece, + content, + group, + }) + if (res.error) { + return { + error: new QueueOperationFailed(res.error.message), + } + } + + // Create effect for receipt + const fx = await AggregatorCaps.pieceOffer + .invoke({ + issuer: context.id, + audience: context.aggregatorId, + with: context.id.did(), + nb: { + piece, + group, + }, + expiration: Infinity, + }) + .delegate() + + // TODO: receipt timestamp? + /** @type {API.UcantoInterface.OkBuilder} */ + const result = Server.ok({ piece }) + return result.join(fx.link()) +} + +/** + * @param {API.Input} input + * @param {import('./api').ServiceContext} context + * @returns {Promise>} + */ +export const filecoinAccept = async ({ capability }, context) => { + const { piece } = capability.nb + const getPieceRes = await context.pieceStore.get({ piece }) + if (getPieceRes.error) { + return { error: new StoreOperationFailed(getPieceRes.error.message) } + } + + const { group } = getPieceRes.ok + const fx = await AggregatorCaps.pieceOffer + .invoke({ + issuer: context.id, + audience: context.aggregatorId, + with: context.id.did(), + nb: { + piece, + group, + }, + expiration: Infinity, + }) + .delegate() + + const dataAggregationProof = await findDataAggregationProof( + context, + fx.link() + ) + if (dataAggregationProof.error) { + return { error: new ProofNotFound(dataAggregationProof.error.message) } + } + + return { + ok: { + aux: dataAggregationProof.ok.aux, + inclusion: dataAggregationProof.ok.inclusion, + piece, + aggregate: dataAggregationProof.ok.aggregate, + }, + } +} + +/** + * Find a DataAggregationProof by following the receipt chain for a piece + * offered to the Filecoin pipeline. Starts on `piece/offer` and issued `piece/accept` receipt, + * making its way into `aggregate/offer` and `aggregate/accept` receipt for getting DealAggregationProof. + * + * @param {{ + * taskStore: API.Store + * receiptStore: API.Store + * }} stores + * @param {API.UcantoInterface.Link} task + * @returns {Promise>} + */ +async function findDataAggregationProof({ taskStore, receiptStore }, task) { + /** @type {API.InclusionProof|undefined} */ + let inclusion + /** @type {API.AggregateAcceptSuccess|undefined} */ + let aggregateAcceptReceipt + while (true) { + const [taskRes, receiptRes] = await Promise.all([ + taskStore.get(task), + receiptStore.get(task), + ]) + if (taskRes.error) { + return { + error: new StoreOperationFailed( + `failed to fetch task: ${task}: ${taskRes.error.message}` + ), + } + } + if (receiptRes.error) { + return { + error: new StoreOperationFailed( + `failed to fetch receipt for task: ${task}: ${receiptRes.error.message}` + ), + } + } + const ability = taskRes.ok.capabilities[0]?.can + if (ability === 'piece/accept' && receiptRes.ok.out.ok) { + inclusion = receiptRes.ok.out.ok.inclusion + } else if (ability === 'aggregate/accept' && receiptRes.ok.out.ok) { + aggregateAcceptReceipt = receiptRes.ok.out.ok + } + if (!receiptRes.ok.fx.join) break + task = receiptRes.ok.fx.join + } + if (!inclusion) { + return { + error: new ProofNotFound( + 'missing inclusion proof for piece in aggregate' + ), + } + } + if (!aggregateAcceptReceipt) { + return { error: new ProofNotFound('missing data aggregation proof') } + } + return { + ok: { + aux: { + dataSource: aggregateAcceptReceipt.dataSource, + dataType: aggregateAcceptReceipt.dataType, + }, + aggregate: aggregateAcceptReceipt.aggregate, + inclusion, + }, + } +} + +export const ProofNotFoundName = /** @type {const} */ ('ProofNotFound') +export class ProofNotFound extends Server.Failure { + get reason() { + return this.message + } + + get name() { + return ProofNotFoundName + } +} + +/** + * @param {import('./api').ServiceContext} context + */ +export function createService(context) { + return { + filecoin: { + offer: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinOffer, + handler: (input) => filecoinOffer(input, context), + }), + submit: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinSubmit, + handler: (input) => filecoinSubmit(input, context), + }), + accept: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinAccept, + handler: (input) => filecoinAccept(input, context), + }), + }, + } +} + +/** + * @param {API.UcantoServerContext & import('./api').ServiceContext} context + */ +export const createServer = (context) => + Server.create({ + id: context.id, + codec: context.codec || CAR.inbound, + service: createService(context), + catch: (error) => context.errorReporter.catch(error), + }) + +/** + * @param {object} options + * @param {API.UcantoInterface.Principal} options.id + * @param {API.UcantoInterface.Transport.Channel} options.channel + * @param {API.UcantoInterface.OutboundCodec} [options.codec] + */ +export const connect = ({ id, channel, codec = CAR.outbound }) => + Client.connect({ + id, + channel, + codec, + }) diff --git a/packages/filecoin-api/src/types.ts b/packages/filecoin-api/src/types.ts index 8dc64f3c9..834c0e218 100644 --- a/packages/filecoin-api/src/types.ts +++ b/packages/filecoin-api/src/types.ts @@ -6,90 +6,69 @@ import type { ParsedCapability, InferInvokedCapability, Match, + Unit, + Result, + ConnectionView, } from '@ucanto/interface' import type { ProviderInput } from '@ucanto/server' -import { PieceLink } from '@web3-storage/data-segment' -import { UnknownLink } from '@ucanto/interface' +import { InvocationConfig } from '@web3-storage/filecoin-client/types' export * as UcantoInterface from '@ucanto/interface' +export type { Result, Variant } from '@ucanto/interface' export * from '@web3-storage/filecoin-client/types' export * from '@web3-storage/capabilities/types' // Resources -export interface Queue { +export interface Queue { add: ( - record: Record, + message: Message, options?: QueueMessageOptions - ) => Promise> + ) => Promise> } -export interface Store { - put: (record: Record) => Promise> +export interface Store { /** - * Gets content data from the store. + * Puts a record in the store. */ - get: (key: any) => Promise> -} - -export interface QueueMessageOptions { - messageGroupId?: string -} - -// Services -export interface StorefrontServiceContext { - id: Signer - addQueue: Queue - pieceStore: Store -} - -export interface AggregatorServiceContext { - id: Signer - addQueue: Queue - pieceStore: Store -} - -export interface DealerServiceContext { - id: Signer - addQueue: Queue - dealStore: Store + put: (record: Rec) => Promise> + /** + * Gets a record from the store. + */ + get: (key: RecKey) => Promise> + /** + * Determine if a record already exists in the store for the given key. + */ + has: (key: RecKey) => Promise> } -// Service Types - -export interface StorefrontRecord { - piece: PieceLink - content: UnknownLink - insertedAt: number +export interface UpdatableStore extends Store { + /** + * Updates a record from the store. + */ + update: ( + key: RecKey, + record: Partial + ) => Promise> } -export interface AggregatorMessageRecord { - piece: PieceLink - storefront: string - group: string - insertedAt: number +export interface QueryableStore extends Store { + /** + * Queries for record matching a given criterium. + */ + query: (search: Query) => Promise> } -export interface AggregatorRecord { - piece: PieceLink - storefront: string - group: string - insertedAt: number -} +export interface UpdatableAndQueryableStore + extends UpdatableStore, + QueryableStore {} -export interface DealerMessageRecord { - aggregate: PieceLink - pieces: PieceLink[] - storefront: string - label?: string - insertedAt: number +export interface QueueMessageOptions { + messageGroupId?: string } -export interface DealerRecord { - aggregate: PieceLink - storefront: string - offer: string - stat: number - insertedAt: number +export interface ServiceConfig> { + connection: ConnectionView + invocationConfig: InvocationConfig } // Errors @@ -98,11 +77,8 @@ export type StorePutError = StoreOperationError | EncodeRecordFailed export type StoreGetError = | StoreOperationError | EncodeRecordFailed - | StoreNotFound -export type QueueAddError = - | QueueOperationError - | EncodeRecordFailed - | StorePutError + | RecordNotFound +export type QueueAddError = QueueOperationError | EncodeRecordFailed export interface QueueOperationError extends Error { name: 'QueueOperationFailed' @@ -112,8 +88,8 @@ export interface StoreOperationError extends Error { name: 'StoreOperationFailed' } -export interface StoreNotFound extends Error { - name: 'StoreNotFound' +export interface RecordNotFound extends Error { + name: 'RecordNotFound' } export interface EncodeRecordFailed extends Error { @@ -132,55 +108,10 @@ export interface ErrorReporter { catch: (error: HandlerExecutionError) => void } -export type Result = Variant<{ - ok: T - error: X -}> - -/** - * Utility type for defining a [keyed union] type as in IPLD Schema. In practice - * this just works around typescript limitation that requires discriminant field - * on all variants. - * - * ```ts - * type Result = - * | { ok: T } - * | { error: X } - * - * const demo = (result: Result) => { - * if (result.ok) { - * // ^^^^^^^^^ Property 'ok' does not exist on type '{ error: Error; }` - * } - * } - * ``` - * - * Using `Variant` type we can define same union type that works as expected: - * - * ```ts - * type Result = Variant<{ - * ok: T - * error: X - * }> - * - * const demo = (result: Result) => { - * if (result.ok) { - * result.ok.toUpperCase() - * } - * } - * ``` - * - * [keyed union]:https://ipld.io/docs/schemas/features/representation-strategies/#union-keyed-representation - */ -export type Variant> = { - [Key in keyof U]: { [K in Exclude]?: never } & { - [K in Key]: U[Key] - } -}[keyof U] - // test export interface UcantoServerContextTest extends UcantoServerContext { - queuedMessages: unknown[] + queuedMessages: Map } export type Test = ( diff --git a/packages/filecoin-api/test/aggregator.spec.js b/packages/filecoin-api/test/aggregator.spec.js index ac89d6b9a..00db93a44 100644 --- a/packages/filecoin-api/test/aggregator.spec.js +++ b/packages/filecoin-api/test/aggregator.spec.js @@ -1,55 +1,199 @@ /* eslint-disable no-only-tests/no-only-tests */ import * as assert from 'assert' -import * as Aggregator from './services/aggregator.js' import * as Signer from '@ucanto/principal/ed25519' -import { Store } from './context/store.js' +import * as AggregatorService from './services/aggregator.js' +import * as AggregatorEvents from './events/aggregator.js' + +import { getStoreImplementations } from './context/store-implementations.js' import { Queue } from './context/queue.js' +import { getMockService, getConnection } from './context/service.js' -describe('aggregate/*', () => { - for (const [name, test] of Object.entries(Aggregator.test)) { - const define = name.startsWith('only ') - ? it.only - : name.startsWith('skip ') - ? it.skip - : it - - define(name, async () => { - const signer = await Signer.generate() - const id = signer.withDID('did:web:test.aggregator.web3.storage') - - // resources - /** @type {unknown[]} */ - const queuedMessages = [] - const addQueue = new Queue({ - onMessage: (message) => queuedMessages.push(message), - }) - const pieceLookupFn = ( - /** @type {Iterable | ArrayLike} */ items, - /** @type {any} */ record - ) => { - return Array.from(items).find((i) => i.piece.equals(record.piece)) - } - const pieceStore = new Store(pieceLookupFn) - - await test( - { - equal: assert.strictEqual, - deepEqual: assert.deepStrictEqual, - ok: assert.ok, - }, - { - id, - errorReporter: { - catch(error) { - assert.fail(error) +describe('Aggregator', () => { + describe('piece/*', () => { + for (const [name, test] of Object.entries(AggregatorService.test)) { + const define = name.startsWith('only ') + ? it.only + : name.startsWith('skip ') + ? it.skip + : it + + define(name, async () => { + const aggregatorSigner = await Signer.generate() + const dealerSigner = await Signer.generate() + + // resources + /** @type {Map} */ + const queuedMessages = new Map() + const { + pieceQueue, + bufferQueue, + pieceAcceptQueue, + aggregateOfferQueue, + } = getQueues(queuedMessages) + const { + aggregator: { + pieceStore, + bufferStore, + aggregateStore, + inclusionStore, + }, + } = getStoreImplementations() + + await test( + { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + ok: assert.ok, + }, + { + id: aggregatorSigner, + dealerId: dealerSigner, + errorReporter: { + catch(error) { + assert.fail(error) + }, }, + pieceStore, + bufferStore, + aggregateStore, + inclusionStore, + pieceQueue, + bufferQueue, + pieceAcceptQueue, + aggregateOfferQueue, + queuedMessages, + } + ) + }) + } + }) + + describe('events', () => { + for (const [name, test] of Object.entries(AggregatorEvents.test)) { + const define = name.startsWith('only ') + ? it.only + : name.startsWith('skip ') + ? it.skip + : it + + define(name, async () => { + const aggregatorSigner = await Signer.generate() + const dealerSigner = await Signer.generate() + + const service = getMockService() + const aggregatorConnection = getConnection( + aggregatorSigner, + service + ).connection + const dealerConnection = getConnection(dealerSigner, service).connection + + // resources + /** @type {Map} */ + const queuedMessages = new Map() + const { bufferQueue, pieceAcceptQueue, aggregateOfferQueue } = + getQueues(queuedMessages) + const { + aggregator: { + pieceStore, + bufferStore, + aggregateStore, + inclusionStore, }, - addQueue, - pieceStore, - queuedMessages, - } - ) - }) - } + } = getStoreImplementations() + + await test( + { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + ok: assert.ok, + }, + { + id: aggregatorSigner, + pieceStore, + bufferStore, + aggregateStore, + inclusionStore, + bufferQueue, + pieceAcceptQueue, + aggregateOfferQueue, + dealerService: { + connection: dealerConnection, + invocationConfig: { + issuer: aggregatorSigner, + with: aggregatorSigner.did(), + audience: dealerSigner, + }, + }, + aggregatorService: { + connection: aggregatorConnection, + invocationConfig: { + issuer: aggregatorSigner, + with: aggregatorSigner.did(), + audience: aggregatorSigner, + }, + }, + queuedMessages, + service, + errorReporter: { + catch(error) { + assert.fail(error) + }, + }, + config: { + maxAggregateSize: 2 ** 35, + minAggregateSize: 2 ** 34, + minUtilizationFactor: 4, + }, + } + ) + }) + } + }) }) + +/** + * @param {Map} queuedMessages + */ +function getQueues(queuedMessages) { + queuedMessages.set('filecoinSubmitQueue', []) + queuedMessages.set('pieceQueue', []) + queuedMessages.set('bufferQueue', []) + queuedMessages.set('pieceAcceptQueue', []) + queuedMessages.set('aggregateOfferQueue', []) + const pieceQueue = new Queue({ + onMessage: (message) => { + const messages = queuedMessages.get('pieceQueue') || [] + messages.push(message) + queuedMessages.set('pieceQueue', messages) + }, + }) + const bufferQueue = new Queue({ + onMessage: (message) => { + const messages = queuedMessages.get('bufferQueue') || [] + messages.push(message) + queuedMessages.set('bufferQueue', messages) + }, + }) + const pieceAcceptQueue = new Queue({ + onMessage: (message) => { + const messages = queuedMessages.get('pieceAcceptQueue') || [] + messages.push(message) + queuedMessages.set('pieceAcceptQueue', messages) + }, + }) + const aggregateOfferQueue = new Queue({ + onMessage: (message) => { + const messages = queuedMessages.get('aggregateOfferQueue') || [] + messages.push(message) + queuedMessages.set('aggregateOfferQueue', messages) + }, + }) + + return { + pieceQueue, + bufferQueue, + pieceAcceptQueue, + aggregateOfferQueue, + } +} diff --git a/packages/filecoin-api/test/context/mocks.js b/packages/filecoin-api/test/context/mocks.js new file mode 100644 index 000000000..fc17443eb --- /dev/null +++ b/packages/filecoin-api/test/context/mocks.js @@ -0,0 +1,54 @@ +import * as Server from '@ucanto/server' + +const notImplemented = () => { + throw new Server.Failure('not implemented') +} + +/** + * @param {Partial<{ + * filecoin: Partial + * piece: Partial + * aggregate: Partial + * deal: Partial + * }>} impl + */ +export function mockService(impl) { + return { + filecoin: { + offer: withCallParams(impl.filecoin?.offer ?? notImplemented), + submit: withCallParams(impl.filecoin?.submit ?? notImplemented), + accept: withCallParams(impl.filecoin?.accept ?? notImplemented), + }, + piece: { + offer: withCallParams(impl.piece?.offer ?? notImplemented), + accept: withCallParams(impl.piece?.accept ?? notImplemented), + }, + aggregate: { + offer: withCallParams(impl.aggregate?.offer ?? notImplemented), + accept: withCallParams(impl.aggregate?.accept ?? notImplemented), + }, + deal: { + info: withCallParams(impl.deal?.info ?? notImplemented), + }, + } +} + +/** + * @template {Function} T + * @param {T} fn + */ +function withCallParams(fn) { + /** @param {T extends (...args: infer A) => any ? A : never} args */ + const annotatedParamsFn = (...args) => { + // @ts-expect-error not typed param + annotatedParamsFn._params.push(args[0].capabilities[0]) + annotatedParamsFn.called = true + annotatedParamsFn.callCount++ + return fn(...args) + } + /** @type {any[]} */ + annotatedParamsFn._params = [] + annotatedParamsFn.called = false + annotatedParamsFn.callCount = 0 + return annotatedParamsFn +} diff --git a/packages/filecoin-api/test/context/queue.js b/packages/filecoin-api/test/context/queue.js index 54139a87b..3d0605247 100644 --- a/packages/filecoin-api/test/context/queue.js +++ b/packages/filecoin-api/test/context/queue.js @@ -1,5 +1,7 @@ import * as API from '../../src/types.js' +import { QueueOperationFailed } from '../../src/errors.js' + /** * @template T * @implements {API.Queue} @@ -28,3 +30,18 @@ export class Queue { }) } } + +/** + * @template T + * @implements {API.Queue} + */ +export class FailingQueue { + /** + * @param {T} record + */ + async add(record) { + return Promise.resolve({ + error: new QueueOperationFailed('failed to add to queue'), + }) + } +} diff --git a/packages/filecoin-api/test/context/receipts.js b/packages/filecoin-api/test/context/receipts.js new file mode 100644 index 000000000..6f5f859ed --- /dev/null +++ b/packages/filecoin-api/test/context/receipts.js @@ -0,0 +1,145 @@ +import { Receipt } from '@ucanto/core' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' +import * as DealerCaps from '@web3-storage/capabilities/filecoin/dealer' + +import * as API from '../../src/types.js' + +/** + * @param {object} context + * @param {import('@ucanto/interface').Signer} context.storefront + * @param {import('@ucanto/interface').Signer} context.aggregator + * @param {import('@ucanto/interface').Signer} context.dealer + * @param {API.PieceLink} context.aggregate + * @param {string} context.group + * @param {API.PieceLink} context.piece + * @param {import('@ucanto/interface').Block} context.piecesBlock + * @param {API.InclusionProof} context.inclusionProof + * @param {API.AggregateAcceptSuccess} context.aggregateAcceptStatus + */ +export async function createInvocationsAndReceiptsForDealDataProofChain({ + storefront, + aggregator, + dealer, + aggregate, + group, + piece, + piecesBlock, + inclusionProof, + aggregateAcceptStatus, +}) { + const pieceOfferInvocation = await AggregatorCaps.pieceOffer + .invoke({ + issuer: storefront, + audience: aggregator, + with: storefront.did(), + nb: { + piece, + group, + }, + expiration: Infinity, + }) + .delegate() + const pieceAcceptInvocation = await AggregatorCaps.pieceAccept + .invoke({ + issuer: aggregator, + audience: aggregator, + with: aggregator.did(), + nb: { + piece, + group, + }, + expiration: Infinity, + }) + .delegate() + const aggregateOfferInvocation = await DealerCaps.aggregateOffer + .invoke({ + issuer: aggregator, + audience: dealer, + with: aggregator.did(), + nb: { + pieces: piecesBlock.cid, + aggregate, + }, + expiration: Infinity, + }) + .delegate() + aggregateOfferInvocation.attach(piecesBlock) + const aggregateAcceptInvocation = await DealerCaps.aggregateAccept + .invoke({ + issuer: aggregator, + audience: dealer, + with: aggregator.did(), + nb: { + pieces: piecesBlock.cid, + aggregate, + }, + expiration: Infinity, + }) + .delegate() + const pieceOfferReceipt = await Receipt.issue({ + issuer: aggregator, + ran: pieceOfferInvocation.cid, + result: { + ok: /** @type {API.PieceOfferSuccess} */ ({ + piece, + }), + }, + fx: { + join: pieceAcceptInvocation.cid, + fork: [], + }, + }) + + const pieceAcceptReceipt = await Receipt.issue({ + issuer: aggregator, + ran: pieceAcceptInvocation.cid, + result: { + ok: /** @type {API.PieceAcceptSuccess} */ ({ + piece, + aggregate, + inclusion: inclusionProof, + }), + }, + fx: { + join: aggregateOfferInvocation.cid, + fork: [], + }, + }) + + const aggregateOfferReceipt = await Receipt.issue({ + issuer: aggregator, + ran: aggregateOfferInvocation.cid, + result: { + ok: /** @type {API.AggregateOfferSuccess} */ ({ + aggregate, + }), + }, + fx: { + join: aggregateAcceptInvocation.cid, + fork: [], + }, + }) + + const aggregateAcceptReceipt = await Receipt.issue({ + issuer: dealer, + ran: aggregateAcceptInvocation.cid, + result: { + ok: /** @type {API.AggregateAcceptSuccess} */ (aggregateAcceptStatus), + }, + }) + + return { + invocations: { + pieceOfferInvocation, + pieceAcceptInvocation, + aggregateOfferInvocation, + aggregateAcceptInvocation, + }, + receipts: { + pieceOfferReceipt, + pieceAcceptReceipt, + aggregateOfferReceipt, + aggregateAcceptReceipt, + }, + } +} diff --git a/packages/filecoin-api/test/context/service.js b/packages/filecoin-api/test/context/service.js new file mode 100644 index 000000000..2a61e0860 --- /dev/null +++ b/packages/filecoin-api/test/context/service.js @@ -0,0 +1,231 @@ +import * as Client from '@ucanto/client' +import * as Server from '@ucanto/server' +import * as CAR from '@ucanto/transport/car' + +import * as StorefrontCaps from '@web3-storage/capabilities/filecoin/storefront' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' +import * as DealerCaps from '@web3-storage/capabilities/filecoin/dealer' +import * as DealTrackerCaps from '@web3-storage/capabilities/filecoin/deal-tracker' + +// eslint-disable-next-line no-unused-vars +import * as API from '../../src/types.js' + +import { mockService } from './mocks.js' + +export function getMockService() { + return mockService({ + aggregate: { + offer: Server.provideAdvanced({ + capability: DealerCaps.aggregateOffer, + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.pieces || !invCap.nb.aggregate) { + throw new Error() + } + const fx = await DealerCaps.aggregateAccept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + aggregate: invCap.nb.aggregate, + pieces: invCap.nb?.pieces, + }, + expiration: Infinity, + }) + .delegate() + + return Server.ok({ aggregate: invCap.nb.aggregate }).join(fx.link()) + }, + }), + accept: Server.provideAdvanced({ + capability: DealerCaps.aggregateAccept, + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.aggregate) { + throw new Error() + } + + return Server.ok({ + aggregate: invCap.nb.aggregate, + dataSource: { + dealID: 15151n, + }, + dataType: 1n, + }) + }, + }), + }, + piece: { + offer: Server.provideAdvanced({ + capability: AggregatorCaps.pieceOffer, + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.piece) { + throw new Error() + } + + // Create effect for receipt with self signed queued operation + const fx = await AggregatorCaps.pieceAccept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + ...invCap.nb, + }, + }) + .delegate() + + return Server.ok({ + piece: invCap.nb?.piece, + }).join(fx.link()) + }, + }), + accept: Server.provideAdvanced({ + capability: AggregatorCaps.pieceAccept, + // @ts-expect-error inclusion types + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.piece) { + throw new Error() + } + + // Create effect for receipt + const fx = await DealerCaps.aggregateOffer + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + aggregate: invCap.nb.piece, + pieces: invCap.nb.piece, + }, + }) + .delegate() + + return Server.ok({ + piece: invCap.nb.piece, + aggregate: invCap.nb.piece, + inclusion: { + subtree: + /** @type {import('@web3-storage/data-segment').ProofData} */ [ + 0n, + /** @type {import('@web3-storage/data-segment').MerkleTreePath} */ ([]), + ], + index: + /** @type {import('@web3-storage/data-segment').ProofData} */ [ + 0n, + /** @type {import('@web3-storage/data-segment').MerkleTreePath} */ ([]), + ], + }, + }).join(fx.link()) + }, + }), + }, + filecoin: { + submit: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinSubmit, + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.piece) { + throw new Error() + } + + // Create effect for receipt with self signed queued operation + const fx = await AggregatorCaps.pieceOffer + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + ...invCap.nb, + group: context.id.did(), + }, + }) + .delegate() + + return Server.ok({ + piece: invCap.nb?.piece, + }).join(fx.link()) + }, + }), + accept: Server.provideAdvanced({ + capability: StorefrontCaps.filecoinAccept, + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.piece) { + throw new Error() + } + + /** @type {API.UcantoInterface.OkBuilder} */ + const result = Server.ok({ + piece: invCap.nb.piece, + aggregate: invCap.nb.piece, + inclusion: { + subtree: + /** @type {import('@web3-storage/data-segment').ProofData} */ [ + 0n, + /** @type {import('@web3-storage/data-segment').MerkleTreePath} */ ([]), + ], + index: + /** @type {import('@web3-storage/data-segment').ProofData} */ [ + 0n, + /** @type {import('@web3-storage/data-segment').MerkleTreePath} */ ([]), + ], + }, + aux: { + dataType: 0n, + dataSource: { + dealID: 1138n, + }, + }, + }) + + return result + }, + }), + }, + deal: { + info: Server.provideAdvanced({ + capability: DealTrackerCaps.dealInfo, + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.piece) { + throw new Error() + } + + /** @type {API.UcantoInterface.OkBuilder} */ + const result = Server.ok({ + deals: { + 111: { + provider: 'f11111', + }, + }, + }) + + return result + }, + }), + }, + }) +} + +/** + * @param {any} service + * @param {any} id + */ +export function getConnection(id, service) { + const server = Server.create({ + id: id, + service, + codec: CAR.inbound, + }) + const connection = Client.connect({ + id: id, + codec: CAR.outbound, + channel: server, + }) + + return { connection } +} diff --git a/packages/filecoin-api/test/context/store-implementations.js b/packages/filecoin-api/test/context/store-implementations.js new file mode 100644 index 000000000..23c4ac000 --- /dev/null +++ b/packages/filecoin-api/test/context/store-implementations.js @@ -0,0 +1,242 @@ +import { UpdatableStore } from './store.js' + +/** + * @typedef {import('@ucanto/interface').Link} Link + * @typedef {import('../../src/storefront/api.js').PieceRecord} PieceRecord + * @typedef {import('../../src/storefront/api.js').PieceRecordKey} PieceRecordKey + * @typedef {import('../../src/aggregator/api.js').PieceRecord} AggregatorPieceRecord + * @typedef {import('../../src/aggregator/api.js').PieceRecordKey} AggregatorPieceRecordKey + * @typedef {import('../../src/aggregator/api.js').BufferRecord} BufferRecord + * @typedef {import('../../src/aggregator/api.js').AggregateRecord} AggregateRecord + * @typedef {import('../../src/aggregator/api.js').AggregateRecordKey} AggregateRecordKey + * @typedef {import('../../src/aggregator/api.js').InclusionRecord} InclusionRecord + * @typedef {import('../../src/aggregator/api.js').InclusionRecordKey} InclusionRecordKey + * @typedef {import('../../src/dealer/api.js').AggregateRecord} DealerAggregateRecord + * @typedef {import('../../src/dealer/api.js').AggregateRecordKey} DealerAggregateRecordKey + * @typedef {import('../../src/dealer/api.js').OfferDocument} OfferDocument + * @typedef {import('../../src/deal-tracker/api.js').DealRecord} DealRecord + * @typedef {import('../../src/deal-tracker/api.js').DealRecordKey} DealRecordKey + */ +export const getStoreImplementations = ( + StoreImplementation = UpdatableStore +) => ({ + storefront: { + pieceStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {PieceRecordKey} */ record + ) => { + return Array.from(items).find((i) => i?.piece.equals(record.piece)) + }, + queryFn: ( + /** @type {Set} */ items, + /** @type {Partial} */ search + ) => { + const filteredItems = Array.from(items).filter((i) => { + if (i.status === search.status) { + return true + } + return true + }) + return filteredItems + }, + updateFn: ( + /** @type {Set} */ items, + /** @type {PieceRecordKey} */ key, + /** @type {Partial} */ item + ) => { + const itemToUpdate = Array.from(items).find((i) => + i?.piece.equals(key.piece) + ) + if (!itemToUpdate) { + throw new Error('not found') + } + const updatedItem = { + ...itemToUpdate, + ...item, + } + items.delete(itemToUpdate) + items.add(updatedItem) + return updatedItem + }, + }), + taskStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {import('@ucanto/interface').UnknownLink} */ record + ) => { + return Array.from(items).find((i) => i.cid.equals(record)) + }, + }), + receiptStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {import('@ucanto/interface').UnknownLink} */ record + ) => { + return Array.from(items).find((i) => i.ran.link().equals(record)) + }, + }), + }, + aggregator: { + pieceStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {AggregatorPieceRecordKey} */ record + ) => { + return Array.from(items).find((i) => i?.piece.equals(record.piece)) + }, + updateFn: ( + /** @type {Set} */ items, + /** @type {AggregatorPieceRecordKey} */ key, + /** @type {Partial} */ item + ) => { + const itemToUpdate = Array.from(items).find( + (i) => i?.piece.equals(key.piece) && i.group === key.group + ) + if (!itemToUpdate) { + throw new Error('not found') + } + const updatedItem = { + ...itemToUpdate, + ...item, + } + items.delete(itemToUpdate) + items.add(updatedItem) + return updatedItem + }, + }), + bufferStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {Link} */ record + ) => { + // Return first item + return Array.from(items).find((i) => i.block.equals(record)) + }, + }), + aggregateStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {AggregateRecordKey} */ record + ) => { + return Array.from(items).find((i) => + i?.aggregate.equals(record.aggregate) + ) + }, + }), + inclusionStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {InclusionRecordKey} */ record + ) => { + return Array.from(items).find( + (i) => + i?.aggregate.equals(record.aggregate) && + i?.piece.equals(record.piece) + ) + }, + queryFn: ( + /** @type {Set} */ items, + /** @type {Partial} */ search + ) => { + const filteredItems = Array.from(items).filter((i) => { + if (search.piece && !i.piece.equals(search.piece)) { + return false + } else if ( + search.aggregate && + !i.aggregate.equals(search.aggregate) + ) { + return false + } else if (search.group && i.group !== search.group) { + return false + } + return true + }) + return filteredItems + }, + }), + }, + dealer: { + aggregateStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {DealerAggregateRecordKey} */ record + ) => { + return Array.from(items).find((i) => + i?.aggregate.equals(record.aggregate) + ) + }, + queryFn: ( + /** @type {Set} */ items, + /** @type {Partial} */ search + ) => { + return Array.from(items).filter((i) => i.status === search.status) + }, + updateFn: ( + /** @type {Set} */ items, + /** @type {DealerAggregateRecordKey} */ key, + /** @type {Partial} */ item + ) => { + const itemToUpdate = Array.from(items).find( + (i) => i.aggregate.equals(key.aggregate) && i.deal === key.deal + ) + if (!itemToUpdate) { + throw new Error('not found') + } + const updatedItem = { + ...itemToUpdate, + ...item, + } + items.delete(itemToUpdate) + items.add(updatedItem) + return updatedItem + }, + }), + offerStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {string} */ record + ) => { + return Array.from(items).find((i) => i.key === record) + }, + updateFn: ( + /** @type {Set} */ items, + /** @type {string} */ key, + /** @type {Partial} */ item + ) => { + const lastItem = Array.from(items).pop() + if (!lastItem) { + throw new Error('not found') + } + + const nItem = { + ...lastItem, + ...item, + } + + items.delete(lastItem) + items.add(nItem) + + return nItem + }, + }), + }, + dealTracker: { + dealStore: new StoreImplementation({ + getFn: ( + /** @type {Set} */ items, + /** @type {DealRecordKey} */ record + ) => { + return Array.from(items).find( + (i) => i?.piece.equals(record.piece) && i.dealId === record.dealId + ) + }, + queryFn: ( + /** @type {Set} */ items, + /** @type {Partial} */ search + ) => { + return Array.from(items).filter((i) => i.piece.equals(search.piece)) + }, + }), + }, +}) diff --git a/packages/filecoin-api/test/context/store.js b/packages/filecoin-api/test/context/store.js index 795ca050b..9bfc3999d 100644 --- a/packages/filecoin-api/test/context/store.js +++ b/packages/filecoin-api/test/context/store.js @@ -1,22 +1,31 @@ import * as API from '../../src/types.js' +import { RecordNotFound, StoreOperationFailed } from '../../src/errors.js' /** - * @template T - * @implements {API.Store} + * @typedef {import('../../src/types.js').StorePutError} StorePutError + * @typedef {import('../../src/types.js').StoreGetError} StoreGetError + */ + +/** + * @template K + * @template V + * @implements {API.Store} */ export class Store { /** - * @param {(items: Set, item: any) => T} lookupFn + * @param {import('./types.js').StoreOptions} options */ - constructor(lookupFn) { - /** @type {Set} */ + constructor(options) { + /** @type {Set} */ this.items = new Set() - this.lookupFn = lookupFn + this.getFn = options.getFn + this.queryFn = options.queryFn } /** - * @param {T} record + * @param {V} record + * @returns {Promise>} */ async put(record) { this.items.add(record) @@ -28,12 +37,154 @@ export class Store { /** * - * @param {any} item - * @returns boolean + * @param {K} item + * @returns {Promise>} */ async get(item) { + if (!this.getFn) { + throw new Error('get not supported') + } + const t = this.getFn(this.items, item) + if (!t) { + return { + error: new RecordNotFound(), + } + } + return { + ok: t, + } + } + + /** + * @param {K} item + * @returns {Promise>} + */ + async has(item) { + if (!this.getFn) { + throw new Error('has not supported') + } + const t = this.getFn(this.items, item) + if (!t) { + return { + error: new RecordNotFound(), + } + } + return { + ok: Boolean(t), + } + } + + /** + * @param {Partial} search + * @returns {Promise>} + */ + async query(search) { + if (!this.queryFn) { + throw new Error('query not supported') + } + const t = this.queryFn(this.items, search) + if (!t) { + return { + error: new RecordNotFound(), + } + } + return { + ok: t, + } + } +} + +/** + * @template K + * @template V + * @implements {API.UpdatableStore} + * @extends {Store} + */ +export class UpdatableStore extends Store { + /** + * @param {import('./types.js').UpdatableStoreOptions} options + */ + constructor(options) { + super(options) + + this.updateFn = options.updateFn + } + + /** + * @param {K} key + * @param {Partial} item + * @returns {Promise>} + */ + async update(key, item) { + if (!this.updateFn) { + throw new Error('query not supported') + } + + const t = this.updateFn(this.items, key, item) + if (!t) { + return { + error: new RecordNotFound(), + } + } + return { + ok: t, + } + } +} + +/** + * @template K + * @template V + * @extends {UpdatableStore} + */ +export class FailingStore extends UpdatableStore { + /** + * @param {V} record + */ + async put(record) { + return { + error: new StoreOperationFailed('failed to put to store'), + } + } + + /** + * @param {K} item + * @returns {Promise>} + */ + async get(item) { + return { + error: new StoreOperationFailed('failed to get from store'), + } + } + + /** + * @param {K} item + * @returns {Promise>} + */ + async has(item) { + return { + error: new StoreOperationFailed('failed to check store'), + } + } + + /** + * @param {Partial} search + * @returns {Promise>} + */ + async query(search) { + return { + error: new StoreOperationFailed('failed to query store'), + } + } + + /** + * @param {K} key + * @param {Partial} item + * @returns {Promise>} + */ + async update(key, item) { return { - ok: this.lookupFn(this.items, item), + error: new StoreOperationFailed('failed to update store'), } } } diff --git a/packages/filecoin-api/test/context/types.ts b/packages/filecoin-api/test/context/types.ts new file mode 100644 index 000000000..1c183c3a6 --- /dev/null +++ b/packages/filecoin-api/test/context/types.ts @@ -0,0 +1,8 @@ +export interface StoreOptions { + getFn?: (items: Set, item: K) => V | undefined + queryFn?: (items: Set, item: Partial) => V[] +} + +export interface UpdatableStoreOptions extends StoreOptions { + updateFn?: (items: Set, key: K, item: Partial) => V +} diff --git a/packages/filecoin-api/test/deal-tracker.spec.js b/packages/filecoin-api/test/deal-tracker.spec.js new file mode 100644 index 000000000..57748c70d --- /dev/null +++ b/packages/filecoin-api/test/deal-tracker.spec.js @@ -0,0 +1,51 @@ +/* eslint-disable no-only-tests/no-only-tests */ +import * as assert from 'assert' +import * as Signer from '@ucanto/principal/ed25519' + +import * as DealTrackerService from './services/deal-tracker.js' + +import { getStoreImplementations } from './context/store-implementations.js' + +/** + * @typedef {import('../src/deal-tracker/api.js').DealRecord} DealRecord + * @typedef {import('../src/deal-tracker/api.js').DealRecordKey} DealRecordKey + */ + +describe('deal-tracker', () => { + describe('deal/*', () => { + for (const [name, test] of Object.entries(DealTrackerService.test)) { + const define = name.startsWith('only ') + ? it.only + : name.startsWith('skip ') + ? it.skip + : it + + define(name, async () => { + const dealTrackerSigner = await Signer.generate() + + // resources + const { + dealTracker: { dealStore }, + } = getStoreImplementations() + + await test( + { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + ok: assert.ok, + }, + { + id: dealTrackerSigner, + dealStore, + queuedMessages: new Map(), + errorReporter: { + catch(error) { + assert.fail(error) + }, + }, + } + ) + }) + } + }) +}) diff --git a/packages/filecoin-api/test/dealer.spec.js b/packages/filecoin-api/test/dealer.spec.js index f85f5930e..66f2dc913 100644 --- a/packages/filecoin-api/test/dealer.spec.js +++ b/packages/filecoin-api/test/dealer.spec.js @@ -1,57 +1,115 @@ /* eslint-disable no-only-tests/no-only-tests */ import * as assert from 'assert' -import * as Broker from './services/dealer.js' import * as Signer from '@ucanto/principal/ed25519' -import { Store } from './context/store.js' -import { Queue } from './context/queue.js' - -describe('deal/*', () => { - for (const [name, test] of Object.entries(Broker.test)) { - const define = name.startsWith('only ') - ? it.only - : name.startsWith('skip ') - ? it.skip - : it - - define(name, async () => { - const signer = await Signer.generate() - const id = signer.withDID('did:web:test.spade-proxy.web3.storage') - - // resources - /** @type {unknown[]} */ - const queuedMessages = [] - const addQueue = new Queue({ - onMessage: (message) => queuedMessages.push(message), - }) - const dealLookupFn = ( - /** @type {Iterable | ArrayLike} */ items, - /** @type {any} */ record - ) => { - return Array.from(items).find((i) => - i.aggregate.equals(record.aggregate) - ) - } - const dealStore = new Store(dealLookupFn) - - await test( - { - equal: assert.strictEqual, - deepEqual: assert.deepStrictEqual, - ok: assert.ok, - }, - { - id, - errorReporter: { - catch(error) { - assert.fail(error) +import * as DealerService from './services/dealer.js' +import * as DealerEvents from './events/dealer.js' + +import { getStoreImplementations } from './context/store-implementations.js' +import { getMockService, getConnection } from './context/service.js' + +describe('Dealer', () => { + describe('aggregate/*', () => { + for (const [name, test] of Object.entries(DealerService.test)) { + const define = name.startsWith('only ') + ? it.only + : name.startsWith('skip ') + ? it.skip + : it + + define(name, async () => { + const dealerSigner = await Signer.generate() + + // resources + /** @type {Map} */ + const queuedMessages = new Map() + const { + dealer: { aggregateStore, offerStore }, + } = getStoreImplementations() + + await test( + { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + ok: assert.ok, + }, + { + id: dealerSigner, + errorReporter: { + catch(error) { + assert.fail(error) + }, }, + aggregateStore, + offerStore, + queuedMessages, + } + ) + }) + } + }) + + describe('events', () => { + for (const [name, test] of Object.entries(DealerEvents.test)) { + const define = name.startsWith('only ') + ? it.only + : name.startsWith('skip ') + ? it.skip + : it + + define(name, async () => { + const dealerSigner = await Signer.generate() + const dealTrackerSigner = await Signer.generate() + const service = getMockService() + const dealerConnection = getConnection(dealerSigner, service).connection + const dealTrackerConnection = getConnection( + dealTrackerSigner, + service + ).connection + + // resources + /** @type {Map} */ + const queuedMessages = new Map() + const { + dealer: { aggregateStore, offerStore }, + } = getStoreImplementations() + + await test( + { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + ok: assert.ok, }, - addQueue, - dealStore, - queuedMessages, - } - ) - }) - } + { + id: dealerSigner, + errorReporter: { + catch(error) { + assert.fail(error) + }, + }, + aggregateStore, + offerStore, + queuedMessages, + dealerService: { + connection: dealerConnection, + invocationConfig: { + issuer: dealerSigner, + with: dealerSigner.did(), + audience: dealerSigner, + }, + }, + dealTrackerService: { + connection: dealTrackerConnection, + invocationConfig: { + issuer: dealerSigner, + with: dealerSigner.did(), + audience: dealTrackerSigner, + }, + }, + service, + } + ) + }) + } + }) }) diff --git a/packages/filecoin-api/test/events/aggregator.js b/packages/filecoin-api/test/events/aggregator.js new file mode 100644 index 000000000..3810b2bcb --- /dev/null +++ b/packages/filecoin-api/test/events/aggregator.js @@ -0,0 +1,1238 @@ +import * as Server from '@ucanto/server' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' +import pWaitFor from 'p-wait-for' +import { CBOR } from '@ucanto/core' + +import * as API from '../../src/types.js' +import * as TestAPI from '../types.js' +import * as AggregatorEvents from '../../src/aggregator/events.js' + +import { FailingStore } from '../context/store.js' +import { FailingQueue } from '../context/queue.js' +import { mockService } from '../context/mocks.js' +import { getConnection } from '../context/service.js' +import { getStoreImplementations } from '../context/store-implementations.js' +import { randomAggregate, randomCargo } from '../utils.js' +import { + QueueOperationErrorName, + RecordNotFoundErrorName, + StoreOperationErrorName, +} from '../../src/errors.js' + +/** + * @typedef {import('../../src/aggregator/api.js').Buffer} Buffer + * @typedef {import('../../src/aggregator/api.js').PiecePolicy} PiecePolicy + * + * @typedef {import('../../src/aggregator/api.js').PieceMessage} PieceMessage + * @typedef {import('../../src/aggregator/api.js').BufferMessage} BufferMessage + * @typedef {import('../../src/aggregator/api.js').AggregateOfferMessage} AggregateOfferMessage + * @typedef {import('../../src/aggregator/api.js').PieceAcceptMessage} PieceAcceptMessage + */ + +/** + * @type {API.Tests} + */ +export const test = { + 'handles piece queue messages successfully': async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + /** @type {PieceMessage} */ + const message = { + piece: cargo.link.link(), + group: context.id.did(), + } + + // Handle message + const handledMessageRes = await AggregatorEvents.handlePieceMessage( + context, + message + ) + assert.ok(handledMessageRes.ok) + + // Verify store + const hasStoredPiece = await context.pieceStore.get({ + piece: message.piece, + group: message.group, + }) + assert.ok(hasStoredPiece.ok) + assert.equal(hasStoredPiece.ok?.status, 'offered') + assert.ok(hasStoredPiece.ok?.insertedAt) + assert.ok(hasStoredPiece.ok?.updatedAt) + }, + 'handles piece queue message errors when fails to access piece store': + wichMockableContext( + async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + /** @type {PieceMessage} */ + const message = { + piece: cargo.link.link(), + group: context.id.did(), + } + + // Handle message + const handledMessageRes = await AggregatorEvents.handlePieceMessage( + context, + message + ) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, StoreOperationErrorName) + }, + async (context) => ({ + ...context, + pieceStore: getStoreImplementations(FailingStore).aggregator.pieceStore, + }) + ), + 'handles pieces insert batch successfully': async (assert, context) => { + const group = context.id.did() + const { pieces } = await randomAggregate(100, 128) + + // Handle event + const handledPieceInsertsRes = await AggregatorEvents.handlePiecesInsert( + context, + pieces.map((p) => ({ + piece: p.link, + group, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + })) + ) + assert.ok(handledPieceInsertsRes.ok) + + // Validate queue and store + await pWaitFor( + () => context.queuedMessages.get('bufferQueue')?.length === 1 + ) + /** @type {BufferMessage} */ + // @ts-expect-error cannot infer buffer message + const message = context.queuedMessages.get('bufferQueue')?.[0] + assert.equal(message.group, group) + + const bufferGet = await context.bufferStore.get(message.pieces) + assert.ok(bufferGet.ok) + assert.ok(bufferGet.ok?.block.equals(message.pieces)) + assert.deepEqual( + bufferGet.ok?.buffer.pieces.map((p) => p.piece), + pieces.map((p) => p.link) + ) + }, + 'handles piece insert event errors when fails to access buffer store': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { pieces } = await randomAggregate(100, 128) + + // Handle event + const handledPieceInsertsRes = + await AggregatorEvents.handlePiecesInsert( + context, + pieces.map((p) => ({ + piece: p.link, + group, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + })) + ) + assert.ok(handledPieceInsertsRes.error) + assert.equal( + handledPieceInsertsRes.error?.name, + StoreOperationErrorName + ) + }, + async (context) => ({ + ...context, + bufferStore: + getStoreImplementations(FailingStore).aggregator.bufferStore, + }) + ), + 'handles piece insert event errors when fails to access buffer queue': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { pieces } = await randomAggregate(100, 128) + + // Handle event + const handledPieceInsertsRes = + await AggregatorEvents.handlePiecesInsert( + context, + pieces.map((p) => ({ + piece: p.link, + group, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + })) + ) + assert.ok(handledPieceInsertsRes.error) + assert.equal( + handledPieceInsertsRes.error?.name, + QueueOperationErrorName + ) + }, + async (context) => ({ + ...context, + bufferQueue: new FailingQueue(), + }) + ), + 'handles buffer queue messages successfully to requeue bigger buffer': async ( + assert, + context + ) => { + const group = context.id.did() + const { buffers, blocks } = await getBuffers(2, group) + + // Store buffers + for (let i = 0; i < blocks.length; i++) { + const putBufferRes = await context.bufferStore.put({ + buffer: buffers[i], + block: blocks[i].cid, + }) + assert.ok(putBufferRes.ok) + } + + // Handle messages + const handledMessageRes = await AggregatorEvents.handleBufferQueueMessage( + { + ...context, + config: { + minAggregateSize: 2 ** 34, + minUtilizationFactor: 4, + maxAggregateSize: 2 ** 35, + }, + }, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.ok) + assert.equal(handledMessageRes.ok?.aggregatedPieces, 0) + + // Validate queue and store + await pWaitFor( + () => context.queuedMessages.get('bufferQueue')?.length === 1 + ) + /** @type {BufferMessage} */ + // @ts-expect-error cannot infer buffer message + const message = context.queuedMessages.get('bufferQueue')?.[0] + assert.equal(message.group, group) + + const bufferGet = await context.bufferStore.get(message.pieces) + assert.ok(bufferGet.ok) + assert.ok(bufferGet.ok?.block.equals(message.pieces)) + assert.equal(bufferGet.ok?.buffer.group, group) + assert.ok(!bufferGet.ok?.buffer.aggregate) + assert.equal( + bufferGet.ok?.buffer.pieces.length, + buffers.reduce((acc, v) => { + acc += v.pieces.length + return acc + }, 0) + ) + }, + 'handles buffer queue messages with failure when fails to read them from store': + async (assert, context) => { + const group = context.id.did() + const { blocks } = await getBuffers(2, group) + + // Handle messages + const handledMessageRes = await AggregatorEvents.handleBufferQueueMessage( + context, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, RecordNotFoundErrorName) + }, + 'handles buffer queue messages successfully to requeue bigger buffer if does not have minimum utilization': + async (assert, context) => { + const group = context.id.did() + const { buffers, blocks } = await getBuffers(2, group, { + length: 10, + size: 1024, + }) + + // Store buffers + for (let i = 0; i < blocks.length; i++) { + const putBufferRes = await context.bufferStore.put({ + buffer: buffers[i], + block: blocks[i].cid, + }) + assert.ok(putBufferRes.ok) + } + + // Handle messages + const handledMessageRes = await AggregatorEvents.handleBufferQueueMessage( + { + ...context, + config: { + minAggregateSize: 2 ** 13, + minUtilizationFactor: 1, + maxAggregateSize: 2 ** 18, + }, + }, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.ok) + assert.equal(handledMessageRes.ok?.aggregatedPieces, 0) + + // Validate queue and store + await pWaitFor( + () => context.queuedMessages.get('bufferQueue')?.length === 1 + ) + /** @type {BufferMessage} */ + // @ts-expect-error cannot infer buffer message + const message = context.queuedMessages.get('bufferQueue')?.[0] + assert.equal(message.group, group) + + const bufferGet = await context.bufferStore.get(message.pieces) + assert.ok(bufferGet.ok) + assert.ok(bufferGet.ok?.block.equals(message.pieces)) + assert.equal(bufferGet.ok?.buffer.group, group) + assert.ok(!bufferGet.ok?.buffer.aggregate) + assert.equal( + bufferGet.ok?.buffer.pieces.length, + buffers.reduce((acc, v) => { + acc += v.pieces.length + return acc + }, 0) + ) + }, + 'handles buffer queue messages successfully to queue aggregate': async ( + assert, + context + ) => { + const group = context.id.did() + const { buffers, blocks } = await getBuffers(2, group, { + length: 100, + size: 128, + }) + const totalPieces = buffers.reduce((acc, v) => { + acc += v.pieces.length + return acc + }, 0) + + // Store buffers + for (let i = 0; i < blocks.length; i++) { + const putBufferRes = await context.bufferStore.put({ + buffer: buffers[i], + block: blocks[i].cid, + }) + assert.ok(putBufferRes.ok) + } + + // Handle messages + const handledMessageRes = await AggregatorEvents.handleBufferQueueMessage( + { + ...context, + config: { + minAggregateSize: 2 ** 19, + minUtilizationFactor: 10e5, + maxAggregateSize: 2 ** 35, + }, + }, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.ok) + assert.equal(handledMessageRes.ok?.aggregatedPieces, totalPieces) + + // Validate queue and store + await pWaitFor( + () => + context.queuedMessages.get('bufferQueue')?.length === 0 && + context.queuedMessages.get('aggregateOfferQueue')?.length === 1 + ) + /** @type {AggregateOfferMessage} */ + // @ts-expect-error cannot infer buffer message + const message = context.queuedMessages.get('aggregateOfferQueue')?.[0] + assert.equal(message.group, group) + + const bufferGet = await context.bufferStore.get(message.pieces) + assert.ok(bufferGet.ok) + assert.ok(bufferGet.ok?.block.equals(message.pieces)) + assert.equal(bufferGet.ok?.buffer.group, group) + assert.ok(message.aggregate.equals(bufferGet.ok?.buffer.aggregate)) + assert.equal(bufferGet.ok?.buffer.pieces.length, totalPieces) + }, + 'handles buffer queue messages successfully to queue aggregate and remaining buffer': + async (assert, context) => { + const group = context.id.did() + const { buffers, blocks } = await getBuffers(2, group, { + length: 10, + size: 1024, + }) + const totalPieces = buffers.reduce((acc, v) => { + acc += v.pieces.length + return acc + }, 0) + + // Store buffers + for (let i = 0; i < blocks.length; i++) { + const putBufferRes = await context.bufferStore.put({ + buffer: buffers[i], + block: blocks[i].cid, + }) + assert.ok(putBufferRes.ok) + } + + // Handle messages + const handledMessageRes = await AggregatorEvents.handleBufferQueueMessage( + { + ...context, + config: { + minAggregateSize: 2 ** 13, + minUtilizationFactor: 10, + maxAggregateSize: 2 ** 15, + }, + }, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.ok) + + // Validate queue and store + await pWaitFor( + () => + context.queuedMessages.get('bufferQueue')?.length === 1 && + context.queuedMessages.get('aggregateOfferQueue')?.length === 1 + ) + /** @type {AggregateOfferMessage} */ + // @ts-expect-error cannot infer buffer message + const aggregateOfferMessage = context.queuedMessages.get( + 'aggregateOfferQueue' + )?.[0] + /** @type {BufferMessage} */ + // @ts-expect-error cannot infer buffer message + const bufferMessage = context.queuedMessages.get('bufferQueue')?.[0] + + const aggregateBufferGet = await context.bufferStore.get( + aggregateOfferMessage.pieces + ) + assert.ok(aggregateBufferGet.ok) + const remainingBufferGet = await context.bufferStore.get( + bufferMessage.pieces + ) + assert.ok(remainingBufferGet.ok) + + assert.equal( + aggregateBufferGet.ok?.buffer.pieces.length, + handledMessageRes.ok?.aggregatedPieces + ) + assert.equal( + (aggregateBufferGet.ok?.buffer.pieces.length || 0) + + (remainingBufferGet.ok?.buffer.pieces.length || 0), + totalPieces + ) + }, + 'handles buffer queue message errors when fails to access buffer store': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { blocks } = await getBuffers(2, group) + + // Handle messages + const handledMessageRes = + await AggregatorEvents.handleBufferQueueMessage( + context, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, StoreOperationErrorName) + }, + async (context) => ({ + ...context, + bufferStore: + getStoreImplementations(FailingStore).aggregator.bufferStore, + }) + ), + 'handles buffer queue message errors when fails to put message in buffer queue': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { buffers, blocks } = await getBuffers(2, group) + + // Store buffers + for (let i = 0; i < blocks.length; i++) { + const putBufferRes = await context.bufferStore.put({ + buffer: buffers[i], + block: blocks[i].cid, + }) + assert.ok(putBufferRes.ok) + } + + // Handle messages + const handledMessageRes = + await AggregatorEvents.handleBufferQueueMessage( + { + ...context, + config: { + minAggregateSize: 2 ** 34, + minUtilizationFactor: 4, + maxAggregateSize: 2 ** 35, + }, + }, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, QueueOperationErrorName) + }, + async (context) => ({ + ...context, + bufferQueue: new FailingQueue(), + }) + ), + 'handles buffer queue message errors when fails to put message in aggregate queue': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { buffers, blocks } = await getBuffers(2, group, { + length: 100, + size: 128, + }) + + // Store buffers + for (let i = 0; i < blocks.length; i++) { + const putBufferRes = await context.bufferStore.put({ + buffer: buffers[i], + block: blocks[i].cid, + }) + assert.ok(putBufferRes.ok) + } + + // Handle messages + const handledMessageRes = + await AggregatorEvents.handleBufferQueueMessage( + { + ...context, + config: { + minAggregateSize: 2 ** 19, + minUtilizationFactor: 10e5, + maxAggregateSize: 2 ** 35, + }, + }, + blocks.map((b) => ({ + pieces: b.cid, + group, + })) + ) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, QueueOperationErrorName) + }, + async (context) => ({ + ...context, + aggregateOfferQueue: new FailingQueue(), + }) + ), + 'handles aggregate offer queue messages successfully': async ( + assert, + context + ) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + + /** @type {Buffer} */ + const buffer = { + pieces: pieces.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: 0, + })), + group, + } + const block = await CBOR.write(buffer) + + /** @type {AggregateOfferMessage} */ + const message = { + aggregate: aggregate.link, + pieces: block.cid, + group, + } + + // Handle message + const handledMessageRes = + await AggregatorEvents.handleAggregateOfferMessage(context, message) + assert.ok(handledMessageRes.ok) + + // Verify store + const hasStoredAggregate = await context.aggregateStore.get({ + aggregate: message.aggregate, + }) + assert.ok(hasStoredAggregate.ok) + assert.ok(hasStoredAggregate.ok?.aggregate.equals(aggregate.link)) + assert.ok(hasStoredAggregate.ok?.pieces.equals(block.cid)) + assert.equal(hasStoredAggregate.ok?.group, group) + assert.ok(hasStoredAggregate.ok?.insertedAt) + }, + 'handles aggregate offer queue message errors when fails to store into aggregate store': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + + /** @type {Buffer} */ + const buffer = { + pieces: pieces.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: 0, + })), + group, + } + const block = await CBOR.write(buffer) + + /** @type {AggregateOfferMessage} */ + const message = { + aggregate: aggregate.link, + pieces: block.cid, + group, + } + + // Handle message + const handledMessageRes = + await AggregatorEvents.handleAggregateOfferMessage(context, message) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, StoreOperationErrorName) + }, + async (context) => ({ + ...context, + aggregateStore: + getStoreImplementations(FailingStore).aggregator.aggregateStore, + }) + ), + 'handles aggregate insert to queue piece accept successfully': async ( + assert, + context + ) => { + const piecesLength = 100 + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(piecesLength, 128) + + /** @type {Buffer} */ + const buffer = { + pieces: pieces.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: 0, + })), + group, + } + const block = await CBOR.write(buffer) + + // Put buffer record + const putBufferRes = await context.bufferStore.put({ + buffer, + block: block.cid, + }) + assert.ok(putBufferRes.ok) + + // Put aggregate record + const aggregateRecord = { + pieces: block.cid, + aggregate: aggregate.link, + group, + insertedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put(aggregateRecord) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledAggregateInsertsRes = + await AggregatorEvents.handleAggregateInsertToPieceAcceptQueue( + context, + aggregateRecord + ) + assert.ok(handledAggregateInsertsRes.ok) + + // Validate queue and store + await pWaitFor( + () => + context.queuedMessages.get('pieceAcceptQueue')?.length === piecesLength + ) + // Validate one message + /** @type {PieceAcceptMessage} */ + // @ts-expect-error cannot infer buffer message + const message = context.queuedMessages.get('pieceAcceptQueue')?.[0] + assert.ok(message.aggregate.equals(aggregate.link)) + assert.ok(pieces.find((p) => p.link.equals(message.piece))) + assert.equal(message.group, group) + + // Verify inclusion proof + const inclusionProof = aggregate.resolveProof(message.piece) + if (!inclusionProof.ok) { + throw new Error() + } + assert.deepEqual(message.inclusion.subtree[0], inclusionProof.ok?.[0][0]) + assert.deepEqual(message.inclusion.index[0], inclusionProof.ok?.[1][0]) + + assert.deepEqual(message.inclusion.subtree[1], inclusionProof.ok?.[0][1]) + assert.deepEqual(message.inclusion.index[1], inclusionProof.ok?.[1][1]) + }, + 'handles aggregate insert event to piece accept queue errors when fails to read from buffer store': + wichMockableContext( + async (assert, context) => { + const piecesLength = 100 + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(piecesLength, 128) + + /** @type {Buffer} */ + const buffer = { + pieces: pieces.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: 0, + })), + group, + } + const block = await CBOR.write(buffer) + + // Put aggregate record + const aggregateRecord = { + pieces: block.cid, + aggregate: aggregate.link, + group, + insertedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put( + aggregateRecord + ) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledAggregateInsertsRes = + await AggregatorEvents.handleAggregateInsertToPieceAcceptQueue( + context, + aggregateRecord + ) + assert.ok(handledAggregateInsertsRes.error) + assert.equal( + handledAggregateInsertsRes.error?.name, + StoreOperationErrorName + ) + }, + async (context) => ({ + ...context, + bufferStore: + getStoreImplementations(FailingStore).aggregator.bufferStore, + }) + ), + 'handles aggregate insert event to piece accept queue errors when fails to add to piece accept queue': + wichMockableContext( + async (assert, context) => { + const piecesLength = 100 + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(piecesLength, 128) + + /** @type {Buffer} */ + const buffer = { + pieces: pieces.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: 0, + })), + group, + } + const block = await CBOR.write(buffer) + // Put buffer record + const putBufferRes = await context.bufferStore.put({ + buffer, + block: block.cid, + }) + assert.ok(putBufferRes.ok) + + // Put aggregate record + const aggregateRecord = { + pieces: block.cid, + aggregate: aggregate.link, + group, + insertedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put( + aggregateRecord + ) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledAggregateInsertsRes = + await AggregatorEvents.handleAggregateInsertToPieceAcceptQueue( + context, + aggregateRecord + ) + assert.ok(handledAggregateInsertsRes.error) + assert.equal( + handledAggregateInsertsRes.error?.name, + QueueOperationErrorName + ) + }, + async (context) => ({ + ...context, + pieceAcceptQueue: new FailingQueue(), + }) + ), + 'handles piece accept queue messages successfully': async ( + assert, + context + ) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // Create inclusion proof + const inclusionProof = aggregate.resolveProof(piece) + if (!inclusionProof.ok) { + throw new Error() + } + + /** @type {PieceAcceptMessage} */ + const message = { + aggregate: aggregate.link, + piece, + group, + inclusion: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + } + + // Handle message + const handledMessageRes = await AggregatorEvents.handlePieceAcceptMessage( + context, + message + ) + assert.ok(handledMessageRes.ok) + + // Verify store + const hasStoredInclusion = await context.inclusionStore.get({ + piece, + aggregate: message.aggregate, + }) + assert.ok(hasStoredInclusion.ok) + assert.ok(hasStoredInclusion.ok?.aggregate.equals(aggregate.link)) + assert.ok(hasStoredInclusion.ok?.piece.equals(piece)) + assert.equal(hasStoredInclusion.ok?.group, group) + assert.ok(hasStoredInclusion.ok?.insertedAt) + assert.deepEqual(hasStoredInclusion.ok?.inclusion, message.inclusion) + }, + 'handles piece accept message errors when fails to store on inclusion store': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // Create inclusion proof + const inclusionProof = aggregate.resolveProof(piece) + if (!inclusionProof.ok) { + throw new Error() + } + + /** @type {PieceAcceptMessage} */ + const message = { + aggregate: aggregate.link, + piece, + group, + inclusion: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + } + + // Handle message + const handledMessageRes = + await AggregatorEvents.handlePieceAcceptMessage(context, message) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, StoreOperationErrorName) + }, + async (context) => ({ + ...context, + inclusionStore: + getStoreImplementations(FailingStore).aggregator.inclusionStore, + }) + ), + 'handles inclusion insert to update piece store entry successfully': async ( + assert, + context + ) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // Store piece + const piecePut = await context.pieceStore.put({ + piece, + group, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + assert.ok(piecePut.ok) + + // Create inclusion proof + const inclusionProof = aggregate.resolveProof(piece) + if (!inclusionProof.ok) { + throw new Error() + } + + // Insert inclusion + const inclusionRecord = { + aggregate: aggregate.link, + piece, + group, + inclusion: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + insertedAt: new Date().toISOString(), + } + const inclusionPut = await context.inclusionStore.put(inclusionRecord) + assert.ok(inclusionPut.ok) + + // Handle insert event + const handledMessageRes = + await AggregatorEvents.handleInclusionInsertToUpdateState( + context, + inclusionRecord + ) + assert.ok(handledMessageRes.ok) + + // Verify store + const pieceGet = await context.pieceStore.get({ + piece, + group, + }) + assert.ok(pieceGet.ok) + assert.equal(pieceGet.ok?.status, 'accepted') + assert.ok(pieceGet.ok?.insertedAt) + assert.ok(pieceGet.ok?.updatedAt) + }, + 'handles inclusion insert event errors when fails to update piece store entry': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // Create inclusion proof + const inclusionProof = aggregate.resolveProof(piece) + if (!inclusionProof.ok) { + throw new Error() + } + + // Insert inclusion + const inclusionRecord = { + aggregate: aggregate.link, + piece, + group, + inclusion: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + insertedAt: new Date().toISOString(), + } + const inclusionPut = await context.inclusionStore.put(inclusionRecord) + assert.ok(inclusionPut.ok) + + // Handle insert event + const handledMessageRes = + await AggregatorEvents.handleInclusionInsertToUpdateState( + context, + inclusionRecord + ) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, StoreOperationErrorName) + }, + async (context) => ({ + ...context, + pieceStore: getStoreImplementations(FailingStore).aggregator.pieceStore, + }) + ), + 'handles inclusion insert to issue piece accept receipt successfully': async ( + assert, + context + ) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // Create inclusion proof + const inclusionProof = aggregate.resolveProof(piece) + if (!inclusionProof.ok) { + throw new Error() + } + + // Insert inclusion + const inclusionRecord = { + aggregate: aggregate.link, + piece, + group, + inclusion: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + insertedAt: new Date().toISOString(), + } + const inclusionPut = await context.inclusionStore.put(inclusionRecord) + assert.ok(inclusionPut.ok) + + // Handle insert event + const handledMessageRes = + await AggregatorEvents.handleInclusionInsertToIssuePieceAccept( + context, + inclusionRecord + ) + assert.ok(handledMessageRes.ok) + + // Verify invocation + // @ts-expect-error not typed hooks + assert.equal(context.service.piece?.accept?.callCount, 1) + assert.ok( + inclusionRecord.piece.equals( + // @ts-expect-error not typed hooks + context.service.piece?.accept?._params[0].nb.piece + ) + ) + assert.equal( + inclusionRecord.group, + // @ts-expect-error not typed hooks + context.service.piece?.accept?._params[0].nb.group + ) + }, + 'handles inclusion insert failures to invoke piece accept': + wichMockableContext( + async (assert, context) => { + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // Create inclusion proof + const inclusionProof = aggregate.resolveProof(piece) + if (!inclusionProof.ok) { + throw new Error() + } + + // Insert inclusion + const inclusionRecord = { + aggregate: aggregate.link, + piece, + group, + inclusion: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + insertedAt: new Date().toISOString(), + } + const inclusionPut = await context.inclusionStore.put(inclusionRecord) + assert.ok(inclusionPut.ok) + + // Handle message + const handledMessageRes = + await AggregatorEvents.handleInclusionInsertToIssuePieceAccept( + context, + inclusionRecord + ) + assert.ok(handledMessageRes.error) + }, + async (context) => { + /** + * Mock aggregator to fail + */ + const service = mockService({ + piece: { + accept: Server.provideAdvanced({ + capability: AggregatorCaps.pieceAccept, + handler: async ({ invocation, context }) => { + return { + error: new Server.Failure(), + } + }, + }), + }, + }) + const aggregatorConnection = getConnection( + context.id, + service + ).connection + return { + ...context, + service, + aggregatorService: { + connection: aggregatorConnection, + invocationConfig: { + issuer: context.id, + with: context.id.did(), + audience: context.id, + }, + }, + } + } + ), + 'handles aggregate insert to invoke aggregate offer successfully': async ( + assert, + context + ) => { + const piecesLength = 100 + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(piecesLength, 128) + + /** @type {Buffer} */ + const buffer = { + pieces: pieces.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: 0, + })), + group, + } + const blockBuffer = await CBOR.write(buffer) + const blockPieces = await CBOR.write(pieces.map((p) => p.link)) + + // Put buffer record + const putBufferRes = await context.bufferStore.put({ + buffer, + block: blockBuffer.cid, + }) + assert.ok(putBufferRes.ok) + + // Put aggregate record + const aggregateRecord = { + pieces: blockBuffer.cid, + aggregate: aggregate.link, + group, + insertedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put(aggregateRecord) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledAggregateInsertsRes = + await AggregatorEvents.handleAggregateInsertToAggregateOffer( + context, + aggregateRecord + ) + assert.ok(handledAggregateInsertsRes.ok) + + // Verify invocation + // @ts-expect-error not typed hooks + assert.equal(context.service.aggregate?.offer?.callCount, 1) + assert.ok( + blockPieces.cid.equals( + // @ts-expect-error not typed hooks + context.service.aggregate?.offer?._params[0].nb.pieces + ) + ) + assert.ok( + aggregateRecord.aggregate.equals( + // @ts-expect-error not typed hooks + context.service.aggregate?.offer?._params[0].nb.aggregate + ) + ) + }, + 'handles aggregate insert event errors when fails to read from buffer store': + wichMockableContext( + async (assert, context) => { + const piecesLength = 100 + const group = context.id.did() + const { aggregate, pieces } = await randomAggregate(piecesLength, 128) + + /** @type {Buffer} */ + const buffer = { + pieces: pieces.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: 0, + })), + group, + } + const blockBuffer = await CBOR.write(buffer) + + // Put aggregate record + const aggregateRecord = { + pieces: blockBuffer.cid, + aggregate: aggregate.link, + group, + insertedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put( + aggregateRecord + ) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledAggregateInsertsRes = + await AggregatorEvents.handleAggregateInsertToAggregateOffer( + context, + aggregateRecord + ) + assert.ok(handledAggregateInsertsRes.error) + assert.equal( + handledAggregateInsertsRes.error?.name, + StoreOperationErrorName + ) + }, + async (context) => ({ + ...context, + bufferStore: + getStoreImplementations(FailingStore).aggregator.bufferStore, + }) + ), +} + +/** + * @param {number} length + * @param {string} group + * @param {object} [piecesOptions] + * @param {number} [piecesOptions.length] + * @param {number} [piecesOptions.size] + */ +async function getBuffers(length, group, piecesOptions = {}) { + const piecesLength = piecesOptions.length || 100 + const piecesSize = piecesOptions.size || 128 + + const pieceBatches = await Promise.all( + Array.from({ length }).map(() => randomCargo(piecesLength, piecesSize)) + ) + + const buffers = pieceBatches.map((b) => ({ + pieces: b.map((p) => ({ + piece: p.link, + insertedAt: new Date().toISOString(), + policy: /** @type {PiecePolicy} */ (0), + })), + group, + })) + + return { + buffers, + blocks: await Promise.all(buffers.map((b) => CBOR.write(b))), + } +} + +/** + * @param {API.Test} testFn + * @param {(context: TestAPI.AggregatorTestEventsContext) => Promise} mockContextFunction + */ +function wichMockableContext(testFn, mockContextFunction) { + // @ts-ignore + return async function (...args) { + const modifiedArgs = [args[0], await mockContextFunction(args[1])] + // @ts-ignore + return testFn(...modifiedArgs) + } +} diff --git a/packages/filecoin-api/test/events/dealer.js b/packages/filecoin-api/test/events/dealer.js new file mode 100644 index 000000000..b583f0ffc --- /dev/null +++ b/packages/filecoin-api/test/events/dealer.js @@ -0,0 +1,448 @@ +import { CBOR } from '@ucanto/core' +import * as Signer from '@ucanto/principal/ed25519' +import * as Server from '@ucanto/server' +import * as DealerCaps from '@web3-storage/capabilities/filecoin/dealer' +import * as DealTrackerCaps from '@web3-storage/capabilities/filecoin/deal-tracker' + +import * as API from '../../src/types.js' +import * as TestAPI from '../types.js' +import * as DealerEvents from '../../src/dealer/events.js' + +import { FailingStore } from '../context/store.js' +import { mockService } from '../context/mocks.js' +import { getConnection } from '../context/service.js' +import { getStoreImplementations } from '../context/store-implementations.js' +import { randomAggregate } from '../utils.js' +import { StoreOperationErrorName } from '../../src/errors.js' + +/** + * @typedef {import('../../src/dealer/api.js').AggregateRecord} AggregateRecord + */ + +/** + * @type {API.Tests} + */ +export const test = { + 'handles aggregate insert event successfully': async (assert, context) => { + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + const putOfferRes = await context.offerStore.put({ + key: piecesBlock.cid.toString(), + value: { + issuer: context.id.did(), + aggregate: aggregate.link, + pieces: offer, + }, + }) + assert.ok(putOfferRes.ok) + + const offerStoreGetBeforeRename = await context.offerStore.get( + piecesBlock.cid.toString() + ) + assert.ok(offerStoreGetBeforeRename.ok) + + // Put aggregate record + /** @type {AggregateRecord} */ + const aggregateRecord = { + pieces: piecesBlock.cid, + aggregate: aggregate.link, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put(aggregateRecord) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledPieceInsertsRes = await DealerEvents.handleAggregateInsert( + context, + aggregateRecord + ) + assert.ok(handledPieceInsertsRes.ok) + + // Old name not available + const offerStoreGetAfterRename0 = await context.offerStore.get( + piecesBlock.cid.toString() + ) + assert.ok(offerStoreGetAfterRename0.error) + // New name available + const offerStoreGetAfterRename1 = await context.offerStore.get( + `${new Date( + aggregateRecord.insertedAt + ).toISOString()} ${aggregateRecord.aggregate.toString()}.json` + ) + assert.ok(offerStoreGetAfterRename1.ok) + }, + 'handles aggregate insert errors when fails to update piece store': + wichMockableContext( + async (assert, context) => { + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Put aggregate record + /** @type {AggregateRecord} */ + const aggregateRecord = { + pieces: piecesBlock.cid, + aggregate: aggregate.link, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put( + aggregateRecord + ) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledPieceInsertsRes = await DealerEvents.handleAggregateInsert( + context, + aggregateRecord + ) + assert.ok(handledPieceInsertsRes.error) + assert.equal( + handledPieceInsertsRes.error?.name, + StoreOperationErrorName + ) + }, + async (context) => ({ + ...context, + offerStore: getStoreImplementations(FailingStore).dealer.offerStore, + }) + ), + 'handles aggregate update status event successfully': async ( + assert, + context + ) => { + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Put aggregate record + /** @type {AggregateRecord} */ + const aggregateRecord = { + pieces: piecesBlock.cid, + aggregate: aggregate.link, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put(aggregateRecord) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledPieceInsertsRes = + await DealerEvents.handleAggregatUpdatedStatus(context, aggregateRecord) + assert.ok(handledPieceInsertsRes.ok) + + // Verify invocation + // @ts-expect-error not typed hooks + assert.equal(context.service.aggregate?.accept?.callCount, 1) + assert.ok( + // @ts-expect-error not typed hooks + context.service.aggregate?.accept?._params[0].nb.pieces.equals( + piecesBlock.cid + ) + ) + assert.ok( + // @ts-expect-error not typed hooks + context.service.aggregate?.accept?._params[0].nb.aggregate.equals( + aggregate.link + ) + ) + }, + 'handles aggregate update status event errors on dealer invocation failure': + wichMockableContext( + async (assert, context) => { + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Put aggregate record + /** @type {AggregateRecord} */ + const aggregateRecord = { + pieces: piecesBlock.cid, + aggregate: aggregate.link, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put( + aggregateRecord + ) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledPieceInsertsRes = + await DealerEvents.handleAggregatUpdatedStatus( + context, + aggregateRecord + ) + assert.ok(handledPieceInsertsRes.error) + }, + async (context) => { + /** + * Mock dealer to fail + */ + const service = mockService({ + aggregate: { + accept: Server.provideAdvanced({ + capability: DealerCaps.aggregateAccept, + handler: async ({ invocation, context }) => { + return { + error: new Server.Failure(), + } + }, + }), + }, + }) + const dealerConnection = getConnection(context.id, service).connection + + return { + ...context, + service, + dealerService: { + ...context.dealerService, + connection: dealerConnection, + }, + } + } + ), + 'handles cron tick successfully by swapping state of offered aggregate': + async (assert, context) => { + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Put aggregate record + /** @type {AggregateRecord} */ + const aggregateRecord = { + pieces: piecesBlock.cid, + aggregate: aggregate.link, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put(aggregateRecord) + assert.ok(putAggregateRes.ok) + const storedDealBeforeCron = await context.aggregateStore.get({ + aggregate: aggregate.link.link(), + }) + assert.ok(storedDealBeforeCron.ok) + assert.equal(storedDealBeforeCron.ok?.status, 'offered') + + // Handle event + const handledCronTick = await DealerEvents.handleCronTick(context) + assert.ok(handledCronTick.ok) + assert.equal(handledCronTick.ok?.updatedCount, 1) + assert.equal(handledCronTick.ok?.pendingCount, 0) + + // Validate stores + // Deal as in mocked service + const deal = { + dataType: 0n, + dataSource: { + dealID: 111n, + }, + } + const storedDealAfterCron = await context.aggregateStore.get({ + aggregate: aggregate.link.link(), + deal, + }) + assert.ok(storedDealAfterCron.ok) + assert.equal(storedDealAfterCron.ok?.status, 'accepted') + assert.deepEqual(storedDealAfterCron.ok?.deal, deal) + assert.ok( + storedDealBeforeCron.ok?.updatedAt !== storedDealAfterCron.ok?.updatedAt + ) + }, + 'handles cron tick several times until deal exists': wichMockableContext( + async (assert, context) => { + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Put aggregate record + /** @type {AggregateRecord} */ + const aggregateRecord = { + pieces: piecesBlock.cid, + aggregate: aggregate.link, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put(aggregateRecord) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledCronTick1 = await DealerEvents.handleCronTick(context) + assert.ok(handledCronTick1.ok) + assert.equal(handledCronTick1.ok?.updatedCount, 0) + assert.equal(handledCronTick1.ok?.pendingCount, 1) + + // Should have same state and no deal + const storedDealAfterFirstCron = await context.aggregateStore.get({ + aggregate: aggregate.link.link(), + }) + assert.ok(storedDealAfterFirstCron.ok) + assert.equal(storedDealAfterFirstCron.ok?.status, 'offered') + + // Handle event second time + const handledCronTick2 = await DealerEvents.handleCronTick(context) + assert.ok(handledCronTick2.ok) + assert.equal(handledCronTick2.ok?.updatedCount, 1) + assert.equal(handledCronTick2.ok?.pendingCount, 0) + }, + async (context) => { + let counter = 1 + + /** + * Mock deal tracker to only send deal info on second call + */ + const dealTrackerSigner = await Signer.generate() + const service = mockService({ + deal: { + info: Server.provideAdvanced({ + capability: DealTrackerCaps.dealInfo, + handler: async ({ invocation, context }) => { + const invCap = invocation.capabilities[0] + if (!invCap.nb?.piece) { + throw new Error() + } + + if (counter === 2) { + /** @type {API.UcantoInterface.OkBuilder} */ + return Server.ok({ + deals: { + 111: { + provider: 'f11111', + }, + }, + }) + } + + counter += 1 + return Server.ok({ + deals: {}, + }) + }, + }), + }, + }) + const dealTrackerConnection = getConnection( + dealTrackerSigner, + service + ).connection + + return { + ...context, + service, + dealTrackerService: { + connection: dealTrackerConnection, + invocationConfig: { + issuer: context.id, + with: context.id.did(), + audience: dealTrackerSigner, + }, + }, + } + } + ), + 'handles cron tick errors when aggregate store query fails': + wichMockableContext( + async (assert, context) => { + // Handle event + const handledCronTick = await DealerEvents.handleCronTick(context) + assert.ok(handledCronTick.error) + assert.equal(handledCronTick.error?.name, StoreOperationErrorName) + }, + async (context) => ({ + ...context, + aggregateStore: + getStoreImplementations(FailingStore).dealer.aggregateStore, + }) + ), + 'handles cron tick errors when deal tracker fails to respond': + wichMockableContext( + async (assert, context) => { + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Put aggregate record + /** @type {AggregateRecord} */ + const aggregateRecord = { + pieces: piecesBlock.cid, + aggregate: aggregate.link, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + const putAggregateRes = await context.aggregateStore.put( + aggregateRecord + ) + assert.ok(putAggregateRes.ok) + + // Handle event + const handledCronTick = await DealerEvents.handleCronTick(context) + assert.ok(handledCronTick.error) + }, + async (context) => { + /** + * Mock deal tracker to fail + */ + const dealTrackerSigner = await Signer.generate() + const service = mockService({ + deal: { + info: Server.provideAdvanced({ + capability: DealTrackerCaps.dealInfo, + handler: async ({ invocation, context }) => { + return { + error: new Server.Failure(), + } + }, + }), + }, + }) + const dealTrackerConnection = getConnection( + dealTrackerSigner, + service + ).connection + + return { + ...context, + service, + dealTrackerService: { + connection: dealTrackerConnection, + invocationConfig: { + issuer: context.id, + with: context.id.did(), + audience: dealTrackerSigner, + }, + }, + } + } + ), +} + +/** + * @param {API.Test} testFn + * @param {(context: TestAPI.DealerTestEventsContext) => Promise} mockContextFunction + */ +function wichMockableContext(testFn, mockContextFunction) { + // @ts-ignore + return async function (...args) { + const modifiedArgs = [args[0], await mockContextFunction(args[1])] + // @ts-ignore + return testFn(...modifiedArgs) + } +} diff --git a/packages/filecoin-api/test/events/storefront.js b/packages/filecoin-api/test/events/storefront.js new file mode 100644 index 000000000..9de711d01 --- /dev/null +++ b/packages/filecoin-api/test/events/storefront.js @@ -0,0 +1,496 @@ +import * as Server from '@ucanto/server' +import * as Signer from '@ucanto/principal/ed25519' +import { CBOR } from '@ucanto/core' +import * as AggregatorCaps from '@web3-storage/capabilities/filecoin/aggregator' + +import * as API from '../../src/types.js' +import * as TestAPI from '../types.js' +import * as StorefrontEvents from '../../src/storefront/events.js' +import { + StoreOperationErrorName, + UnexpectedStateErrorName, +} from '../../src/errors.js' + +import { randomCargo, randomAggregate } from '../utils.js' +import { FailingStore } from '../context/store.js' +import { mockService } from '../context/mocks.js' +import { getConnection } from '../context/service.js' +import { getStoreImplementations } from '../context/store-implementations.js' +import { createInvocationsAndReceiptsForDealDataProofChain } from '../context/receipts.js' + +/** + * @typedef {import('../../src/storefront/api.js').PieceRecord} PieceRecord + */ + +/** + * @type {API.Tests} + */ +export const test = { + 'handles filecoin submit messages successfully': async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + + // Handle message + const handledMessageRes = + await StorefrontEvents.handleFilecoinSubmitMessage(context, message) + assert.ok(handledMessageRes.ok) + + // Verify store + const hasStoredPiece = await context.pieceStore.get({ + piece: cargo.link.link(), + }) + assert.ok(hasStoredPiece.ok) + assert.equal(hasStoredPiece.ok?.status, 'submitted') + }, + 'handles filecoin submit messages deduping when stored': async ( + assert, + context + ) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + /** @type {PieceRecord} */ + const pieceRecord = { + ...message, + status: 'submitted', + insertedAt: new Date(Date.now() - 10).toISOString(), + updatedAt: new Date(Date.now() - 5).toISOString(), + } + const putRes = await context.pieceStore.put(pieceRecord) + assert.ok(putRes.ok) + + // Handle message + const handledMessageRes = + await StorefrontEvents.handleFilecoinSubmitMessage(context, message) + assert.ok(handledMessageRes.ok) + + // Verify store + const hasStoredPiece = await context.pieceStore.get({ + piece: cargo.link.link(), + }) + assert.ok(hasStoredPiece.ok) + assert.equal(hasStoredPiece.ok?.status, 'submitted') + assert.equal(hasStoredPiece.ok?.updatedAt, pieceRecord.updatedAt) + }, + 'handles filecoin submit messages errors when fails to access piece store': + wichMockableContext( + async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + + // Handle message + const handledMessageRes = + await StorefrontEvents.handleFilecoinSubmitMessage(context, message) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, StoreOperationErrorName) + }, + async (context) => ({ + ...context, + pieceStore: getStoreImplementations(FailingStore).storefront.pieceStore, + }) + ), + 'handles piece offer messages successfully': async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + + // Handle message + const handledMessageRes = await StorefrontEvents.handlePieceOfferMessage( + context, + message + ) + assert.ok(handledMessageRes.ok) + + // Verify invocation + // @ts-expect-error not typed hooks + assert.equal(context.service.piece?.offer?.callCount, 1) + assert.equal( + // @ts-expect-error not typed hooks + context.service.piece?.offer?._params[0].nb.group, + message.group + ) + assert.ok( + // @ts-expect-error not typed hooks + message.piece.equals(context.service.piece?.offer?._params[0].nb.piece) + ) + }, + 'handles piece offer messages erroring when fails to invoke piece offer': + wichMockableContext( + async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + + // Handle message + const handledMessageRes = + await StorefrontEvents.handlePieceOfferMessage(context, message) + assert.ok(handledMessageRes.error) + }, + async (context) => { + /** + * Mock aggregator to fail + */ + const aggregatorSigner = await Signer.generate() + const service = mockService({ + piece: { + offer: Server.provideAdvanced({ + capability: AggregatorCaps.pieceOffer, + handler: async ({ invocation, context }) => { + return { + error: new Server.Failure(), + } + }, + }), + }, + }) + const aggregatorConnection = getConnection( + aggregatorSigner, + service + ).connection + return { + ...context, + service, + aggregatorService: { + connection: aggregatorConnection, + invocationConfig: { + issuer: context.id, + with: context.id.did(), + audience: aggregatorSigner, + }, + }, + } + } + ), + 'handles piece insert event successfully': async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + /** @type {PieceRecord} */ + const pieceRecord = { + ...message, + status: 'submitted', + insertedAt: new Date(Date.now() - 10).toISOString(), + updatedAt: new Date(Date.now() - 5).toISOString(), + } + + // Handle message + const handledMessageRes = await StorefrontEvents.handlePieceInsert( + context, + pieceRecord + ) + assert.ok(handledMessageRes.ok) + + // Verify invocation + // @ts-expect-error not typed hooks + assert.equal(context.service.filecoin?.submit?.callCount, 1) + assert.ok( + message.content.equals( + // @ts-expect-error not typed hooks + context.service.filecoin?.submit?._params[0].nb.content + ) + ) + assert.ok( + message.piece.equals( + // @ts-expect-error not typed hooks + context.service.filecoin?.submit?._params[0].nb.piece + ) + ) + }, + 'handles piece status update event successfully': async (assert, context) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + /** @type {PieceRecord} */ + const pieceRecord = { + ...message, + status: 'accepted', + insertedAt: new Date(Date.now() - 10).toISOString(), + updatedAt: new Date(Date.now() - 5).toISOString(), + } + + // Handle message + const handledMessageRes = await StorefrontEvents.handlePieceStatusUpdate( + context, + pieceRecord + ) + assert.ok(handledMessageRes.ok) + + // Verify invocation + // @ts-expect-error not typed hooks + assert.equal(context.service.filecoin?.accept?.callCount, 1) + assert.ok( + message.content.equals( + // @ts-expect-error not typed hooks + context.service.filecoin?.accept?._params[0].nb.content + ) + ) + assert.ok( + message.piece.equals( + // @ts-expect-error not typed hooks + context.service.filecoin?.accept?._params[0].nb.piece + ) + ) + }, + 'fails to handle piece status update event if unexpected state': async ( + assert, + context + ) => { + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // Store piece into store + const message = { + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + } + /** @type {PieceRecord} */ + const pieceRecord = { + ...message, + status: 'submitted', + insertedAt: new Date(Date.now() - 10).toISOString(), + updatedAt: new Date(Date.now() - 5).toISOString(), + } + + // Handle message + const handledMessageRes = await StorefrontEvents.handlePieceStatusUpdate( + context, + pieceRecord + ) + assert.ok(handledMessageRes.error) + assert.equal(handledMessageRes.error?.name, UnexpectedStateErrorName) + }, + 'handles cron tick successfully to modify status': async ( + assert, + context + ) => { + const { dealer } = await getServiceContext() + const group = context.id.did() + + // Create piece and aggregate for test + const { aggregate, pieces } = await randomAggregate(10, 128) + const piece = pieces[0] + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Store pieces into store + await Promise.all( + pieces.map(async (p) => { + const putRes = await context.pieceStore.put({ + piece: p.link, + content: p.content, + group: context.id.did(), + status: 'submitted', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + assert.ok(putRes.ok) + }) + ) + + // Create inclusion proof for test + const inclusionProof = aggregate.resolveProof(piece.link) + if (inclusionProof.error) { + throw new Error('could not compute inclusion proof') + } + + // Cron ticks with no deals or receipts still available + const handledCronTickResBeforeAnyReceipt = + await StorefrontEvents.handleCronTick(context) + assert.ok(handledCronTickResBeforeAnyReceipt.ok) + assert.equal(handledCronTickResBeforeAnyReceipt.ok?.updatedCount, 0) + assert.equal( + handledCronTickResBeforeAnyReceipt.ok?.pendingCount, + pieces.length + ) + + // Create invocations and receipts for chain into DealDataProof + const dealMetadata = { + dataType: 0n, + dataSource: { + dealID: 100n, + }, + } + + // Create invocation and receipts chain until deal + const { invocations, receipts } = + await createInvocationsAndReceiptsForDealDataProofChain({ + storefront: context.id, + aggregator: context.aggregatorId, + dealer, + aggregate: aggregate.link, + group, + piece: piece.link, + piecesBlock, + inclusionProof: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + aggregateAcceptStatus: { + ...dealMetadata, + aggregate: aggregate.link, + }, + }) + + // Store all invocations and receipts, except for the very last receipt on the chain + const storedInvocationsAndReceiptsRes = await storeInvocationsAndReceipts({ + invocations, + receipts: { + pieceOfferReceipt: receipts.pieceOfferReceipt, + pieceAcceptReceipt: receipts.pieceAcceptReceipt, + aggregateOfferReceipt: receipts.aggregateOfferReceipt, + }, + taskStore: context.taskStore, + receiptStore: context.receiptStore, + }) + assert.ok(storedInvocationsAndReceiptsRes.ok) + + // Cron ticks with no deals or receipts still available + const handledCronTickResBeforeFinalReceipt = + await StorefrontEvents.handleCronTick(context) + assert.ok(handledCronTickResBeforeFinalReceipt.ok) + assert.equal(handledCronTickResBeforeFinalReceipt.ok?.updatedCount, 0) + assert.equal( + handledCronTickResBeforeFinalReceipt.ok?.pendingCount, + pieces.length + ) + + // Store all invocations and receipts, except for the very last receipt on the chain + const storeLastReceipt = await storeInvocationsAndReceipts({ + invocations: {}, + receipts: { + aggregateAcceptReceipt: receipts.aggregateAcceptReceipt, + }, + taskStore: context.taskStore, + receiptStore: context.receiptStore, + }) + assert.ok(storeLastReceipt.ok) + + // Cron ticks with one deal for the first piece + const handledCronTickResAfterFinalReceipt = + await StorefrontEvents.handleCronTick(context) + assert.ok(handledCronTickResAfterFinalReceipt.ok) + assert.equal(handledCronTickResAfterFinalReceipt.ok?.updatedCount, 1) + assert.equal( + handledCronTickResAfterFinalReceipt.ok?.pendingCount, + pieces.length - 1 + ) + }, + 'handles cron tick error attempting to find pieces to track': + wichMockableContext( + async (assert, context) => { + // Cron ticks with no deals or receipts still available + const handledCronTickResBeforeAnyReceipt = + await StorefrontEvents.handleCronTick(context) + assert.ok(handledCronTickResBeforeAnyReceipt.error) + assert.equal( + handledCronTickResBeforeAnyReceipt.error?.name, + StoreOperationErrorName + ) + }, + async (context) => ({ + ...context, + pieceStore: getStoreImplementations(FailingStore).storefront.pieceStore, + }) + ), +} + +/** + * @param {API.Test} testFn + * @param {(context: TestAPI.StorefrontTestEventsContext) => Promise} mockContextFunction + */ +function wichMockableContext(testFn, mockContextFunction) { + // @ts-ignore + return async function (...args) { + const modifiedArgs = [args[0], await mockContextFunction(args[1])] + // @ts-ignore + return testFn(...modifiedArgs) + } +} + +async function getServiceContext() { + const dealer = await Signer.generate() + + return { dealer } +} + +/** + * @param {object} context + * @param {Record} context.invocations + * @param {Record} context.receipts + * @param {API.Store} context.taskStore + * @param {API.Store} context.receiptStore + */ +async function storeInvocationsAndReceipts({ + invocations, + receipts, + taskStore, + receiptStore, +}) { + // Store invocations + const storedInvocations = await Promise.all( + Object.values(invocations).map((invocation) => { + return taskStore.put(invocation) + }) + ) + if (storedInvocations.find((si) => si.error)) { + throw new Error('failed to store test invocations') + } + // Store receipts + const storedReceipts = await Promise.all( + Object.values(receipts).map((receipt) => { + return receiptStore.put(receipt) + }) + ) + if (storedReceipts.find((si) => si.error)) { + throw new Error('failed to store test receipts') + } + + return { + ok: {}, + } +} diff --git a/packages/filecoin-api/test/lib.js b/packages/filecoin-api/test/lib.js index 0ff2d6c31..d9f015187 100644 --- a/packages/filecoin-api/test/lib.js +++ b/packages/filecoin-api/test/lib.js @@ -1,12 +1,14 @@ -import * as Aggregator from './services/aggregator.js' -import * as Dealer from './services/dealer.js' -import * as Storefront from './services/storefront.js' +import * as AggregatorService from './services/aggregator.js' +import * as DealerService from './services/dealer.js' +import * as StorefrontService from './services/storefront.js' + export * from './utils.js' export const test = { - ...Aggregator.test, - ...Dealer.test, - ...Storefront.test, + service: { + ...AggregatorService.test, + ...DealerService.test, + ...StorefrontService.test, + }, + events: {}, } - -export { Aggregator, Dealer, Storefront } diff --git a/packages/filecoin-api/test/services/aggregator.js b/packages/filecoin-api/test/services/aggregator.js index 76c04bff7..61432e077 100644 --- a/packages/filecoin-api/test/services/aggregator.js +++ b/packages/filecoin-api/test/services/aggregator.js @@ -1,75 +1,226 @@ -import { Filecoin } from '@web3-storage/capabilities' +import { Aggregator } from '@web3-storage/capabilities' +import * as DealerCaps from '@web3-storage/capabilities/filecoin/dealer' import * as Signer from '@ucanto/principal/ed25519' +import { CBOR } from '@ucanto/core' import pWaitFor from 'p-wait-for' import * as API from '../../src/types.js' +import * as AggregatorApi from '../../src/aggregator/api.js' -import { randomCargo } from '../utils.js' -import { createServer, connect } from '../../src/aggregator.js' +import { createServer, connect } from '../../src/aggregator/service.js' +import { randomAggregate, randomCargo } from '../utils.js' +import { FailingStore } from '../context/store.js' +import { FailingQueue } from '../context/queue.js' +import { getStoreImplementations } from '../context/store-implementations.js' +import { + QueueOperationErrorName, + StoreOperationErrorName, +} from '../../src/errors.js' /** - * @type {API.Tests} + * @typedef {import('@web3-storage/data-segment').PieceLink} PieceLink + * @typedef {import('@ucanto/interface').Link} Link + * @typedef {import('../../src/aggregator/api.js').PieceRecord} PieceRecord + * @typedef {import('../../src/aggregator/api.js').PieceRecordKey} PieceRecordKey + * @typedef {import('../../src/aggregator/api.js').BufferRecord} BufferRecord + * @typedef {import('../../src/aggregator/api.js').AggregateRecord} AggregateRecord + * @typedef {import('../../src/aggregator/api.js').AggregateRecordKey} AggregateRecordKey + * @typedef {import('../../src/aggregator/api.js').InclusionRecord} InclusionRecord + * @typedef {import('../../src/aggregator/api.js').InclusionRecordKey} InclusionRecordKey + */ + +/** + * @type {API.Tests} */ export const test = { - 'piece/queue inserts piece into processing queue': async ( - assert, - context - ) => { - const { storefront } = await getServiceContext() - const connection = connect({ - id: context.id, - channel: createServer(context), - }) + 'piece/offer inserts piece into piece queue if not in piece store and returns effects': + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) - // Generate piece for test - const [cargo] = await randomCargo(1, 128) - const group = 'did:web:free.web3.storage' + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + const group = 'did:web:free.web3.storage' - // storefront invocation - const pieceAddInv = Filecoin.aggregateQueue.invoke({ - issuer: storefront, - audience: connection.id, - with: storefront.did(), - nb: { + // storefront invocation + const pieceAddInv = Aggregator.pieceOffer.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + piece: cargo.link.link(), + group, + }, + }) + + const response = await pieceAddInv.execute(connection) + if (response.out.error) { + throw new Error('invocation failed', { cause: response.out.error }) + } + assert.ok(response.out.ok) + assert.ok(response.out.ok.piece.equals(cargo.link.link())) + + // Validate effect in receipt + const fxJoin = await Aggregator.pieceAccept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece: cargo.link.link(), + group, + }, + expiration: Infinity, + }) + .delegate() + + assert.ok(response.fx.join) + assert.ok(fxJoin.link().equals(response.fx.join?.link())) + + // Validate queue and store + await pWaitFor( + () => context.queuedMessages.get('pieceQueue')?.length === 1 + ) + + // Piece not yet stored + const hasStoredPiece = await context.pieceStore.get({ piece: cargo.link.link(), group, - }, - }) + }) + assert.ok(!hasStoredPiece.ok) + }, + 'piece/offer dedupes piece and returns effects without propagating message': + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) - const response = await pieceAddInv.execute(connection) - if (response.out.error) { - throw new Error('invocation failed', { cause: response.out.error }) - } - assert.ok(response.out.ok) - assert.ok(response.out.ok.piece.equals(cargo.link.link())) + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + const group = 'did:web:free.web3.storage' - // Validate effect in receipt - const fx = await Filecoin.aggregateAdd - .invoke({ - issuer: context.id, - audience: context.id, - with: context.id.did(), + // Store piece into store + const putRes = await context.pieceStore.put({ + piece: cargo.link.link(), + group: context.id.did(), + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + assert.ok(putRes.ok) + + // storefront invocation + const pieceAddInv = Aggregator.pieceOffer.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), nb: { piece: cargo.link.link(), - storefront: storefront.did(), group, }, }) - .delegate() - assert.ok(response.fx.join) - assert.ok(fx.link().equals(response.fx.join?.link())) + const response = await pieceAddInv.execute(connection) + if (response.out.error) { + throw new Error('invocation failed', { cause: response.out.error }) + } + assert.ok(response.out.ok) + assert.ok(response.out.ok.piece.equals(cargo.link.link())) + + // Validate effect in receipt + const fxJoin = await Aggregator.pieceAccept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece: cargo.link.link(), + group, + }, + expiration: Infinity, + }) + .delegate() + + assert.ok(response.fx.join) + assert.ok(fxJoin.link().equals(response.fx.join?.link())) + + // Validate queue has no new message + await pWaitFor( + () => context.queuedMessages.get('pieceQueue')?.length === 0 + ) + }, + 'piece/offer fails if not able to verify piece store': wichMockableContext( + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + const group = 'did:web:free.web3.storage' - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 1) + // storefront invocation + const pieceAddInv = Aggregator.pieceOffer.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + piece: cargo.link.link(), + group, + }, + }) - const hasStoredPiece = await context.pieceStore.get({ - piece: cargo.link.link(), - storefront: storefront.did(), + const response = await pieceAddInv.execute(connection) + assert.ok(response.out.error) + // @ts-ignore + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + pieceStore: getStoreImplementations(FailingStore).aggregator.pieceStore, }) - assert.ok(!hasStoredPiece.ok) - }, - 'piece/add from signer inserts piece into store and returns accepted': async ( + ), + 'piece/offer fails if not able to add to piece queue': wichMockableContext( + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + const group = 'did:web:free.web3.storage' + + // storefront invocation + const pieceAddInv = Aggregator.pieceOffer.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + piece: cargo.link.link(), + group, + }, + }) + + const response = await pieceAddInv.execute(connection) + assert.ok(response.out.error) + // @ts-ignore + assert.equal(response.out.error?.name, QueueOperationErrorName) + }, + (context) => ({ + ...context, + pieceQueue: new FailingQueue(), + }) + ), + 'piece/accept issues receipt with data aggregation proof': async ( assert, context ) => { @@ -80,80 +231,176 @@ export const test = { }) // Generate piece for test - const [cargo] = await randomCargo(1, 128) - const group = 'did:web:free.web3.storage' + const group = storefront.did() + const { pieces, aggregate } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // Store aggregate record into store + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + const aggregatePutRes = await context.aggregateStore.put({ + aggregate: aggregate.link, + pieces: piecesBlock.cid, + group, + insertedAt: new Date().toISOString(), + }) + assert.ok(aggregatePutRes.ok) + + // compute proof for piece in aggregate + const proof = aggregate.resolveProof(piece) + if (proof.error) { + throw new Error('could not compute proof') + } + + // Store inclusion record into store + const inclusionPutRes = await context.inclusionStore.put({ + piece, + aggregate: aggregate.link, + group, + inclusion: { + subtree: proof.ok[0], + index: proof.ok[1], + }, + insertedAt: new Date().toISOString(), + }) + assert.ok(inclusionPutRes.ok) - // aggregator invocation - const pieceAddInv = Filecoin.aggregateAdd.invoke({ - issuer: context.id, + // storefront invocation + const pieceAcceptInv = Aggregator.pieceAccept.invoke({ + issuer: storefront, audience: connection.id, - with: context.id.did(), + with: storefront.did(), nb: { - piece: cargo.link.link(), - storefront: storefront.did(), + piece, group, }, }) - const response = await pieceAddInv.execute(connection) + const response = await pieceAcceptInv.execute(connection) if (response.out.error) { throw new Error('invocation failed', { cause: response.out.error }) } + // Validate receipt assert.ok(response.out.ok) - assert.ok(response.out.ok.piece.equals(cargo.link.link())) - - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 0) + assert.ok(response.out.ok.piece.equals(piece.link())) + assert.ok(response.out.ok.aggregate.equals(aggregate.link)) + assert.equal( + BigInt(response.out.ok.inclusion.subtree[0]), + BigInt(proof.ok[0][0]) + ) + assert.deepEqual(response.out.ok.inclusion.subtree[1], proof.ok[0][1]) + assert.equal( + BigInt(response.out.ok.inclusion.index[0]), + BigInt(proof.ok[1][0]) + ) + assert.deepEqual(response.out.ok.inclusion.index[1], proof.ok[1][1]) - const hasStoredPiece = await context.pieceStore.get({ - piece: cargo.link.link(), - storefront: storefront.did(), - }) - assert.ok(hasStoredPiece.ok) - assert.ok(hasStoredPiece.ok?.piece.equals(cargo.link.link())) - assert.deepEqual(hasStoredPiece.ok?.group, group) - assert.deepEqual(hasStoredPiece.ok?.storefront, storefront.did()) - }, - 'skip piece/add from signer inserts piece into store and returns rejected': - async (assert, context) => { - const { storefront } = await getServiceContext() - const connection = connect({ - id: context.id, - channel: createServer(context), - }) - - // Generate piece for test - const [cargo] = await randomCargo(1, 128) - const group = 'did:web:free.web3.storage' - - // aggregator invocation - const pieceAddInv = Filecoin.aggregateAdd.invoke({ + // Validate effect in receipt + const fxJoin = await DealerCaps.aggregateOffer + .invoke({ issuer: context.id, - audience: connection.id, + audience: context.dealerId, with: context.id.did(), nb: { - piece: cargo.link.link(), - storefront: storefront.did(), - group, + aggregate: aggregate.link, + pieces: piecesBlock.cid, }, }) + .delegate() + assert.ok(response.fx.join) + assert.ok(fxJoin.link().equals(response.fx.join?.link())) + }, + 'piece/accept fails if not able to query inclusion store': + wichMockableContext( + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) - const response = await pieceAddInv.execute(connection) - if (response.out.error) { - throw new Error('invocation failed', { cause: response.out.error }) - } - assert.ok(response.out.ok) - assert.ok(response.out.ok.piece.equals(cargo.link.link())) + // Generate piece for test + const group = storefront.did() + const { pieces } = await randomAggregate(100, 128) + const piece = pieces[0].link - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 0) + // storefront invocation + const pieceAcceptInv = Aggregator.pieceAccept.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + piece, + group, + }, + }) - const hasStoredPiece = await context.pieceStore.get({ - piece: cargo.link.link(), - storefront: storefront.did(), + const response = await pieceAcceptInv.execute(connection) + // Validate receipt + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + inclusionStore: + getStoreImplementations(FailingStore).aggregator.inclusionStore, }) - assert.ok(!hasStoredPiece.ok) - }, + ), + 'piece/accept fails if not able to read from aggregate store': + wichMockableContext( + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const group = storefront.did() + const { pieces, aggregate } = await randomAggregate(100, 128) + const piece = pieces[0].link + + // compute proof for piece in aggregate + const proof = aggregate.resolveProof(piece) + if (proof.error) { + throw new Error('could not compute proof') + } + + // Store inclusion record into store + const inclusionPutRes = await context.inclusionStore.put({ + piece, + aggregate: aggregate.link, + group, + inclusion: { + subtree: proof.ok[0], + index: proof.ok[1], + }, + insertedAt: new Date().toISOString(), + }) + assert.ok(inclusionPutRes.ok) + + // storefront invocation + const pieceAcceptInv = Aggregator.pieceAccept.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + piece, + group, + }, + }) + + const response = await pieceAcceptInv.execute(connection) + // Validate receipt + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + aggregateStore: + getStoreImplementations(FailingStore).aggregator.aggregateStore, + }) + ), } async function getServiceContext() { @@ -161,3 +408,16 @@ async function getServiceContext() { return { storefront } } + +/** + * @param {API.Test} testFn + * @param {(context: AggregatorApi.ServiceContext) => AggregatorApi.ServiceContext} mockContextFunction + */ +function wichMockableContext(testFn, mockContextFunction) { + // @ts-ignore + return function (...args) { + const modifiedArgs = [args[0], mockContextFunction(args[1])] + // @ts-ignore + return testFn(...modifiedArgs) + } +} diff --git a/packages/filecoin-api/test/services/deal-tracker.js b/packages/filecoin-api/test/services/deal-tracker.js new file mode 100644 index 000000000..bd059b485 --- /dev/null +++ b/packages/filecoin-api/test/services/deal-tracker.js @@ -0,0 +1,143 @@ +import { DealTracker } from '@web3-storage/capabilities' +import * as Signer from '@ucanto/principal/ed25519' + +import * as API from '../../src/types.js' +import * as DealTrackerApi from '../../src/deal-tracker/api.js' + +import { createServer, connect } from '../../src/deal-tracker/service.js' +import { randomCargo } from '../utils.js' +import { FailingStore } from '../context/store.js' +import { getStoreImplementations } from '../context/store-implementations.js' +import { StoreOperationErrorName } from '../../src/errors.js' + +/** + * @typedef {import('../../src/deal-tracker/api.js').DealRecord} DealRecord + * @typedef {import('../../src/deal-tracker/api.js').DealRecordKey} DealRecordKey + */ + +/** + * @type {API.Tests} + */ +export const test = { + 'deal/info fails to get info for non existent piece CID': async ( + assert, + context + ) => { + const { dealer } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // dealer invocation + const dealInfoInv = DealTracker.dealInfo.invoke({ + issuer: dealer, + audience: connection.id, + with: dealer.did(), + nb: { + piece: cargo.link.link(), + }, + }) + + const response = await dealInfoInv.execute(connection) + assert.ok(response.out) + assert.deepEqual(response.out.ok?.deals, {}) + }, + 'deal/info retrieves available deals for aggregate piece CID': async ( + assert, + context + ) => { + const { dealer } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + const dealIds = [111, 222] + await Promise.all( + dealIds.map(async (dealId) => { + const dealPutRes = await context.dealStore.put({ + piece: cargo.link, + dealId, + provider: `f0${dealId}`, + expirationEpoch: Date.now() + 10e9, + source: 'cargo.dag.haus', + insertedAt: new Date().toISOString(), + }) + + assert.ok(dealPutRes.ok) + }) + ) + + // dealer invocation + const dealInfoInv = DealTracker.dealInfo.invoke({ + issuer: dealer, + audience: connection.id, + with: dealer.did(), + nb: { + piece: cargo.link.link(), + }, + }) + + const response = await dealInfoInv.execute(connection) + assert.ok(response.out.ok) + for (const dealId of dealIds) { + assert.ok(response.out.ok?.deals[`${dealId}`]) + assert.equal(response.out.ok?.deals[`${dealId}`].provider, `f0${dealId}`) + } + }, + 'deal/info fails if not able to query deal store': wichMockableContext( + async (assert, context) => { + const { dealer } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // dealer invocation + const dealInfoInv = DealTracker.dealInfo.invoke({ + issuer: dealer, + audience: connection.id, + with: dealer.did(), + nb: { + piece: cargo.link.link(), + }, + }) + + const response = await dealInfoInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + dealStore: getStoreImplementations(FailingStore).dealTracker.dealStore, + }) + ), +} + +async function getServiceContext() { + const dealer = await Signer.generate() + + return { dealer } +} + +/** + * @param {API.Test} testFn + * @param {(context: DealTrackerApi.ServiceContext) => DealTrackerApi.ServiceContext} mockContextFunction + */ +function wichMockableContext(testFn, mockContextFunction) { + // @ts-ignore + return function (...args) { + const modifiedArgs = [args[0], mockContextFunction(args[1])] + // @ts-ignore + return testFn(...modifiedArgs) + } +} diff --git a/packages/filecoin-api/test/services/dealer.js b/packages/filecoin-api/test/services/dealer.js index 36125d61e..e025b62d9 100644 --- a/packages/filecoin-api/test/services/dealer.js +++ b/packages/filecoin-api/test/services/dealer.js @@ -1,23 +1,28 @@ -import { Filecoin } from '@web3-storage/capabilities' +import { Dealer } from '@web3-storage/capabilities' import * as Signer from '@ucanto/principal/ed25519' -import pWaitFor from 'p-wait-for' import { CBOR } from '@ucanto/core' import * as API from '../../src/types.js' +import * as DealerApi from '../../src/dealer/api.js' +import { createServer, connect } from '../../src/dealer/service.js' import { randomAggregate } from '../utils.js' -import { createServer, connect } from '../../src/dealer.js' +import { FailingStore } from '../context/store.js' +import { getStoreImplementations } from '../context/store-implementations.js' +import { StoreOperationErrorName } from '../../src/errors.js' /** - * @type {API.Tests} + * @typedef {import('../../src/dealer/api.js').AggregateRecord} AggregateRecord + * @typedef {import('../../src/dealer/api.js').AggregateRecordKey} AggregateRecordKey + * @typedef {import('../../src/dealer/api.js').OfferDocument} OfferDocument + */ + +/** + * @type {API.Tests} */ export const test = { - 'aggregate/queue inserts piece into processing queue': async ( - assert, - context - ) => { - const { aggregator, storefront: storefrontSigner } = - await getServiceContext() + 'aggregate/offer inserts aggregate into stores': async (assert, context) => { + const { storefront } = await getServiceContext() const connection = connect({ id: context.id, channel: createServer(context), @@ -27,19 +32,15 @@ export const test = { const { pieces, aggregate } = await randomAggregate(100, 128) const offer = pieces.map((p) => p.link) const piecesBlock = await CBOR.write(offer) - const storefront = storefrontSigner.did() - const label = 'label' // aggregator invocation - const pieceAddInv = Filecoin.dealQueue.invoke({ - issuer: aggregator, + const pieceAddInv = Dealer.aggregateOffer.invoke({ + issuer: storefront, audience: connection.id, - with: aggregator.did(), + with: storefront.did(), nb: { aggregate: aggregate.link, pieces: piecesBlock.cid, - storefront, - label, }, }) pieceAddInv.attach(piecesBlock) @@ -52,7 +53,7 @@ export const test = { assert.ok(response.out.ok.aggregate?.equals(aggregate.link)) // Validate effect in receipt - const fx = await Filecoin.dealAdd + const fxJoin = await Dealer.aggregateAccept .invoke({ issuer: context.id, audience: context.id, @@ -60,124 +61,216 @@ export const test = { nb: { aggregate: aggregate.link, pieces: piecesBlock.cid, - storefront, - label, }, + expiration: Infinity, }) .delegate() assert.ok(response.fx.join) - assert.ok(fx.link().equals(response.fx.join?.link())) + assert.ok(fxJoin.link().equals(response.fx.join?.link())) - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 1) - - const hasStoredDeal = await context.dealStore.get({ - piece: aggregate.link.link(), + // Validate stores + const storedDeal = await context.aggregateStore.get({ + aggregate: aggregate.link.link(), }) - assert.ok(!hasStoredDeal.ok) + assert.ok(storedDeal.ok) + assert.ok(storedDeal.ok?.aggregate.equals(aggregate.link.link())) + assert.ok(storedDeal.ok?.pieces.equals(piecesBlock.cid)) + assert.equal(storedDeal.ok?.status, 'offered') + assert.ok(storedDeal.ok?.insertedAt) + assert.ok(storedDeal.ok?.updatedAt) + // Still pending resolution + assert.ok(!storedDeal.ok?.deal) + + const storedOffer = await context.offerStore.get(piecesBlock.cid.toString()) + assert.ok(storedOffer.ok) + assert.ok(storedOffer.ok?.value.aggregate.equals(aggregate.link.link())) + assert.equal(storedOffer.ok?.value.issuer, storefront.did()) + assert.deepEqual( + storedOffer.ok?.value.pieces.map((p) => p.toString()), + offer.map((p) => p.toString()) + ) }, - 'aggregate/add from signer inserts piece into store and returns accepted': - async (assert, context) => { - const { storefront: storefrontSigner } = await getServiceContext() - const connection = connect({ - id: context.id, - channel: createServer(context), - }) + 'aggregate/offer fails if not able to check aggregate store': + wichMockableContext( + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) - // Generate piece for test - const { pieces, aggregate } = await randomAggregate(100, 128) - const offer = pieces.map((p) => p.link) - const piecesBlock = await CBOR.write(offer) - const storefront = storefrontSigner.did() - const label = 'label' + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) - await context.dealStore.put({ - aggregate: aggregate.link, - offer: aggregate.link.toString(), - storefront, - stat: 1, - insertedAt: Date.now(), - }) + // aggregator invocation + const pieceAddInv = Dealer.aggregateOffer.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + aggregate: aggregate.link, + pieces: piecesBlock.cid, + }, + }) + pieceAddInv.attach(piecesBlock) - // aggregator invocation - const pieceAddInv = Filecoin.dealAdd.invoke({ - issuer: context.id, - audience: connection.id, - with: context.id.did(), - nb: { - aggregate: aggregate.link, - pieces: piecesBlock.cid, - storefront, - label, - }, + const response = await pieceAddInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + aggregateStore: + getStoreImplementations(FailingStore).dealer.aggregateStore, }) - pieceAddInv.attach(piecesBlock) + ), + 'aggregate/offer fails if not able to put to offer store': + wichMockableContext( + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) - const response = await pieceAddInv.execute(connection) - if (response.out.error) { - throw new Error('invocation failed', { cause: response.out.error }) - } - assert.ok(response.out.ok) - assert.ok(response.out.ok.aggregate?.equals(aggregate.link)) + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 0) + // aggregator invocation + const pieceAddInv = Dealer.aggregateOffer.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + aggregate: aggregate.link, + pieces: piecesBlock.cid, + }, + }) + pieceAddInv.attach(piecesBlock) - const hasStoredDeal = await context.dealStore.get({ - aggregate: aggregate.link.link(), - }) - assert.ok(hasStoredDeal.ok) - }, - 'skip aggregate/add from signer inserts piece into store and returns rejected': - async (assert, context) => { - const { storefront: storefrontSigner } = await getServiceContext() - const connection = connect({ - id: context.id, - channel: createServer(context), + const response = await pieceAddInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + offerStore: getStoreImplementations(FailingStore).dealer.offerStore, }) + ), + 'aggregate/accept issues receipt with data aggregation proof': async ( + assert, + context + ) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) - // Generate piece for test - const { pieces, aggregate } = await randomAggregate(100, 128) - const offer = pieces.map((p) => p.link) - const piecesBlock = await CBOR.write(offer) - const storefront = storefrontSigner.did() - const label = 'label' + // Set aggregate with deal + const deal = { + dataType: 0n, + dataSource: { + dealID: 100n, + }, + } + const putRes = await context.aggregateStore.put({ + aggregate: aggregate.link, + pieces: piecesBlock.cid, + status: 'offered', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + deal, + }) + assert.ok(putRes.ok) - // aggregator invocation - const pieceAddInv = Filecoin.dealAdd.invoke({ - issuer: context.id, - audience: connection.id, - with: context.id.did(), - nb: { - aggregate: aggregate.link, - pieces: piecesBlock.cid, - storefront, - label, - }, - }) - pieceAddInv.attach(piecesBlock) + // aggregator invocation + const pieceAddInv = Dealer.aggregateAccept.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + aggregate: aggregate.link, + pieces: piecesBlock.cid, + }, + }) + pieceAddInv.attach(piecesBlock) - const response = await pieceAddInv.execute(connection) - if (response.out.error) { - throw new Error('invocation failed', { cause: response.out.error }) - } - assert.ok(response.out.ok) - assert.deepEqual(response.out.ok.aggregate, aggregate.link) + const response = await pieceAddInv.execute(connection) + if (response.out.error) { + throw new Error('invocation failed', { cause: response.out.error }) + } + assert.ok(response.out.ok) + assert.equal( + BigInt(response.out.ok.dataSource.dealID), + BigInt(deal.dataSource.dealID) + ) + assert.equal(BigInt(response.out.ok.dataType), BigInt(deal.dataType)) + }, + 'aggregate/accept fails if not able to read from aggregate store': + wichMockableContext( + async (assert, context) => { + const { storefront } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const { pieces, aggregate } = await randomAggregate(100, 128) + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 0) + // aggregator invocation + const pieceAddInv = Dealer.aggregateAccept.invoke({ + issuer: storefront, + audience: connection.id, + with: storefront.did(), + nb: { + aggregate: aggregate.link, + pieces: piecesBlock.cid, + }, + }) + pieceAddInv.attach(piecesBlock) - const hasStoredDeal = await context.dealStore.get({ - aggregate: aggregate.link.link(), + const response = await pieceAddInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + aggregateStore: + getStoreImplementations(FailingStore).dealer.aggregateStore, }) - assert.ok(!hasStoredDeal.ok) - }, + ), } async function getServiceContext() { - const aggregator = await Signer.generate() + const dealer = await Signer.generate() const storefront = await Signer.generate() - return { aggregator, storefront } + return { dealer, storefront } +} + +/** + * @param {API.Test} testFn + * @param {(context: DealerApi.ServiceContext) => DealerApi.ServiceContext} mockContextFunction + */ +function wichMockableContext(testFn, mockContextFunction) { + // @ts-ignore + return function (...args) { + const modifiedArgs = [args[0], mockContextFunction(args[1])] + // @ts-ignore + return testFn(...modifiedArgs) + } } diff --git a/packages/filecoin-api/test/services/storefront.js b/packages/filecoin-api/test/services/storefront.js index 2c9737b66..928deac62 100644 --- a/packages/filecoin-api/test/services/storefront.js +++ b/packages/filecoin-api/test/services/storefront.js @@ -1,73 +1,105 @@ -import { Filecoin } from '@web3-storage/capabilities' +import { Filecoin, Aggregator } from '@web3-storage/capabilities' +import { CBOR } from '@ucanto/core' import * as Signer from '@ucanto/principal/ed25519' import pWaitFor from 'p-wait-for' import * as API from '../../src/types.js' +import * as StorefrontApi from '../../src/storefront/api.js' -import { randomCargo } from '../utils.js' -import { createServer, connect } from '../../src/storefront.js' +import { createServer, connect } from '../../src/storefront/service.js' +import { + QueueOperationErrorName, + StoreOperationErrorName, +} from '../../src/errors.js' +import { randomCargo, randomAggregate } from '../utils.js' +import { createInvocationsAndReceiptsForDealDataProofChain } from '../context/receipts.js' +import { getStoreImplementations } from '../context/store-implementations.js' +import { FailingStore } from '../context/store.js' +import { FailingQueue } from '../context/queue.js' /** - * @type {API.Tests} + * @typedef {import('../../src/storefront/api.js').PieceRecord} PieceRecord + * @typedef {import('../../src/storefront/api.js').PieceRecordKey} PieceRecordKey */ -export const test = { - 'filecoin/queue inserts piece into verification queue': async ( - assert, - context - ) => { - const { agent } = await getServiceContext() - const connection = connect({ - id: context.id, - channel: createServer(context), - }) - // Generate piece for test - const [cargo] = await randomCargo(1, 128) +/** + * @type {API.Tests} + */ +export const test = { + 'filecoin/offer inserts piece into submission queue if not in piece store and returns effects': + async (assert, context) => { + const { agent } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) - // agent invocation - const filecoinAddInv = Filecoin.filecoinQueue.invoke({ - issuer: agent, - audience: connection.id, - with: agent.did(), - nb: { - piece: cargo.link.link(), - content: cargo.content.link(), - }, - }) + // Generate piece for test + const [cargo] = await randomCargo(1, 128) - const response = await filecoinAddInv.execute(connection) - if (response.out.error) { - throw new Error('invocation failed', { cause: response.out.error }) - } - assert.ok(response.out.ok) - assert.ok(response.out.ok.piece.equals(cargo.link.link())) - - // Validate effect in receipt - const fx = await Filecoin.filecoinAdd - .invoke({ - issuer: context.id, - audience: context.id, - with: context.id.did(), + // agent invocation + const filecoinAddInv = Filecoin.offer.invoke({ + issuer: agent, + audience: connection.id, + with: agent.did(), nb: { piece: cargo.link.link(), content: cargo.content.link(), }, }) - .delegate() - assert.ok(response.fx.join) - assert.ok(fx.link().equals(response.fx.join?.link())) + const response = await filecoinAddInv.execute(connection) + if (response.out.error) { + throw new Error('invocation failed', { cause: response.out.error }) + } + assert.ok(response.out.ok) + assert.ok(response.out.ok.piece.equals(cargo.link.link())) - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 1) + // Validate effects in receipt + const fxFork = await Filecoin.submit + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + expiration: Infinity, + }) + .delegate() + const fxJoin = await Filecoin.accept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + expiration: Infinity, + }) + .delegate() - const hasStoredPiece = await context.pieceStore.get({ - piece: cargo.link.link(), - }) - assert.ok(!hasStoredPiece.ok) - }, - 'filecoin/add from signer inserts piece into store and returns accepted': + assert.ok(response.fx.join) + assert.ok(fxJoin.link().equals(response.fx.join?.link())) + assert.equal(response.fx.fork.length, 1) + assert.ok(fxFork.link().equals(response.fx.fork[0].link())) + + // Validate queue and store + await pWaitFor( + () => context.queuedMessages.get('filecoinSubmitQueue')?.length === 1 + ) + + // Piece not yet stored + const hasStoredPiece = await context.pieceStore.get({ + piece: cargo.link.link(), + }) + assert.ok(!hasStoredPiece.ok) + }, + 'filecoin/offer dedupes piece and returns effects without propagating message': async (assert, context) => { + const { agent } = await getServiceContext() const connection = connect({ id: context.id, channel: createServer(context), @@ -76,11 +108,22 @@ export const test = { // Generate piece for test const [cargo] = await randomCargo(1, 128) - // storefront invocation - const filecoinAddInv = Filecoin.filecoinAdd.invoke({ - issuer: context.id, + // Store piece into store + const putRes = await context.pieceStore.put({ + piece: cargo.link.link(), + content: cargo.content.link(), + group: context.id.did(), + status: 'submitted', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + assert.ok(putRes.ok) + + // agent invocation + const filecoinAddInv = Filecoin.offer.invoke({ + issuer: agent, audience: connection.id, - with: context.id.did(), + with: agent.did(), nb: { piece: cargo.link.link(), content: cargo.content.link(), @@ -94,16 +137,109 @@ export const test = { assert.ok(response.out.ok) assert.ok(response.out.ok.piece.equals(cargo.link.link())) - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 0) + // Validate effects in receipt + const fxFork = await Filecoin.submit + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + expiration: Infinity, + }) + .delegate() + const fxJoin = await Filecoin.accept + .invoke({ + issuer: context.id, + audience: context.id, + with: context.id.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + expiration: Infinity, + }) + .delegate() - const hasStoredPiece = await context.pieceStore.get({ - piece: cargo.link.link(), - }) - assert.ok(hasStoredPiece.ok) + assert.ok(response.fx.join) + assert.ok(fxJoin.link().equals(response.fx.join?.link())) + assert.equal(response.fx.fork.length, 1) + assert.ok(fxFork.link().equals(response.fx.fork[0].link())) + + // Validate queue has no new message + await pWaitFor( + () => context.queuedMessages.get('filecoinSubmitQueue')?.length === 0 + ) }, - 'skip filecoin/add from signer inserts piece into store and returns rejected': + 'filecoin/offer invocation fails if fails to write to submission queue': + wichMockableContext( + async (assert, context) => { + const { agent } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // agent invocation + const filecoinAddInv = Filecoin.offer.invoke({ + issuer: agent, + audience: connection.id, + with: agent.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + }) + + const response = await filecoinAddInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, QueueOperationErrorName) + }, + (context) => ({ + ...context, + filecoinSubmitQueue: new FailingQueue(), + }) + ), + 'filecoin/offer invocation fails if fails to check piece store': + wichMockableContext( + async (assert, context) => { + const { agent } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + + // agent invocation + const filecoinAddInv = Filecoin.offer.invoke({ + issuer: agent, + audience: connection.id, + with: agent.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + }) + + const response = await filecoinAddInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + pieceStore: getStoreImplementations(FailingStore).storefront.pieceStore, + }) + ), + 'filecoin/submit inserts piece into piece offer queue and returns effect': async (assert, context) => { + const { agent } = await getServiceContext() const connection = connect({ id: context.id, channel: createServer(context), @@ -111,37 +247,267 @@ export const test = { // Generate piece for test const [cargo] = await randomCargo(1, 128) - - // storefront invocation - const filecoinAddInv = Filecoin.filecoinAdd.invoke({ - issuer: context.id, + const filecoinSubmitInv = Filecoin.submit.invoke({ + issuer: agent, audience: connection.id, - with: context.id.did(), + with: agent.did(), nb: { piece: cargo.link.link(), content: cargo.content.link(), }, }) - const response = await filecoinAddInv.execute(connection) + const response = await filecoinSubmitInv.execute(connection) if (response.out.error) { throw new Error('invocation failed', { cause: response.out.error }) } assert.ok(response.out.ok) assert.ok(response.out.ok.piece.equals(cargo.link.link())) - // Validate queue and store - await pWaitFor(() => context.queuedMessages.length === 0) + // Validate effects in receipt + const fxJoin = await Aggregator.pieceOffer + .invoke({ + issuer: context.id, + audience: context.aggregatorId, + with: context.id.did(), + nb: { + piece: cargo.link.link(), + group: context.id.did(), + }, + expiration: Infinity, + }) + .delegate() - const hasStoredPiece = await context.pieceStore.get({ - piece: cargo.link.link(), - }) - assert.ok(!hasStoredPiece.ok) + assert.ok(response.fx.join) + assert.ok(fxJoin.link().equals(response.fx.join?.link())) }, + 'filecoin/submit fails if fails to write to submission queue': + wichMockableContext( + async (assert, context) => { + const { agent } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + const filecoinSubmitInv = Filecoin.submit.invoke({ + issuer: agent, + audience: connection.id, + with: agent.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + }) + + const response = await filecoinSubmitInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, QueueOperationErrorName) + }, + (context) => ({ + ...context, + pieceOfferQueue: new FailingQueue(), + }) + ), + 'filecoin/accept issues receipt with data aggregation proof': async ( + assert, + context + ) => { + const { agent, aggregator, dealer } = await getServiceContext() + const group = context.id.did() + const connection = connect({ + id: context.id, + channel: createServer({ + ...context, + aggregatorId: aggregator, + }), + }) + + // Create piece and aggregate for test + const { aggregate, pieces } = await randomAggregate(10, 128) + const piece = pieces[0] + const offer = pieces.map((p) => p.link) + const piecesBlock = await CBOR.write(offer) + + // Store piece into store + const putRes = await context.pieceStore.put({ + piece: piece.link, + content: piece.content, + group: context.id.did(), + status: 'submitted', + insertedAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + assert.ok(putRes.ok) + + // Create inclusion proof for test + const inclusionProof = aggregate.resolveProof(piece.link) + if (inclusionProof.error) { + throw new Error('could not compute inclusion proof') + } + + // Create invocations and receipts for chain into DealDataProof + const dealMetadata = { + dataType: 0n, + dataSource: { + dealID: 100n, + }, + } + const { invocations, receipts } = + await createInvocationsAndReceiptsForDealDataProofChain({ + storefront: context.id, + aggregator, + dealer, + aggregate: aggregate.link, + group, + piece: piece.link, + piecesBlock, + inclusionProof: { + subtree: inclusionProof.ok[0], + index: inclusionProof.ok[1], + }, + aggregateAcceptStatus: { + ...dealMetadata, + aggregate: aggregate.link, + }, + }) + + const storedInvocationsAndReceiptsRes = await storeInvocationsAndReceipts({ + invocations, + receipts, + taskStore: context.taskStore, + receiptStore: context.receiptStore, + }) + assert.ok(storedInvocationsAndReceiptsRes.ok) + + const filecoinAddInv = Filecoin.accept.invoke({ + issuer: agent, + audience: connection.id, + with: agent.did(), + nb: { + piece: piece.link.link(), + content: piece.content.link(), + }, + }) + + const response = await filecoinAddInv.execute(connection) + if (response.out.error) { + throw new Error('invocation failed', { cause: response.out.error }) + } + assert.ok(response.out.ok) + assert.deepEqual( + response.out.ok.inclusion.subtree[1], + inclusionProof.ok[0][1] + ) + assert.deepEqual( + response.out.ok.inclusion.index[1], + inclusionProof.ok[1][1] + ) + assert.deepEqual( + BigInt(response.out.ok.inclusion.subtree[0]), + BigInt(inclusionProof.ok[0][0]) + ) + assert.deepEqual( + BigInt(response.out.ok.inclusion.index[0]), + BigInt(inclusionProof.ok[1][0]) + ) + assert.deepEqual( + BigInt(response.out.ok.aux.dataType), + BigInt(dealMetadata.dataType) + ) + assert.deepEqual( + BigInt(response.out.ok.aux.dataSource.dealID), + BigInt(dealMetadata.dataSource.dealID) + ) + }, + 'filecoin/accept fails if fails to read from piece store': + wichMockableContext( + async (assert, context) => { + const { agent } = await getServiceContext() + const connection = connect({ + id: context.id, + channel: createServer(context), + }) + + // Generate piece for test + const [cargo] = await randomCargo(1, 128) + const filecoinSubmitInv = Filecoin.accept.invoke({ + issuer: agent, + audience: connection.id, + with: agent.did(), + nb: { + piece: cargo.link.link(), + content: cargo.content.link(), + }, + }) + + const response = await filecoinSubmitInv.execute(connection) + assert.ok(response.out.error) + assert.equal(response.out.error?.name, StoreOperationErrorName) + }, + (context) => ({ + ...context, + pieceStore: getStoreImplementations(FailingStore).storefront.pieceStore, + }) + ), +} + +/** + * @param {object} context + * @param {Record} context.invocations + * @param {Record} context.receipts + * @param {API.Store} context.taskStore + * @param {API.Store} context.receiptStore + */ +async function storeInvocationsAndReceipts({ + invocations, + receipts, + taskStore, + receiptStore, +}) { + // Store invocations + const storedInvocations = await Promise.all( + Object.values(invocations).map((invocation) => { + return taskStore.put(invocation) + }) + ) + if (storedInvocations.find((si) => si.error)) { + throw new Error('failed to store test invocations') + } + // Store receipts + const storedReceipts = await Promise.all( + Object.values(receipts).map((receipt) => { + return receiptStore.put(receipt) + }) + ) + if (storedReceipts.find((si) => si.error)) { + throw new Error('failed to store test receipts') + } + + return { + ok: {}, + } } async function getServiceContext() { const agent = await Signer.generate() + const aggregator = await Signer.generate() + const dealer = await Signer.generate() - return { agent } + return { agent, dealer, aggregator } +} + +/** + * @param {API.Test} testFn + * @param {(context: StorefrontApi.ServiceContext) => StorefrontApi.ServiceContext} mockContextFunction + */ +function wichMockableContext(testFn, mockContextFunction) { + // @ts-ignore + return function (...args) { + const modifiedArgs = [args[0], mockContextFunction(args[1])] + // @ts-ignore + return testFn(...modifiedArgs) + } } diff --git a/packages/filecoin-api/test/storefront.spec.js b/packages/filecoin-api/test/storefront.spec.js index 9f563e0cf..64924e9d1 100644 --- a/packages/filecoin-api/test/storefront.spec.js +++ b/packages/filecoin-api/test/storefront.spec.js @@ -2,55 +2,140 @@ import * as assert from 'assert' import * as Signer from '@ucanto/principal/ed25519' -import * as Storefront from './services/storefront.js' +import * as StorefrontService from './services/storefront.js' +import * as StorefrontEvents from './events/storefront.js' -import { Store } from './context/store.js' +import { getStoreImplementations } from './context/store-implementations.js' import { Queue } from './context/queue.js' +import { getMockService, getConnection } from './context/service.js' -describe('filecoin/*', () => { - for (const [name, test] of Object.entries(Storefront.test)) { - const define = name.startsWith('only ') - ? it.only - : name.startsWith('skip ') - ? it.skip - : it - - define(name, async () => { - const signer = await Signer.generate() - const id = signer.withDID('did:web:test.web3.storage') - - // resources - /** @type {unknown[]} */ - const queuedMessages = [] - const addQueue = new Queue({ - onMessage: (message) => queuedMessages.push(message), - }) - const pieceLookupFn = ( - /** @type {Iterable | ArrayLike} */ items, - /** @type {any} */ record - ) => { - return Array.from(items).find((i) => i.piece.equals(record.piece)) - } - const pieceStore = new Store(pieceLookupFn) - - await test( - { - equal: assert.strictEqual, - deepEqual: assert.deepStrictEqual, - ok: assert.ok, - }, - { - id, - errorReporter: { - catch(error) { - assert.fail(error) +describe('storefront', () => { + describe('filecoin/*', () => { + for (const [name, test] of Object.entries(StorefrontService.test)) { + const define = name.startsWith('only ') + ? it.only + : name.startsWith('skip ') + ? it.skip + : it + + define(name, async () => { + const storefrontSigner = await Signer.generate() + const aggregatorSigner = await Signer.generate() + + // resources + /** @type {Map} */ + const queuedMessages = new Map() + queuedMessages.set('filecoinSubmitQueue', []) + queuedMessages.set('pieceOfferQueue', []) + const filecoinSubmitQueue = new Queue({ + onMessage: (message) => { + const messages = queuedMessages.get('filecoinSubmitQueue') || [] + messages.push(message) + queuedMessages.set('filecoinSubmitQueue', messages) + }, + }) + const pieceOfferQueue = new Queue({ + onMessage: (message) => { + const messages = queuedMessages.get('pieceOfferQueue') || [] + messages.push(message) + queuedMessages.set('pieceOfferQueue', messages) + }, + }) + const { + storefront: { pieceStore, receiptStore, taskStore }, + } = getStoreImplementations() + + await test( + { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + ok: assert.ok, + }, + { + id: storefrontSigner, + aggregatorId: aggregatorSigner, + errorReporter: { + catch(error) { + assert.fail(error) + }, }, + pieceStore, + filecoinSubmitQueue, + pieceOfferQueue, + taskStore, + receiptStore, + queuedMessages, + } + ) + }) + } + }) + + describe('events', () => { + for (const [name, test] of Object.entries(StorefrontEvents.test)) { + const define = name.startsWith('only ') + ? it.only + : name.startsWith('skip ') + ? it.skip + : it + + define(name, async () => { + const storefrontSigner = await Signer.generate() + const aggregatorSigner = await Signer.generate() + + const service = getMockService() + const storefrontConnection = getConnection( + storefrontSigner, + service + ).connection + const aggregatorConnection = getConnection( + aggregatorSigner, + service + ).connection + + // context + const { + storefront: { pieceStore, taskStore, receiptStore }, + } = getStoreImplementations() + + await test( + { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + ok: assert.ok, }, - addQueue, - pieceStore, - queuedMessages, - } - ) - }) - } + { + id: storefrontSigner, + aggregatorId: aggregatorSigner, + pieceStore, + receiptStore, + taskStore, + storefrontService: { + connection: storefrontConnection, + invocationConfig: { + issuer: storefrontSigner, + with: storefrontSigner.did(), + audience: storefrontSigner, + }, + }, + aggregatorService: { + connection: aggregatorConnection, + invocationConfig: { + issuer: storefrontSigner, + with: storefrontSigner.did(), + audience: aggregatorSigner, + }, + }, + queuedMessages: new Map(), + service, + errorReporter: { + catch(error) { + assert.fail(error) + }, + }, + } + ) + }) + } + }) }) diff --git a/packages/filecoin-api/test/types.js b/packages/filecoin-api/test/types.js new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/filecoin-api/test/types.js @@ -0,0 +1 @@ +export {} diff --git a/packages/filecoin-api/test/types.ts b/packages/filecoin-api/test/types.ts new file mode 100644 index 000000000..03f9beb6e --- /dev/null +++ b/packages/filecoin-api/test/types.ts @@ -0,0 +1,50 @@ +import type { Signer } from '@ucanto/interface' +import * as AggregatorInterface from '../src/aggregator/api.js' +import * as DealerInterface from '../src/dealer/api.js' +import * as StorefrontInterface from '../src/storefront/api.js' + +export interface AggregatorTestEventsContext + extends AggregatorInterface.PieceMessageContext, + AggregatorInterface.PieceAcceptMessageContext, + AggregatorInterface.AggregateOfferMessageContext, + AggregatorInterface.PieceInsertEventContext, + AggregatorInterface.InclusionInsertEventToUpdateState, + AggregatorInterface.InclusionInsertEventToIssuePieceAccept, + AggregatorInterface.AggregateInsertEventToAggregateOfferContext, + AggregatorInterface.AggregateInsertEventToPieceAcceptQueueContext, + AggregatorInterface.BufferMessageContext { + id: Signer + service: Partial<{ + filecoin: Partial + piece: Partial + aggregate: Partial + deal: Partial + }> +} + +export interface DealerTestEventsContext + extends DealerInterface.AggregateInsertEventContext, + DealerInterface.AggregateUpdatedStatusEventContext, + DealerInterface.CronContext { + id: Signer + service: Partial<{ + filecoin: Partial + piece: Partial + aggregate: Partial + deal: Partial + }> +} + +export interface StorefrontTestEventsContext + extends StorefrontInterface.FilecoinSubmitMessageContext, + StorefrontInterface.PieceOfferMessageContext, + StorefrontInterface.StorefrontClientContext, + StorefrontInterface.CronContext { + id: Signer + service: Partial<{ + filecoin: Partial + piece: Partial + aggregate: Partial + deal: Partial + }> +} diff --git a/packages/filecoin-client/package.json b/packages/filecoin-client/package.json index 3f11fccae..706857d32 100644 --- a/packages/filecoin-client/package.json +++ b/packages/filecoin-client/package.json @@ -70,7 +70,7 @@ "@types/mocha": "^10.0.1", "@ucanto/principal": "^8.0.0", "@ucanto/server": "^8.0.1", - "@web3-storage/data-segment": "^3.0.1", + "@web3-storage/data-segment": "^3.2.0", "assert": "^2.0.0", "c8": "^7.13.0", "hd-scripts": "^4.0.0", diff --git a/packages/filecoin-client/src/dealer.js b/packages/filecoin-client/src/dealer.js index f8ad92cab..1c6919a8c 100644 --- a/packages/filecoin-client/src/dealer.js +++ b/packages/filecoin-client/src/dealer.js @@ -30,7 +30,7 @@ export const connection = connect({ * * Otherwise the task is failed and the receipt will contain details of the * reason behind the failure. - * + * * @see https://github.com/web3-storage/specs/blob/main/w3-filecoin.md#aggregateoffer * * @param {import('./types.js').InvocationConfig} conf - Configuration @@ -82,7 +82,7 @@ export async function aggregateOffer( * * @param {import('./types.js').InvocationConfig} conf - Configuration * @param {import('@web3-storage/data-segment').PieceLink} aggregate - * @param {import('@web3-storage/data-segment').PieceLink[]} pieces + * @param {import('@ucanto/interface').Link} pieces * @param {import('./types.js').RequestOptions} [options] */ export async function aggregateAccept( @@ -93,8 +93,6 @@ export async function aggregateAccept( ) { /* c8 ignore next */ const conn = options.connection ?? connection - - const block = await CBOR.write(pieces) const invocation = Dealer.aggregateAccept.invoke({ issuer, /* c8 ignore next */ @@ -102,11 +100,10 @@ export async function aggregateAccept( with: resource, nb: { aggregate, - pieces: block.cid, + pieces, }, proofs, }) - invocation.attach(block) return await invocation.execute(conn) } diff --git a/packages/filecoin-client/src/types.ts b/packages/filecoin-client/src/types.ts index 963212f0d..5a12236c6 100644 --- a/packages/filecoin-client/src/types.ts +++ b/packages/filecoin-client/src/types.ts @@ -30,7 +30,7 @@ import { AggregateAcceptFailure, DealInfo, DealInfoSuccess, - DealInfoFailure + DealInfoFailure, } from '@web3-storage/capabilities/types' export type SERVICE = 'STOREFRONT' | 'AGGREGATOR' | 'DEALER' | 'DEAL_TRACKER' @@ -60,9 +60,21 @@ export interface InvocationConfig { export interface StorefrontService { filecoin: { - offer: ServiceMethod - submit: ServiceMethod - accept: ServiceMethod + offer: ServiceMethod< + FilecoinOffer, + FilecoinOfferSuccess, + FilecoinOfferFailure + > + submit: ServiceMethod< + FilecoinSubmit, + FilecoinSubmitSuccess, + FilecoinSubmitFailure + > + accept: ServiceMethod< + FilecoinAccept, + FilecoinAcceptSuccess, + FilecoinAcceptFailure + > } } @@ -75,8 +87,16 @@ export interface AggregatorService { export interface DealerService { aggregate: { - offer: ServiceMethod - accept: ServiceMethod + offer: ServiceMethod< + AggregateOffer, + AggregateOfferSuccess, + AggregateOfferFailure + > + accept: ServiceMethod< + AggregateAccept, + AggregateAcceptSuccess, + AggregateAcceptFailure + > } } diff --git a/packages/filecoin-client/test/aggregator.test.js b/packages/filecoin-client/test/aggregator.test.js index 7d3796694..e590ca7d8 100644 --- a/packages/filecoin-client/test/aggregator.test.js +++ b/packages/filecoin-client/test/aggregator.test.js @@ -76,22 +76,23 @@ describe('aggregator', () => { it('aggregator accepts a filecoin piece', async () => { const { pieces, aggregate } = await randomAggregate(100, 100) + const piece = pieces[0].link const group = 'did:web:free.web3.storage' + // compute proof for piece in aggregate + const proof = aggregate.resolveProof(piece) + if (proof.error) { + throw new Error('could not compute proof') + } + /** @type {import('@web3-storage/capabilities/types').PieceAcceptSuccess} */ const pieceAcceptResponse = { - piece: pieces[0].link, + piece, aggregate: aggregate.link, inclusion: { - subtree: { - path: [], - index: 0n, - }, - index: { - path: [], - index: 0n, - }, - } + subtree: proof.ok[0], + index: proof.ok[1], + }, } // Create Ucanto service @@ -106,7 +107,7 @@ describe('aggregator', () => { assert.strictEqual(invCap.can, AggregatorCaps.pieceAccept.can) assert.equal(invCap.with, invocation.issuer.did()) // piece link - assert.ok(invCap.nb?.piece.equals(pieces[0].link)) + assert.ok(invCap.nb?.piece.equals(piece)) // group assert.strictEqual(invCap.nb?.group, group) @@ -123,7 +124,7 @@ describe('aggregator', () => { with: aggregatorService.did(), audience: aggregatorService, }, - pieces[0].link, + piece, group, { connection: getConnection(service).connection } ) diff --git a/packages/filecoin-client/test/deal-tracker.test.js b/packages/filecoin-client/test/deal-tracker.test.js index ae12d202e..9bafe19d0 100644 --- a/packages/filecoin-client/test/deal-tracker.test.js +++ b/packages/filecoin-client/test/deal-tracker.test.js @@ -19,10 +19,10 @@ describe('deal tracker', () => { /** @type {import('@web3-storage/capabilities/types').DealInfoSuccess} */ const dealInfoResponse = { deals: { - '12345': { - provider: 'f099' - } - } + 12_345: { + provider: 'f099', + }, + }, } // Create Ucanto service diff --git a/packages/filecoin-client/test/dealer.test.js b/packages/filecoin-client/test/dealer.test.js index 08ff14124..ac455c7e3 100644 --- a/packages/filecoin-client/test/dealer.test.js +++ b/packages/filecoin-client/test/dealer.test.js @@ -85,20 +85,11 @@ describe('dealer', () => { /** @type {import('@web3-storage/capabilities/types').AggregateAcceptSuccess} */ const aggregateAcceptResponse = { - inclusion: { - subtree: { - path: [], - index: 0n, - }, - index: { - path: [], - index: 0n, - }, + dataType: 0n, + dataSource: { + dealID: 1138n, }, - auxDataType: 0n, - auxDataSource: { - dealID: 1138n - } + aggregate: aggregate.link, } // Create Ucanto service @@ -117,12 +108,6 @@ describe('dealer', () => { // piece link assert.ok(invCap.nb.aggregate.equals(aggregate.link.link())) - // Validate block inline exists - const invocationBlocks = Array.from(invocation.iterateIPLDBlocks()) - assert.ok( - invocationBlocks.find((b) => b.cid.equals(piecesBlock.cid)) - ) - return Server.ok(aggregateAcceptResponse) }, }), @@ -137,12 +122,16 @@ describe('dealer', () => { audience: dealerService, }, aggregate.link.link(), - offer, + piecesBlock.cid, { connection: getConnection(service).connection } ) assert.ok(res.out.ok) - assert.deepEqual(res.out.ok, aggregateAcceptResponse) + assert.ok(res.out.ok.aggregate.equals(aggregate.link)) + assert.deepEqual( + BigInt(res.out.ok.dataSource.dealID), + BigInt(aggregateAcceptResponse.dataSource.dealID) + ) // does not include effect fx in receipt assert.ok(!res.fx.join) }) @@ -157,10 +146,12 @@ describe('dealer', () => { name: 'InvalidPiece', message: 'Aggregate is not a valid piece.', // piece 1 was a bad - cause: [{ - name: 'InvalidPieceCID', - piece: pieces[1].link - }] + cause: [ + { + name: 'InvalidPieceCID', + piece: pieces[1].link, + }, + ], } // Create Ucanto service @@ -179,12 +170,6 @@ describe('dealer', () => { // piece link assert.ok(invCap.nb.aggregate.equals(aggregate.link.link())) - // Validate block inline exists - const invocationBlocks = Array.from(invocation.iterateIPLDBlocks()) - assert.ok( - invocationBlocks.find((b) => b.cid.equals(piecesBlock.cid)) - ) - return { error: aggregateAcceptResponse, } @@ -201,12 +186,15 @@ describe('dealer', () => { audience: dealerService, }, aggregate.link.link(), - offer, + piecesBlock.cid, { connection: getConnection(service).connection } ) assert.ok(res.out.error) - assert.equal(dagJSON.stringify(res.out.error), dagJSON.stringify(aggregateAcceptResponse)) + assert.equal( + dagJSON.stringify(res.out.error), + dagJSON.stringify(aggregateAcceptResponse) + ) // does not include effect fx in receipt assert.ok(!res.fx.join) }) diff --git a/packages/filecoin-client/test/storefront.test.js b/packages/filecoin-client/test/storefront.test.js index 4058cbdad..80f1db115 100644 --- a/packages/filecoin-client/test/storefront.test.js +++ b/packages/filecoin-client/test/storefront.test.js @@ -5,8 +5,12 @@ import * as Server from '@ucanto/server' import * as CAR from '@ucanto/transport/car' import * as StorefrontCaps from '@web3-storage/capabilities/filecoin/storefront' import * as dagJSON from '@ipld/dag-json' -import { filecoinOffer, filecoinSubmit, filecoinAccept } from '../src/storefront.js' -import { randomCargo } from './helpers/random.js' +import { + filecoinOffer, + filecoinSubmit, + filecoinAccept, +} from '../src/storefront.js' +import { randomAggregate, randomCargo } from './helpers/random.js' import { mockService } from './helpers/mocks.js' import { serviceProvider as storefrontService } from './fixtures.js' @@ -56,7 +60,9 @@ describe('storefront', () => { }) .delegate() - return Server.ok(filecoinOfferResponse).fork(submitfx.link()).join(acceptfx.link()) + return Server.ok(filecoinOfferResponse) + .fork(submitfx.link()) + .join(acceptfx.link()) }, }), }, @@ -85,7 +91,7 @@ describe('storefront', () => { /** @type {import('@web3-storage/capabilities/types').FilecoinSubmitSuccess} */ const filecoinSubmitResponse = { - piece: cargo.link + piece: cargo.link, } // Create Ucanto service @@ -125,24 +131,29 @@ describe('storefront', () => { }) it('storefront accepts a filecoin piece', async () => { - const [cargo] = await randomCargo(1, 100) + const { pieces, aggregate } = await randomAggregate(100, 100) + const cargo = pieces[0] + + // compute proof for piece in aggregate + const proof = aggregate.resolveProof(cargo.link) + if (proof.error) { + throw new Error('could not compute proof') + } /** @type {import('@web3-storage/capabilities/types').FilecoinAcceptSuccess} */ const filecoinAcceptResponse = { + aggregate: aggregate.link, + piece: cargo.link, inclusion: { - subtree: { - path: [], - index: 0n, - }, - index: { - path: [], - index: 0n, + subtree: proof.ok[0], + index: proof.ok[1], + }, + aux: { + dataType: 0n, + dataSource: { + dealID: 1138n, }, }, - auxDataType: 0n, - auxDataSource: { - dealID: 1138n - } } // Create Ucanto service @@ -181,7 +192,13 @@ describe('storefront', () => { ) assert.ok(res.out.ok) - assert.deepEqual(res.out.ok, filecoinAcceptResponse) + assert.ok(res.out.ok.aggregate.equals(aggregate.link)) + assert.ok(res.out.ok.piece.equals(cargo.link)) + assert.equal( + BigInt(res.out.ok.aux.dataSource.dealID), + BigInt(filecoinAcceptResponse.aux.dataSource.dealID) + ) + assert.deepEqual(res.out.ok.inclusion, filecoinAcceptResponse.inclusion) // does not include effect fx in receipt assert.ok(!res.fx.join) }) @@ -193,7 +210,7 @@ describe('storefront', () => { const filecoinAcceptResponse = { name: 'InvalidContentPiece', message: 'Piece is a bad one.', - content: cargo.link + content: cargo.link, } // Create Ucanto service @@ -234,7 +251,10 @@ describe('storefront', () => { ) assert.ok(res.out.error) - assert.equal(dagJSON.stringify(res.out.error), dagJSON.stringify(filecoinAcceptResponse)) + assert.equal( + dagJSON.stringify(res.out.error), + dagJSON.stringify(filecoinAcceptResponse) + ) // does not include effect fx in receipt assert.ok(!res.fx.join) }) diff --git a/packages/upload-api/test/handlers/admin/store/inspect.js b/packages/upload-api/test/handlers/admin/store/inspect.js index 0c16c18a3..decb082a2 100644 --- a/packages/upload-api/test/handlers/admin/store/inspect.js +++ b/packages/upload-api/test/handlers/admin/store/inspect.js @@ -58,6 +58,9 @@ export const test = { `failed to get shard: ${adminStoreInspect.out.error?.message}` ) assert.equal(adminStoreInspect.out.ok?.spaces[0].did, spaceDid) - assert.equal(typeof adminStoreInspect.out.ok?.spaces[0].insertedAt, 'string') + assert.equal( + typeof adminStoreInspect.out.ok?.spaces[0].insertedAt, + 'string' + ) }, } diff --git a/packages/upload-api/test/handlers/admin/upload/inspect.js b/packages/upload-api/test/handlers/admin/upload/inspect.js index b5c24cbf4..fbd59fc71 100644 --- a/packages/upload-api/test/handlers/admin/upload/inspect.js +++ b/packages/upload-api/test/handlers/admin/upload/inspect.js @@ -62,7 +62,9 @@ export const test = { `failed to get root: ${adminUploadInspect.out.error?.message}` ) assert.equal(adminUploadInspect.out.ok?.spaces[0].did, spaceDid) - assert.equal(typeof adminUploadInspect.out.ok?.spaces[0].insertedAt, 'string') - + assert.equal( + typeof adminUploadInspect.out.ok?.spaces[0].insertedAt, + 'string' + ) }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 655db93fc..0cc600b8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -20,7 +20,7 @@ importers: devDependencies: '@docusaurus/core': specifier: ^2.3.1 - version: 2.3.1(eslint@8.49.0)(typescript@4.9.5) + version: 2.3.1(eslint@8.51.0)(typescript@4.9.5) docusaurus-plugin-typedoc: specifier: ^0.18.0 version: 0.18.0(typedoc-plugin-markdown@3.14.0)(typedoc@0.23.28) @@ -41,7 +41,7 @@ importers: dependencies: '@ipld/car': specifier: ^5.1.1 - version: 5.1.1 + version: 5.2.2 '@ipld/dag-ucan': specifier: ^3.3.2 version: 3.3.2 @@ -180,7 +180,7 @@ importers: version: 8.0.0 '@web3-storage/data-segment': specifier: ^3.0.1 - version: 3.0.1 + version: 3.2.0 devDependencies: '@types/assert': specifier: ^1.5.6 @@ -252,12 +252,12 @@ importers: specifier: workspace:^ version: link:../capabilities '@web3-storage/data-segment': - specifier: ^3.0.1 - version: 3.0.1 + specifier: ^3.2.0 + version: 3.2.0 devDependencies: '@ipld/car': specifier: ^5.1.1 - version: 5.1.1 + version: 5.2.2 '@types/mocha': specifier: ^10.0.1 version: 10.0.1 @@ -309,7 +309,7 @@ importers: devDependencies: '@ipld/car': specifier: ^5.1.1 - version: 5.1.1 + version: 5.2.2 '@ipld/dag-json': specifier: ^10.1.4 version: 10.1.4 @@ -326,8 +326,8 @@ importers: specifier: ^8.0.1 version: 8.0.1 '@web3-storage/data-segment': - specifier: ^3.0.1 - version: 3.0.1 + specifier: ^3.2.0 + version: 3.2.0 assert: specifier: ^2.0.0 version: 2.0.0 @@ -394,7 +394,7 @@ importers: devDependencies: '@ipld/car': specifier: ^5.1.1 - version: 5.1.1 + version: 5.2.2 '@ipld/dag-ucan': specifier: ^3.3.2 version: 3.3.2 @@ -427,7 +427,7 @@ importers: version: 5.2.2 '@ipld/dag-cbor': specifier: ^9.0.0 - version: 9.0.4 + version: 9.0.0 '@ipld/dag-ucan': specifier: ^3.2.0 version: 3.3.2 @@ -543,10 +543,10 @@ importers: devDependencies: '@docusaurus/core': specifier: ^2.2.0 - version: 2.3.1(eslint@8.49.0)(typescript@4.9.5) + version: 2.3.1(eslint@8.51.0)(typescript@4.9.5) '@ipld/car': specifier: ^5.1.1 - version: 5.1.1 + version: 5.2.2 '@types/mocha': specifier: ^10.0.1 version: 10.0.1 @@ -608,7 +608,7 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/trace-mapping': 0.3.20 dev: true /@arr/every@1.0.1: @@ -616,15 +616,15 @@ packages: engines: {node: '>=4'} dev: true - /@babel/code-frame@7.22.10: - resolution: {integrity: sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==} + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/highlight': 7.22.10 + '@babel/highlight': 7.22.20 chalk: 2.4.2 - /@babel/compat-data@7.22.9: - resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} + /@babel/compat-data@7.23.2: + resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} engines: {node: '>=6.9.0'} dev: true @@ -632,41 +632,41 @@ packages: resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.12.9) - '@babel/helpers': 7.22.10 - '@babel/parser': 7.22.10 - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.10 - '@babel/types': 7.22.10 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.12.9) + '@babel/helpers': 7.23.2 + '@babel/parser': 7.23.0 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 convert-source-map: 1.9.0 debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 lodash: 4.17.21 - resolve: 1.22.4 + resolve: 1.22.8 semver: 5.7.2 source-map: 0.5.7 transitivePeerDependencies: - supports-color dev: true - /@babel/core@7.22.10: - resolution: {integrity: sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==} + /@babel/core@7.23.2: + resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) - '@babel/helpers': 7.22.10 - '@babel/parser': 7.22.10 - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.10 - '@babel/types': 7.22.10 - convert-source-map: 1.9.0 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) + '@babel/helpers': 7.23.2 + '@babel/parser': 7.23.0 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 + convert-source-map: 2.0.0 debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 @@ -675,149 +675,149 @@ packages: - supports-color dev: true - /@babel/generator@7.22.10: - resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/trace-mapping': 0.3.20 jsesc: 2.5.2 /@babel/helper-annotate-as-pure@7.22.5: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true - /@babel/helper-builder-binary-assignment-operator-visitor@7.22.10: - resolution: {integrity: sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==} + /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: + resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true - /@babel/helper-compilation-targets@7.22.10: - resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.22.9 - '@babel/helper-validator-option': 7.22.5 - browserslist: 4.21.10 + '@babel/compat-data': 7.23.2 + '@babel/helper-validator-option': 7.22.15 + browserslist: 4.22.1 lru-cache: 5.1.1 semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==} + /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-member-expression-to-functions': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.9(@babel/core@7.22.10) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 dev: true - /@babel/helper-create-regexp-features-plugin@7.22.9(@babel/core@7.22.10): - resolution: {integrity: sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==} + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 semver: 6.3.1 dev: true - /@babel/helper-define-polyfill-provider@0.4.2(@babel/core@7.22.10): - resolution: {integrity: sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==} + /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.2): + resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 + '@babel/core': 7.23.2 + '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 - resolve: 1.22.4 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-environment-visitor@7.22.5: - resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.22.10 + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 - /@babel/helper-member-expression-to-functions@7.22.5: - resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==} + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true - /@babel/helper-module-imports@7.22.5: - resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true - /@babel/helper-module-transforms@7.22.9(@babel/core@7.12.9): - resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} + /@babel/helper-module-transforms@7.23.0(@babel/core@7.12.9): + resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.12.9 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.10): - resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} + /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/core': 7.23.2 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 dev: true /@babel/helper-optimise-call-expression@7.22.5: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@babel/helper-plugin-utils@7.10.4: @@ -829,27 +829,27 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator@7.22.9(@babel/core@7.22.10): - resolution: {integrity: sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==} + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.2): + resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-wrap-function': 7.22.10 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-wrap-function': 7.22.20 dev: true - /@babel/helper-replace-supers@7.22.9(@babel/core@7.22.10): - resolution: {integrity: sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==} + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.2): + resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-member-expression-to-functions': 7.22.5 + '@babel/core': 7.23.2 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 dev: true @@ -857,60 +857,60 @@ packages: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.22.5: - resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.22.5: - resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-wrap-function@7.22.10: - resolution: {integrity: sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==} + /@babel/helper-wrap-function@7.22.20: + resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.22.5 - '@babel/template': 7.22.5 - '@babel/types': 7.22.10 + '@babel/helper-function-name': 7.23.0 + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 dev: true - /@babel/helpers@7.22.10: - resolution: {integrity: sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==} + /@babel/helpers@7.23.2: + resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.10 - '@babel/types': 7.22.10 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 transitivePeerDependencies: - supports-color dev: true - /@babel/highlight@7.22.10: - resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 @@ -919,36 +919,36 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: false - /@babel/parser@7.22.10: - resolution: {integrity: sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==} + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==} + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==} + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.22.10(@babel/core@7.22.10) + '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.2) dev: true /@babel/plugin-proposal-object-rest-spread@7.12.1(@babel/core@7.12.9): @@ -959,99 +959,99 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.10.4 '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.12.9) - '@babel/plugin-transform-parameters': 7.22.5(@babel/core@7.12.9) + '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.12.9) dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.22.10): + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.2): resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.10): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.2): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.10): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.2): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.22.10): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.2): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.22.10): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.22.10): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.22.10): + /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.22.10): + /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.22.10): + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.2): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.10): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -1064,40 +1064,40 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.10): + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.10): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.2): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.10): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.10): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.2): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -1110,449 +1110,449 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.10): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.10): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.10): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.22.10): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.2): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.22.10): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.2): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.22.10): + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.22.10): + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.2): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-async-generator-functions@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==} + /@babel/plugin-transform-async-generator-functions@7.23.2(@babel/core@7.23.2): + resolution: {integrity: sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 + '@babel/core': 7.23.2 + '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.9(@babel/core@7.22.10) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.10) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-module-imports': 7.22.5 + '@babel/core': 7.23.2 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.9(@babel/core@7.22.10) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-block-scoping@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==} + /@babel/plugin-transform-block-scoping@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-class-features-plugin': 7.22.10(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-static-block@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==} + /@babel/plugin-transform-class-static-block@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-class-features-plugin': 7.22.10(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.10) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-classes@7.22.6(@babel/core@7.22.10): - resolution: {integrity: sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==} + /@babel/plugin-transform-classes@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-compilation-targets': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.9(@babel/core@7.22.10) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 dev: true - /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/template': 7.22.5 + '@babel/template': 7.22.15 dev: true - /@babel/plugin-transform-destructuring@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==} + /@babel/plugin-transform-destructuring@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dynamic-import@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==} + /@babel/plugin-transform-dynamic-import@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.10) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.10 + '@babel/core': 7.23.2 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-export-namespace-from@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==} + /@babel/plugin-transform-export-namespace-from@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.10) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-for-of@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==} + /@babel/plugin-transform-for-of@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 - '@babel/helper-function-name': 7.22.5 + '@babel/core': 7.23.2 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-function-name': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-json-strings@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==} + /@babel/plugin-transform-json-strings@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.10) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-literals@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-logical-assignment-operators@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==} + /@babel/plugin-transform-logical-assignment-operators@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.10) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-amd@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==} + /@babel/plugin-transform-modules-amd@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-commonjs@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==} + /@babel/plugin-transform-modules-commonjs@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 dev: true - /@babel/plugin-transform-modules-systemjs@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==} + /@babel/plugin-transform-modules-systemjs@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==} + /@babel/plugin-transform-nullish-coalescing-operator@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.10) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-numeric-separator@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==} + /@babel/plugin-transform-numeric-separator@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.10) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-object-rest-spread@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==} + /@babel/plugin-transform-object-rest-spread@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.22.9 - '@babel/core': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 + '@babel/compat-data': 7.23.2 + '@babel/core': 7.23.2 + '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-transform-parameters': 7.22.5(@babel/core@7.22.10) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.9(@babel/core@7.22.10) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-optional-catch-binding@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==} + /@babel/plugin-transform-optional-catch-binding@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.10) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-optional-chaining@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==} + /@babel/plugin-transform-optional-chaining@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.10) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-parameters@7.22.5(@babel/core@7.12.9): - resolution: {integrity: sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==} + /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.12.9): + resolution: {integrity: sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1561,431 +1561,431 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-parameters@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==} + /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-class-features-plugin': 7.22.10(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-property-in-object@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==} + /@babel/plugin-transform-private-property-in-object@7.22.11(@babel/core@7.23.2): + resolution: {integrity: sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.10(@babel/core@7.22.10) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.10) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-constant-elements@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-react-constant-elements@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==} + /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) - '@babel/types': 7.22.10 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) + '@babel/types': 7.23.0 dev: true - /@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.22.10(@babel/core@7.22.10): + /@babel/plugin-transform-regenerator@7.22.10(@babel/core@7.23.2): resolution: {integrity: sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 regenerator-transform: 0.15.2 dev: true - /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-runtime@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-RchI7HePu1eu0CYNKHHHQdfenZcM4nz8rew5B1VWqeRKdcwW5aQ5HeG9eTUbWiAS1UrmHVLmoxTWHt3iLD/NhA==} + /@babel/plugin-transform-runtime@7.23.2(@babel/core@7.23.2): + resolution: {integrity: sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-module-imports': 7.22.5 + '@babel/core': 7.23.2 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - babel-plugin-polyfill-corejs2: 0.4.5(@babel/core@7.22.10) - babel-plugin-polyfill-corejs3: 0.8.3(@babel/core@7.22.10) - babel-plugin-polyfill-regenerator: 0.5.2(@babel/core@7.22.10) + babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.2) + babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.23.2) + babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.2) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-spread@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-spread@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typescript@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-7++c8I/ymsDo4QQBAgbraXLzIM6jmfao11KgIBEYZRReWzNWH9NtNgJcyrZiXsOPh523FQm6LfpLyy/U5fn46A==} + /@babel/plugin-transform-typescript@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.10(@babel/core@7.22.10) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-unicode-escapes@7.22.10(@babel/core@7.22.10): + /@babel/plugin-transform-unicode-escapes@7.22.10(@babel/core@7.23.2): resolution: {integrity: sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/preset-env@7.22.10(@babel/core@7.22.10): - resolution: {integrity: sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==} + /@babel/preset-env@7.23.2(@babel/core@7.23.2): + resolution: {integrity: sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.22.9 - '@babel/core': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 + '@babel/compat-data': 7.23.2 + '@babel/core': 7.23.2 + '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.5 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.22.10) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.10) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.10) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.10) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.10) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.10) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.10) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.10) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.10) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.22.10) - '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-async-generator-functions': 7.22.10(@babel/core@7.22.10) - '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-block-scoping': 7.22.10(@babel/core@7.22.10) - '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-class-static-block': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-classes': 7.22.6(@babel/core@7.22.10) - '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-destructuring': 7.22.10(@babel/core@7.22.10) - '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-dynamic-import': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-export-namespace-from': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-for-of': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-json-strings': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-logical-assignment-operators': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-modules-amd': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-modules-commonjs': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-modules-systemjs': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-nullish-coalescing-operator': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-numeric-separator': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-object-rest-spread': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-optional-catch-binding': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-optional-chaining': 7.22.10(@babel/core@7.22.10) - '@babel/plugin-transform-parameters': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-private-property-in-object': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-regenerator': 7.22.10(@babel/core@7.22.10) - '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-unicode-escapes': 7.22.10(@babel/core@7.22.10) - '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.22.10) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.22.10) - '@babel/types': 7.22.10 - babel-plugin-polyfill-corejs2: 0.4.5(@babel/core@7.22.10) - babel-plugin-polyfill-corejs3: 0.8.3(@babel/core@7.22.10) - babel-plugin-polyfill-regenerator: 0.5.2(@babel/core@7.22.10) - core-js-compat: 3.32.0 + '@babel/helper-validator-option': 7.22.15 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.2) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.2) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.2) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.2) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.2) + '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-async-generator-functions': 7.23.2(@babel/core@7.23.2) + '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-block-scoping': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-class-static-block': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-classes': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-destructuring': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-dynamic-import': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-export-namespace-from': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-for-of': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-json-strings': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-logical-assignment-operators': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-modules-amd': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-modules-systemjs': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-nullish-coalescing-operator': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-numeric-separator': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-object-rest-spread': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-optional-catch-binding': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-private-property-in-object': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-regenerator': 7.22.10(@babel/core@7.23.2) + '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-escapes': 7.22.10(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.2) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.2) + '@babel/types': 7.23.0 + babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.2) + babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.23.2) + babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.2) + core-js-compat: 3.33.0 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.22.10): + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.2): resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 esutils: 2.0.3 dev: true - /@babel/preset-react@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ==} + /@babel/preset-react@7.22.15(@babel/core@7.23.2): + resolution: {integrity: sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.5 - '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.22.10) + '@babel/helper-validator-option': 7.22.15 + '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.23.2) dev: true - /@babel/preset-typescript@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==} + /@babel/preset-typescript@7.23.2(@babel/core@7.23.2): + resolution: {integrity: sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-modules-commonjs': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-typescript': 7.22.10(@babel/core@7.22.10) + '@babel/helper-validator-option': 7.22.15 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-typescript': 7.22.15(@babel/core@7.23.2) dev: true /@babel/regjsgen@0.8.0: resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} dev: true - /@babel/runtime-corejs3@7.22.10: - resolution: {integrity: sha512-IcixfV2Jl3UrqZX4c81+7lVg5++2ufYJyAFW3Aux/ZTvY6LVYYhJ9rMgnbX0zGVq6eqfVpnoatTjZdVki/GmWA==} + /@babel/runtime-corejs3@7.23.2: + resolution: {integrity: sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==} engines: {node: '>=6.9.0'} dependencies: - core-js-pure: 3.32.0 + core-js-pure: 3.33.0 regenerator-runtime: 0.14.0 dev: true - /@babel/runtime@7.22.10: - resolution: {integrity: sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==} + /@babel/runtime@7.23.2: + resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 dev: true - /@babel/template@7.22.5: - resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.22.10 - '@babel/parser': 7.22.10 - '@babel/types': 7.22.10 + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 - /@babel/traverse@7.22.10: - resolution: {integrity: sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==} + /@babel/traverse@7.23.2: + resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.22.10 - '@babel/types': 7.22.10 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color - /@babel/types@7.22.10: - resolution: {integrity: sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==} + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 /@bcoe/v8-coverage@0.2.3: @@ -2004,7 +2004,7 @@ packages: engines: {node: '>=10.0.0'} dev: true - /@docusaurus/core@2.3.1(eslint@8.49.0)(typescript@4.9.5): + /@docusaurus/core@2.3.1(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-0Jd4jtizqnRAr7svWaBbbrCCN8mzBNd2xFLoT/IM7bGfFie5y58oz97KzXliwiLY3zWjqMXjQcuP1a5VgCv2JA==} engines: {node: '>=16.14'} hasBin: true @@ -2017,16 +2017,16 @@ packages: react-dom: optional: true dependencies: - '@babel/core': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-transform-runtime': 7.22.10(@babel/core@7.22.10) - '@babel/preset-env': 7.22.10(@babel/core@7.22.10) - '@babel/preset-react': 7.22.5(@babel/core@7.22.10) - '@babel/preset-typescript': 7.22.5(@babel/core@7.22.10) - '@babel/runtime': 7.22.10 - '@babel/runtime-corejs3': 7.22.10 - '@babel/traverse': 7.22.10 + '@babel/core': 7.23.2 + '@babel/generator': 7.23.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-transform-runtime': 7.23.2(@babel/core@7.23.2) + '@babel/preset-env': 7.23.2(@babel/core@7.23.2) + '@babel/preset-react': 7.22.15(@babel/core@7.23.2) + '@babel/preset-typescript': 7.23.2(@babel/core@7.23.2) + '@babel/runtime': 7.23.2 + '@babel/runtime-corejs3': 7.23.2 + '@babel/traverse': 7.23.2 '@docusaurus/cssnano-preset': 2.3.1 '@docusaurus/logger': 2.3.1 '@docusaurus/mdx-loader': 2.3.1 @@ -2036,41 +2036,41 @@ packages: '@docusaurus/utils-validation': 2.3.1 '@slorber/static-site-generator-webpack-plugin': 4.0.7 '@svgr/webpack': 6.5.1 - autoprefixer: 10.4.14(postcss@8.4.27) - babel-loader: 8.3.0(@babel/core@7.22.10)(webpack@5.88.2) + autoprefixer: 10.4.16(postcss@8.4.31) + babel-loader: 8.3.0(@babel/core@7.23.2)(webpack@5.89.0) babel-plugin-dynamic-import-node: 2.3.3 boxen: 6.2.1 chalk: 4.1.2 chokidar: 3.5.3 clean-css: 5.3.2 cli-table3: 0.6.3 - combine-promises: 1.1.0 + combine-promises: 1.2.0 commander: 5.1.0 - copy-webpack-plugin: 11.0.0(webpack@5.88.2) - core-js: 3.32.0 - css-loader: 6.8.1(webpack@5.88.2) - css-minimizer-webpack-plugin: 4.2.2(clean-css@5.3.2)(webpack@5.88.2) - cssnano: 5.1.15(postcss@8.4.27) + copy-webpack-plugin: 11.0.0(webpack@5.89.0) + core-js: 3.33.0 + css-loader: 6.8.1(webpack@5.89.0) + css-minimizer-webpack-plugin: 4.2.2(clean-css@5.3.2)(webpack@5.89.0) + cssnano: 5.1.15(postcss@8.4.31) del: 6.1.1 detect-port: 1.5.1 escape-html: 1.0.3 eta: 2.2.0 - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) fs-extra: 10.1.0 html-minifier-terser: 6.1.0 html-tags: 3.3.1 - html-webpack-plugin: 5.5.3(webpack@5.88.2) + html-webpack-plugin: 5.5.3(webpack@5.89.0) import-fresh: 3.3.0 leven: 3.1.0 lodash: 4.17.21 - mini-css-extract-plugin: 2.7.6(webpack@5.88.2) - postcss: 8.4.27 - postcss-loader: 7.3.3(postcss@8.4.27)(webpack@5.88.2) + mini-css-extract-plugin: 2.7.6(webpack@5.89.0) + postcss: 8.4.31 + postcss-loader: 7.3.3(postcss@8.4.31)(typescript@4.9.5)(webpack@5.89.0) prompts: 2.4.2 - react-dev-utils: 12.0.1(eslint@8.49.0)(typescript@4.9.5)(webpack@5.88.2) + react-dev-utils: 12.0.1(eslint@8.51.0)(typescript@4.9.5)(webpack@5.89.0) react-helmet-async: 1.3.0 react-loadable: /@docusaurus/react-loadable@5.5.2 - react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.88.2) + react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.89.0) react-router: 5.3.4 react-router-config: 5.1.1(react-router@5.3.4) react-router-dom: 5.3.4 @@ -2078,16 +2078,16 @@ packages: semver: 7.5.4 serve-handler: 6.1.5 shelljs: 0.8.5 - terser-webpack-plugin: 5.3.9(webpack@5.88.2) - tslib: 2.6.1 + terser-webpack-plugin: 5.3.9(webpack@5.89.0) + tslib: 2.6.2 update-notifier: 5.1.0 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.88.2) + url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.89.0) wait-on: 6.0.1 - webpack: 5.88.2 - webpack-bundle-analyzer: 4.9.0 - webpack-dev-server: 4.15.1(webpack@5.88.2) - webpack-merge: 5.9.0 - webpackbar: 5.0.2(webpack@5.88.2) + webpack: 5.89.0 + webpack-bundle-analyzer: 4.9.1 + webpack-dev-server: 4.15.1(webpack@5.89.0) + webpack-merge: 5.10.0 + webpackbar: 5.0.2(webpack@5.89.0) transitivePeerDependencies: - '@docusaurus/types' - '@parcel/css' @@ -2111,10 +2111,10 @@ packages: resolution: {integrity: sha512-7mIhAROES6CY1GmCjR4CZkUfjTL6B3u6rKHK0ChQl2d1IevYXq/k/vFgvOrJfcKxiObpMnE9+X6R2Wt1KqxC6w==} engines: {node: '>=16.14'} dependencies: - cssnano-preset-advanced: 5.3.10(postcss@8.4.27) - postcss: 8.4.27 - postcss-sort-media-queries: 4.4.1(postcss@8.4.27) - tslib: 2.6.1 + cssnano-preset-advanced: 5.3.10(postcss@8.4.31) + postcss: 8.4.31 + postcss-sort-media-queries: 4.4.1(postcss@8.4.31) + tslib: 2.6.2 dev: true /@docusaurus/logger@2.3.1: @@ -2122,7 +2122,7 @@ packages: engines: {node: '>=16.14'} dependencies: chalk: 4.1.2 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /@docusaurus/mdx-loader@2.3.1: @@ -2137,23 +2137,23 @@ packages: react-dom: optional: true dependencies: - '@babel/parser': 7.22.10 - '@babel/traverse': 7.22.10 + '@babel/parser': 7.23.0 + '@babel/traverse': 7.23.2 '@docusaurus/logger': 2.3.1 '@docusaurus/utils': 2.3.1 '@mdx-js/mdx': 1.6.22 escape-html: 1.0.3 - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) fs-extra: 10.1.0 image-size: 1.0.2 mdast-util-to-string: 2.0.0 remark-emoji: 2.2.0 stringify-object: 3.3.0 - tslib: 2.6.1 + tslib: 2.6.2 unified: 9.2.2 unist-util-visit: 2.0.3 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.88.2) - webpack: 5.88.2 + url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.89.0) + webpack: 5.89.0 transitivePeerDependencies: - '@docusaurus/types' - '@swc/core' @@ -2171,7 +2171,7 @@ packages: react: optional: true dependencies: - '@types/react': 18.2.22 + '@types/react': 18.2.28 prop-types: 15.8.1 dev: true @@ -2184,7 +2184,7 @@ packages: '@docusaurus/types': optional: true dependencies: - tslib: 2.6.1 + tslib: 2.6.2 dev: true /@docusaurus/utils-validation@2.3.1: @@ -2193,9 +2193,9 @@ packages: dependencies: '@docusaurus/logger': 2.3.1 '@docusaurus/utils': 2.3.1 - joi: 17.9.2 + joi: 17.11.0 js-yaml: 4.1.0 - tslib: 2.6.1 + tslib: 2.6.2 transitivePeerDependencies: - '@docusaurus/types' - '@swc/core' @@ -2217,7 +2217,7 @@ packages: '@docusaurus/logger': 2.3.1 '@svgr/webpack': 6.5.1 escape-string-regexp: 4.0.0 - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) fs-extra: 10.1.0 github-slugger: 1.5.0 globby: 11.1.0 @@ -2227,9 +2227,9 @@ packages: micromatch: 4.0.5 resolve-pathname: 3.0.0 shelljs: 0.8.5 - tslib: 2.6.1 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.88.2) - webpack: 5.88.2 + tslib: 2.6.2 + url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.89.0) + webpack: 5.89.0 transitivePeerDependencies: - '@swc/core' - esbuild @@ -2454,53 +2454,21 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.46.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.46.0 - eslint-visitor-keys: 3.4.2 - dev: true - - /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.51.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.49.0 - eslint-visitor-keys: 3.4.2 - dev: true - - /@eslint-community/regexpp@4.6.2: - resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + eslint: 8.51.0 + eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/regexpp@4.8.1: - resolution: {integrity: sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==} + /@eslint-community/regexpp@4.9.1: + resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc@2.1.1: - resolution: {integrity: sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) - espree: 9.6.1 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - /@eslint/eslintrc@2.1.2: resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2508,7 +2476,7 @@ packages: ajv: 6.12.6 debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 - globals: 13.21.0 + globals: 13.23.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -2518,13 +2486,8 @@ packages: - supports-color dev: true - /@eslint/js@8.46.0: - resolution: {integrity: sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /@eslint/js@8.49.0: - resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + /@eslint/js@8.51.0: + resolution: {integrity: sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -2538,17 +2501,6 @@ packages: '@hapi/hoek': 9.3.0 dev: true - /@humanwhocodes/config-array@0.11.10: - resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4(supports-color@8.1.1) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - /@humanwhocodes/config-array@0.11.11: resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} engines: {node: '>=10.10.0'} @@ -2569,24 +2521,14 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@ipld/car@5.1.1: - resolution: {integrity: sha512-HoFTUqUJL9cPGhC9qRmHCvamfIsj1JllQSQ/Xu9/KN/VNJp8To9Ms4qiZPEMOwcrNFclfYqrahjGYbf4KL/d9A==} - engines: {node: '>=16.0.0', npm: '>=7.0.0'} - dependencies: - '@ipld/dag-cbor': 9.0.4 - cborg: 1.10.2 - multiformats: 11.0.2 - varint: 6.0.0 - /@ipld/car@5.2.2: resolution: {integrity: sha512-8IapvzPNB1Z2VwtA7n6olB3quhrLMbFxk4JaENIT4OlQ6YQNz1peY00qb2iJTC/kCDir7yb3TuNHkbdDzSKiXA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - '@ipld/dag-cbor': 9.0.4 + '@ipld/dag-cbor': 9.0.0 cborg: 2.0.5 - multiformats: 12.1.1 + multiformats: 12.1.2 varint: 6.0.0 - dev: false /@ipld/dag-cbor@9.0.0: resolution: {integrity: sha512-zdsiSiYDEOIDW7mmWOYWC9gukjXO+F8wqxz/LfN7iSwTfIyipC8+UQrCbPupFMRb/33XQTZk8yl3My8vUQBRoA==} @@ -2594,45 +2536,44 @@ packages: dependencies: cborg: 1.10.2 multiformats: 11.0.2 - dev: false - /@ipld/dag-cbor@9.0.4: - resolution: {integrity: sha512-HBNVngk/47pKNLTAelN6ORWgKkjJtQj96Xb+jIBtRShJGCsXgghj1TzTynTTIp1dZxwPe5rVIL6yjZmvdyP2Wg==} + /@ipld/dag-cbor@9.0.6: + resolution: {integrity: sha512-3kNab5xMppgWw6DVYx2BzmFq8t7I56AGWfp5kaU1fIPkwHVpBRglJJTYsGtbVluCi/s/q97HZM3bC+aDW4sxbQ==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - cborg: 2.0.5 - multiformats: 12.1.1 + cborg: 4.0.3 + multiformats: 12.1.2 /@ipld/dag-json@10.1.4: resolution: {integrity: sha512-Vgm739qPQ7P8cstna60oYx19tzJzep+Uy7yWi80dzIOygibfVaaRZ07M6qbHP+C9BJl81GNFaXy2Plr0y7poBA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: cborg: 4.0.3 - multiformats: 12.1.1 + multiformats: 12.1.2 - /@ipld/dag-pb@4.0.5: - resolution: {integrity: sha512-El2Jhmv6bWuakhvnw1dl6xOhqLeVhlY8DIAJ06NtZRAoDcOzeGzvOtPzMCszVgCT0EQz+LOctyfgQ5Oszba19A==} + /@ipld/dag-pb@4.0.6: + resolution: {integrity: sha512-wOij3jfDKZsb9yjhQeHp+TQy0pu1vmUkGv324xciFFZ7xGbDfAGTQW03lSA5aJ/7HBBNYgjEE0nvHmNW1Qjfag==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - multiformats: 12.1.1 + multiformats: 12.1.2 /@ipld/dag-ucan@3.3.2: resolution: {integrity: sha512-EhuOrAfnudsVYIbzEIgi3itHAEo3WZNOt1VNPsYhxKBhOzDMeoTXh6/IHc7ZKBW1T2vDQHdgj4m1r64z6MssGA==} dependencies: - '@ipld/dag-cbor': 9.0.4 + '@ipld/dag-cbor': 9.0.0 '@ipld/dag-json': 10.1.4 multiformats: 11.0.2 /@ipld/unixfs@2.1.1: resolution: {integrity: sha512-g3gr/3XvfQs4x2VFjlICae09ul5fbWCKRInN6Vgeot2+GH0h/krr3PqZCIo4dy4Ou2mQOsIddxUvG8UZ4p9SbQ==} dependencies: - '@ipld/dag-pb': 4.0.5 - '@multiformats/murmur3': 2.1.6 + '@ipld/dag-pb': 4.0.6 + '@multiformats/murmur3': 2.1.7 '@perma/map': 1.0.3 '@web-std/stream': 1.0.1 actor: 2.3.1 multiformats: 11.0.2 - protobufjs: 7.2.4 + protobufjs: 7.2.5 rabin-rs: 2.1.0 dev: false @@ -2641,22 +2582,22 @@ packages: engines: {node: '>=8'} dev: true - /@jest/schemas@29.6.0: - resolution: {integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==} + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@sinclair/typebox': 0.27.8 dev: true - /@jest/types@29.6.1: - resolution: {integrity: sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==} + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.6.0 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.17.4 - '@types/yargs': 17.0.24 + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.5 + '@types/istanbul-reports': 3.0.3 + '@types/node': 18.11.18 + '@types/yargs': 17.0.28 chalk: 4.1.2 dev: true @@ -2666,7 +2607,7 @@ packages: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/trace-mapping': 0.3.20 /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} @@ -2680,14 +2621,14 @@ packages: resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} dependencies: '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/trace-mapping': 0.3.20 dev: true /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.19: - resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 @@ -2726,18 +2667,18 @@ packages: resolution: {integrity: sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==} dev: true - /@multiformats/murmur3@2.1.6: - resolution: {integrity: sha512-kpJDN+o8B0gJaaqbdV/spIVPj35hqew4rEw8VzPmcITsLpHSgP8pJDeaVaGGVeX/UM8n4IGctLCxw7PBfVks+A==} + /@multiformats/murmur3@2.1.7: + resolution: {integrity: sha512-Yf0UpAaONjed+8PTt5NM/GG4Z4Ai4m1qfT7bqevjnkwRQ12K+0jxtRomirz+VJx4PokpA2St1ZSD1iMkZTqPRQ==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - multiformats: 12.1.1 + multiformats: 12.1.2 murmurhash3js-revisited: 3.0.0 /@noble/ed25519@1.7.3: resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==} - /@noble/hashes@1.3.1: - resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} engines: {node: '>= 16'} /@nodelib/fs.scandir@2.1.5: @@ -2764,7 +2705,7 @@ packages: /@perma/map@1.0.3: resolution: {integrity: sha512-Bf5njk0fnJGTFE2ETntq0N1oJ6YdCPIpTDn3R3KYZJQdeYSOCNL7mBrFlGnbqav8YQhJA/p81pvHINX9vAtHkQ==} dependencies: - '@multiformats/murmur3': 2.1.6 + '@multiformats/murmur3': 2.1.7 murmurhash3js-revisited: 3.0.0 dev: false @@ -2880,101 +2821,101 @@ packages: webpack-sources: 3.2.3 dev: true - /@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.22.10): + /@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.23.2): resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.22.10): + /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.22.10): + /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.22.10): + /@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.23.2): resolution: {integrity: sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.22.10): + /@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.23.2): resolution: {integrity: sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.22.10): + /@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.23.2): resolution: {integrity: sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.22.10): + /@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.23.2): resolution: {integrity: sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.22.10): + /@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.23.2): resolution: {integrity: sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-preset@6.5.1(@babel/core@7.22.10): + /@svgr/babel-preset@6.5.1(@babel/core@7.23.2): resolution: {integrity: sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.22.10) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.22.10) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.22.10) - '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.22.10) - '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.22.10) - '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.22.10) - '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.22.10) - '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.23.2) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.23.2) + '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.23.2) + '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.23.2) + '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.23.2) + '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.23.2) dev: true /@svgr/core@6.5.1: resolution: {integrity: sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.22.10 - '@svgr/babel-preset': 6.5.1(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@svgr/babel-preset': 6.5.1(@babel/core@7.23.2) '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) camelcase: 6.3.0 cosmiconfig: 7.1.0 @@ -2986,7 +2927,7 @@ packages: resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} engines: {node: '>=10'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 entities: 4.5.0 dev: true @@ -2996,8 +2937,8 @@ packages: peerDependencies: '@svgr/core': ^6.0.0 dependencies: - '@babel/core': 7.22.10 - '@svgr/babel-preset': 6.5.1(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@svgr/babel-preset': 6.5.1(@babel/core@7.23.2) '@svgr/core': 6.5.1 '@svgr/hast-util-to-babel-ast': 6.5.1 svg-parser: 2.0.4 @@ -3021,11 +2962,11 @@ packages: resolution: {integrity: sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.22.10 - '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.22.10) - '@babel/preset-env': 7.22.10(@babel/core@7.22.10) - '@babel/preset-react': 7.22.5(@babel/core@7.22.10) - '@babel/preset-typescript': 7.22.5(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.23.2) + '@babel/preset-env': 7.23.2(@babel/core@7.23.2) + '@babel/preset-react': 7.22.15(@babel/core@7.23.2) + '@babel/preset-typescript': 7.23.2(@babel/core@7.23.2) '@svgr/core': 6.5.1 '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1) @@ -3049,113 +2990,113 @@ packages: resolution: {integrity: sha512-Y7gDJiIqb9qKUHfBQYOWGngUpLORtirAVPuj/CWJrU2C6ZM4/y3XLwuwfGMF8s7QzW746LQZx23m0+1FSgjfug==} dev: true - /@types/body-parser@1.19.2: - resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} + /@types/body-parser@1.19.4: + resolution: {integrity: sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==} dependencies: - '@types/connect': 3.4.35 - '@types/node': 18.17.4 + '@types/connect': 3.4.37 + '@types/node': 18.11.18 dev: true - /@types/bonjour@3.5.10: - resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} + /@types/bonjour@3.5.12: + resolution: {integrity: sha512-ky0kWSqXVxSqgqJvPIkgFkcn4C8MnRog308Ou8xBBIVo39OmUFy+jqNe0nPwLCDFxUpmT9EvT91YzOJgkDRcFg==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true - /@types/connect-history-api-fallback@1.5.0: - resolution: {integrity: sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==} + /@types/connect-history-api-fallback@1.5.2: + resolution: {integrity: sha512-gX2j9x+NzSh4zOhnRPSdPPmTepS4DfxES0AvIFv3jGv5QyeAJf6u6dY5/BAoAJU9Qq1uTvwOku8SSC2GnCRl6Q==} dependencies: - '@types/express-serve-static-core': 4.17.35 - '@types/node': 18.17.4 + '@types/express-serve-static-core': 4.17.38 + '@types/node': 18.11.18 dev: true - /@types/connect@3.4.35: - resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} + /@types/connect@3.4.37: + resolution: {integrity: sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true - /@types/eslint-scope@3.7.4: - resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} + /@types/eslint-scope@3.7.6: + resolution: {integrity: sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ==} dependencies: - '@types/eslint': 8.44.2 - '@types/estree': 1.0.1 + '@types/eslint': 8.44.5 + '@types/estree': 1.0.3 dev: true - /@types/eslint@8.44.2: - resolution: {integrity: sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==} + /@types/eslint@8.44.5: + resolution: {integrity: sha512-Ol2eio8LtD/tGM4Ga7Jb83NuFwEv3NqvssSlifXL9xuFpSyQZw0ecmm2Kux6iU0KxQmp95hlPmGCzGJ0TCFeRA==} dependencies: - '@types/estree': 1.0.1 - '@types/json-schema': 7.0.12 + '@types/estree': 1.0.3 + '@types/json-schema': 7.0.14 dev: true - /@types/estree@1.0.1: - resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} + /@types/estree@1.0.3: + resolution: {integrity: sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==} dev: true - /@types/express-serve-static-core@4.17.35: - resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==} + /@types/express-serve-static-core@4.17.38: + resolution: {integrity: sha512-hXOtc0tuDHZPFwwhuBJXPbjemWtXnJjbvuuyNH2Y5Z6in+iXc63c4eXYDc7GGGqHy+iwYqAJMdaItqdnbcBKmg==} dependencies: - '@types/node': 18.17.4 - '@types/qs': 6.9.7 - '@types/range-parser': 1.2.4 - '@types/send': 0.17.1 + '@types/node': 18.11.18 + '@types/qs': 6.9.8 + '@types/range-parser': 1.2.5 + '@types/send': 0.17.2 dev: true - /@types/express@4.17.17: - resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} + /@types/express@4.17.20: + resolution: {integrity: sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==} dependencies: - '@types/body-parser': 1.19.2 - '@types/express-serve-static-core': 4.17.35 - '@types/qs': 6.9.7 - '@types/serve-static': 1.15.2 + '@types/body-parser': 1.19.4 + '@types/express-serve-static-core': 4.17.38 + '@types/qs': 6.9.8 + '@types/serve-static': 1.15.3 dev: true - /@types/hast@2.3.5: - resolution: {integrity: sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg==} + /@types/hast@2.3.7: + resolution: {integrity: sha512-EVLigw5zInURhzfXUM65eixfadfsHKomGKUakToXo84t8gGIJuTcD2xooM2See7GyQ7DRtYjhCHnSUQez8JaLw==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 dev: true /@types/html-minifier-terser@6.1.0: resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} dev: true - /@types/http-errors@2.0.1: - resolution: {integrity: sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==} + /@types/http-errors@2.0.3: + resolution: {integrity: sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==} dev: true - /@types/http-proxy@1.17.11: - resolution: {integrity: sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==} + /@types/http-proxy@1.17.13: + resolution: {integrity: sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true /@types/inquirer@9.0.3: resolution: {integrity: sha512-CzNkWqQftcmk2jaCWdBTf9Sm7xSw4rkI1zpU/Udw3HX5//adEZUIm9STtoRP1qgWj0CWQtJ9UTvqmO2NNjhMJw==} dependencies: - '@types/through': 0.0.30 + '@types/through': 0.0.31 rxjs: 7.8.1 dev: true - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + /@types/istanbul-lib-coverage@2.0.5: + resolution: {integrity: sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==} dev: true - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} + /@types/istanbul-lib-report@3.0.2: + resolution: {integrity: sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==} dependencies: - '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-lib-coverage': 2.0.5 dev: true - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + /@types/istanbul-reports@3.0.3: + resolution: {integrity: sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==} dependencies: - '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-lib-report': 3.0.2 dev: true - /@types/json-schema@7.0.12: - resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + /@types/json-schema@7.0.14: + resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==} dev: true /@types/json5@0.0.29: @@ -3165,21 +3106,21 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true - /@types/mdast@3.0.12: - resolution: {integrity: sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==} + /@types/mdast@3.0.14: + resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 dev: true - /@types/mime@1.3.2: - resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} + /@types/mime@1.3.3: + resolution: {integrity: sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==} dev: true - /@types/mime@3.0.1: - resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} + /@types/mime@3.0.2: + resolution: {integrity: sha512-Wj+fqpTLtTbG7c0tH47dkahefpLKEbB+xAZuLq7b4/IDHPl/n6VoXcyUQ2bypFlbSwvCr0y+bD4euTTqTJsPxQ==} dev: true /@types/minimatch@3.0.5: @@ -3192,46 +3133,42 @@ packages: /@types/node@18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} - dev: true - - /@types/node@18.17.4: - resolution: {integrity: sha512-ATL4WLgr7/W40+Sp1WnNTSKbgVn6Pvhc/2RHAdt8fl6NsQyp4oPCi2eKcGOvA494bwf1K/W6nGgZ9TwDqvpjdw==} - /@types/normalize-package-data@2.4.1: - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + /@types/normalize-package-data@2.4.3: + resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==} dev: true - /@types/parse-json@4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + /@types/parse-json@4.0.1: + resolution: {integrity: sha512-3YmXzzPAdOTVljVMkTMBdBEvlOLg2cDQaDhnnhT3nT9uDbnJzjWhKlzb+desT12Y7tGqaN6d+AbozcKzyL36Ng==} /@types/parse5@5.0.3: resolution: {integrity: sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==} dev: true - /@types/prop-types@15.7.6: - resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==} + /@types/prop-types@15.7.9: + resolution: {integrity: sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==} dev: true - /@types/qs@6.9.7: - resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + /@types/qs@6.9.8: + resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==} dev: true - /@types/range-parser@1.2.4: - resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} + /@types/range-parser@1.2.5: + resolution: {integrity: sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==} dev: true - /@types/react@18.2.22: - resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==} + /@types/react@18.2.28: + resolution: {integrity: sha512-ad4aa/RaaJS3hyGz0BGegdnSRXQBkd1CCYDCdNjBPg90UUpLgo+WlJqb9fMYUxtehmzF3PJaTWqRZjko6BRzBg==} dependencies: - '@types/prop-types': 15.7.6 - '@types/scheduler': 0.16.3 + '@types/prop-types': 15.7.9 + '@types/scheduler': 0.16.4 csstype: 3.1.2 dev: true - /@types/responselike@1.0.0: - resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} + /@types/responselike@1.0.1: + resolution: {integrity: sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true /@types/retry@0.12.0: @@ -3242,65 +3179,65 @@ packages: resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} dev: false - /@types/scheduler@0.16.3: - resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} + /@types/scheduler@0.16.4: + resolution: {integrity: sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==} dev: true - /@types/semver@7.5.0: - resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} + /@types/semver@7.5.3: + resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} dev: true - /@types/send@0.17.1: - resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} + /@types/send@0.17.2: + resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==} dependencies: - '@types/mime': 1.3.2 - '@types/node': 18.17.4 + '@types/mime': 1.3.3 + '@types/node': 18.11.18 dev: true - /@types/serve-index@1.9.1: - resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==} + /@types/serve-index@1.9.2: + resolution: {integrity: sha512-asaEIoc6J+DbBKXtO7p2shWUpKacZOoMBEGBgPG91P8xhO53ohzHWGCs4ScZo5pQMf5ukQzVT9fhX1WzpHihig==} dependencies: - '@types/express': 4.17.17 + '@types/express': 4.17.20 dev: true - /@types/serve-static@1.15.2: - resolution: {integrity: sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==} + /@types/serve-static@1.15.3: + resolution: {integrity: sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==} dependencies: - '@types/http-errors': 2.0.1 - '@types/mime': 3.0.1 - '@types/node': 18.17.4 + '@types/http-errors': 2.0.3 + '@types/mime': 3.0.2 + '@types/node': 18.11.18 dev: true /@types/sinon@10.0.13: resolution: {integrity: sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==} dependencies: - '@types/sinonjs__fake-timers': 8.1.2 + '@types/sinonjs__fake-timers': 8.1.3 dev: true - /@types/sinonjs__fake-timers@8.1.2: - resolution: {integrity: sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==} + /@types/sinonjs__fake-timers@8.1.3: + resolution: {integrity: sha512-4g+2YyWe0Ve+LBh+WUm1697PD0Kdi6coG1eU0YjQbwx61AZ8XbEpL1zIT6WjuUKrCMCROpEaYQPDjBnDouBVAQ==} dev: true - /@types/sockjs@0.3.33: - resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} + /@types/sockjs@0.3.34: + resolution: {integrity: sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true - /@types/through@0.0.30: - resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} + /@types/through@0.0.31: + resolution: {integrity: sha512-LpKpmb7FGevYgXnBXYs6HWnmiFyVG07Pt1cnbgM1IhEacITTiUaBXXvOR3Y50ksaJWGSfhbEvQFivQEFGCC55w==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true - /@types/unist@2.0.7: - resolution: {integrity: sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==} + /@types/unist@2.0.8: + resolution: {integrity: sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==} dev: true /@types/varint@6.0.1: resolution: {integrity: sha512-fQdOiZpDMBvaEdl12P1x7xlTPRAtd7qUUtVaWgkCy8DC//wCv19nqFFtrnR3y/ac6VFY0UUvYuQqfKzZTSE26w==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true /@types/ws@8.5.4: @@ -3309,23 +3246,23 @@ packages: '@types/node': 18.11.18 dev: true - /@types/ws@8.5.5: - resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} + /@types/ws@8.5.7: + resolution: {integrity: sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 dev: true - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + /@types/yargs-parser@21.0.1: + resolution: {integrity: sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==} dev: true - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + /@types/yargs@17.0.28: + resolution: {integrity: sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==} dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.1 dev: true - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0)(typescript@4.9.5): + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3336,13 +3273,13 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 5.62.0(eslint@8.46.0)(typescript@4.9.5) + '@eslint-community/regexpp': 4.9.1 + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@4.9.5) '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.46.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.46.0)(typescript@4.9.5) + '@typescript-eslint/type-utils': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@4.9.5) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.46.0 + eslint: 8.51.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare-lite: 1.4.0 @@ -3353,48 +3290,20 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.49.0)(typescript@4.9.5): - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 5.62.0(eslint@8.49.0)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.49.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.49.0)(typescript@4.9.5) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.49.0 - graphemer: 1.4.0 - ignore: 5.2.4 - natural-compare-lite: 1.4.0 - semver: 7.5.4 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/experimental-utils@5.62.0(eslint@8.46.0)(typescript@4.9.5): + /@typescript-eslint/experimental-utils@5.62.0(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==} 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.62.0(eslint@8.46.0)(typescript@4.9.5) - eslint: 8.46.0 + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + eslint: 8.51.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/parser@5.62.0(eslint@8.46.0)(typescript@4.9.5): + /@typescript-eslint/parser@5.62.0(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3408,27 +3317,7 @@ packages: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.46.0 - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser@5.62.0(eslint@8.49.0)(typescript@4.9.5): - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.49.0 + eslint: 8.51.0 typescript: 4.9.5 transitivePeerDependencies: - supports-color @@ -3442,7 +3331,7 @@ packages: '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/type-utils@5.62.0(eslint@8.46.0)(typescript@4.9.5): + /@typescript-eslint/type-utils@5.62.0(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3453,29 +3342,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.46.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@4.9.5) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.46.0 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/type-utils@5.62.0(eslint@8.49.0)(typescript@4.9.5): - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.49.0)(typescript@4.9.5) - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.49.0 + eslint: 8.51.0 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: @@ -3508,39 +3377,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.46.0)(typescript@4.9.5): - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.46.0) - '@types/json-schema': 7.0.12 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - eslint: 8.46.0 - eslint-scope: 5.1.1 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/utils@5.62.0(eslint@8.49.0)(typescript@4.9.5): + /@typescript-eslint/utils@5.62.0(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) - '@types/json-schema': 7.0.12 - '@types/semver': 7.5.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + '@types/json-schema': 7.0.14 + '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - eslint: 8.49.0 + eslint: 8.51.0 eslint-scope: 5.1.1 semver: 7.5.4 transitivePeerDependencies: @@ -3553,7 +3402,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: '@typescript-eslint/types': 5.62.0 - eslint-visitor-keys: 3.4.2 + eslint-visitor-keys: 3.4.3 dev: true /@ucanto/client@8.0.0: @@ -3566,8 +3415,8 @@ packages: /@ucanto/core@8.0.0: resolution: {integrity: sha512-Ne45bH0uUhAexNkJWj8tYsvKut58LaL3rsu30vxVh+ybkj47nD02mY8KqW+vCHY80E1ubPcjlHuCt0+efR9bgw==} dependencies: - '@ipld/car': 5.1.1 - '@ipld/dag-cbor': 9.0.4 + '@ipld/car': 5.2.2 + '@ipld/dag-cbor': 9.0.0 '@ipld/dag-ucan': 3.3.2 '@ucanto/interface': 8.0.0 multiformats: 11.0.2 @@ -3604,8 +3453,8 @@ packages: /@ucanto/validator@8.0.0: resolution: {integrity: sha512-S+cGKUVu074TT1FaoOyZa3mKf3CuEBLHLlE3TU1UoIC5Yp9WnvX+cDOGKIyfJ/HgHHBvAEDxYNOkNZbCATsRdA==} dependencies: - '@ipld/car': 5.1.1 - '@ipld/dag-cbor': 9.0.4 + '@ipld/car': 5.2.2 + '@ipld/dag-cbor': 9.0.0 '@ucanto/core': 8.0.0 '@ucanto/interface': 8.0.0 multiformats: 11.0.2 @@ -3613,7 +3462,7 @@ packages: /@vue/compiler-core@3.3.4: resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==} dependencies: - '@babel/parser': 7.22.10 + '@babel/parser': 7.23.0 '@vue/shared': 3.3.4 estree-walker: 2.0.2 source-map-js: 1.0.2 @@ -3629,15 +3478,15 @@ packages: /@vue/compiler-sfc@3.3.4: resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==} dependencies: - '@babel/parser': 7.22.10 + '@babel/parser': 7.23.0 '@vue/compiler-core': 3.3.4 '@vue/compiler-dom': 3.3.4 '@vue/compiler-ssr': 3.3.4 '@vue/reactivity-transform': 3.3.4 '@vue/shared': 3.3.4 estree-walker: 2.0.2 - magic-string: 0.30.2 - postcss: 8.4.27 + magic-string: 0.30.5 + postcss: 8.4.31 source-map-js: 1.0.2 dev: false @@ -3651,11 +3500,11 @@ packages: /@vue/reactivity-transform@3.3.4: resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==} dependencies: - '@babel/parser': 7.22.10 + '@babel/parser': 7.23.0 '@vue/compiler-core': 3.3.4 '@vue/shared': 3.3.4 estree-walker: 2.0.2 - magic-string: 0.30.2 + magic-string: 0.30.5 dev: false /@vue/shared@3.3.4: @@ -3681,16 +3530,17 @@ packages: web-streams-polyfill: 3.2.1 dev: false - /@web3-storage/data-segment@3.0.1: - resolution: {integrity: sha512-+e0KeqofejZ/1JLCdNmlEMCCSNf0PeJFXA/2Vw5+vWj4Nfko8sVLIGva86L8hXzm4dZGhWYU5m6JhX2U2Wn5Dg==} + /@web3-storage/data-segment@3.2.0: + resolution: {integrity: sha512-SM6eNumXzrXiQE2/J59+eEgCRZNYPxKhRoHX2QvV3/scD4qgcf4g+paWBc3UriLEY1rCboygGoPsnqYJNyZyfA==} dependencies: + '@ipld/dag-cbor': 9.0.6 multiformats: 11.0.2 sync-multihash-sha2: 1.0.0 /@web3-storage/sigv4@1.0.2: resolution: {integrity: sha512-ZUXKK10NmuQgPkqByhb1H3OQxkIM0CIn2BMPhGQw7vQw8WIzrBkk9IJiAVfJ/UVBFrf6uzPbx2lEBLt4diCMnQ==} dependencies: - '@noble/hashes': 1.3.1 + '@noble/hashes': 1.3.2 dev: true /@webassemblyjs/ast@1.11.6: @@ -4022,13 +3872,13 @@ packages: resolution: {integrity: sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==} dev: true - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 get-intrinsic: 1.2.1 is-string: 1.0.7 dev: true @@ -4037,54 +3887,55 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - /array.prototype.findlastindex@1.2.2: - resolution: {integrity: sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==} + /array.prototype.findlastindex@1.2.3: + resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 es-shim-unscopables: 1.0.0 get-intrinsic: 1.2.1 dev: true - /array.prototype.flat@1.3.1: - resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 es-shim-unscopables: 1.0.0 dev: true - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 es-shim-unscopables: 1.0.0 dev: true - /array.prototype.tosorted@1.1.1: - resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + /array.prototype.tosorted@1.1.2: + resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 es-shim-unscopables: 1.0.0 get-intrinsic: 1.2.1 dev: true - /arraybuffer.prototype.slice@1.0.1: - resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + /arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 call-bind: 1.0.2 - define-properties: 1.2.0 + define-properties: 1.2.1 + es-abstract: 1.22.2 get-intrinsic: 1.2.1 is-array-buffer: 3.0.2 is-shared-array-buffer: 1.0.2 @@ -4124,6 +3975,12 @@ packages: engines: {node: '>=8'} dev: true + /asynciterator.prototype@1.0.0: + resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} + dependencies: + has-symbols: 1.0.3 + dev: true + /at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} @@ -4134,19 +3991,19 @@ packages: engines: {node: '>=10.12.0'} dev: false - /autoprefixer@10.4.14(postcss@8.4.27): - resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} + /autoprefixer@10.4.16(postcss@8.4.31): + resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.21.10 - caniuse-lite: 1.0.30001519 - fraction.js: 4.2.0 + browserslist: 4.22.1 + caniuse-lite: 1.0.30001550 + fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true @@ -4158,24 +4015,24 @@ packages: /axios@0.25.0: resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.3 transitivePeerDependencies: - debug dev: true - /babel-loader@8.3.0(@babel/core@7.22.10)(webpack@5.88.2): + /babel-loader@8.3.0(@babel/core@7.23.2)(webpack@5.89.0): resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} engines: {node: '>= 8.9'} peerDependencies: '@babel/core': ^7.0.0 webpack: '>=2' dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.2 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /babel-plugin-apply-mdx-type-prop@1.6.22(@babel/core@7.12.9): @@ -4200,38 +4057,38 @@ packages: '@babel/helper-plugin-utils': 7.10.4 dev: true - /babel-plugin-polyfill-corejs2@0.4.5(@babel/core@7.22.10): - resolution: {integrity: sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==} + /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.2): + resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/compat-data': 7.22.9 - '@babel/core': 7.22.10 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.22.10) + '@babel/compat-data': 7.23.2 + '@babel/core': 7.23.2 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.8.3(@babel/core@7.22.10): - resolution: {integrity: sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==} + /babel-plugin-polyfill-corejs3@0.8.5(@babel/core@7.23.2): + resolution: {integrity: sha512-Q6CdATeAvbScWPNLB8lzSO7fgUVBkQt6zLgNlfyeCr/EQaEQR+bWiBYYPYAFyE528BMjRhL+1QBMOI4jc/c5TA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.22.10) - core-js-compat: 3.32.0 + '@babel/core': 7.23.2 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) + core-js-compat: 3.33.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.5.2(@babel/core@7.22.10): - resolution: {integrity: sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==} + /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.2): + resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.22.10) + '@babel/core': 7.23.2 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) transitivePeerDependencies: - supports-color dev: true @@ -4370,15 +4227,15 @@ packages: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} dev: true - /browserslist@4.21.10: - resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001519 - electron-to-chromium: 1.4.490 + caniuse-lite: 1.0.30001550 + electron-to-chromium: 1.4.557 node-releases: 2.0.13 - update-browserslist-db: 1.0.11(browserslist@4.21.10) + update-browserslist-db: 1.0.13(browserslist@4.22.1) dev: true /buffer-from@1.1.2: @@ -4426,7 +4283,7 @@ packages: istanbul-reports: 3.1.6 rimraf: 3.0.2 test-exclude: 6.0.0 - v8-to-istanbul: 9.1.0 + v8-to-istanbul: 9.1.3 yargs: 16.2.0 yargs-parser: 20.2.9 dev: true @@ -4445,7 +4302,7 @@ packages: istanbul-reports: 3.1.6 rimraf: 3.0.2 test-exclude: 6.0.0 - v8-to-istanbul: 9.1.0 + v8-to-istanbul: 9.1.3 yargs: 17.7.2 yargs-parser: 21.1.1 dev: true @@ -4466,7 +4323,7 @@ packages: /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 get-intrinsic: 1.2.1 dev: true @@ -4478,7 +4335,7 @@ packages: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /camelcase-css@2.0.1: @@ -4498,14 +4355,14 @@ packages: /caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: - browserslist: 4.21.10 - caniuse-lite: 1.0.30001519 + browserslist: 4.22.1 + caniuse-lite: 1.0.30001550 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true - /caniuse-lite@1.0.30001519: - resolution: {integrity: sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==} + /caniuse-lite@1.0.30001550: + resolution: {integrity: sha512-p82WjBYIypO0ukTsd/FG3Xxs+4tFeaY9pfT4amQL8KWtYH7H9nYwReGAbMTJ0hsmRO8IfDtsS6p3ZWj8+1c2RQ==} dev: true /cborg@1.10.2: @@ -4588,8 +4445,8 @@ packages: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} dev: true - /ci-info@3.8.0: - resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} dev: true @@ -4642,15 +4499,9 @@ packages: dependencies: restore-cursor: 4.0.0 - /cli-spinners@2.9.0: - resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==} - engines: {node: '>=6'} - dev: false - /cli-spinners@2.9.1: resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} engines: {node: '>=6'} - dev: true /cli-table3@0.6.3: resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} @@ -4747,8 +4598,8 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true - /combine-promises@1.1.0: - resolution: {integrity: sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==} + /combine-promises@1.2.0: + resolution: {integrity: sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==} engines: {node: '>=10'} dev: true @@ -4872,6 +4723,10 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} dev: true @@ -4881,7 +4736,7 @@ packages: engines: {node: '>= 0.6'} dev: true - /copy-webpack-plugin@11.0.0(webpack@5.88.2): + /copy-webpack-plugin@11.0.0(webpack@5.89.0): resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -4893,22 +4748,22 @@ packages: normalize-path: 3.0.0 schema-utils: 4.2.0 serialize-javascript: 6.0.1 - webpack: 5.88.2 + webpack: 5.89.0 dev: true - /core-js-compat@3.32.0: - resolution: {integrity: sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw==} + /core-js-compat@3.33.0: + resolution: {integrity: sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw==} dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 dev: true - /core-js-pure@3.32.0: - resolution: {integrity: sha512-qsev1H+dTNYpDUEURRuOXMvpdtAnNEvQWS/FMJ2Vb5AY8ZP4rAPQldkE27joykZPJTe0+IVgHZYh1P5Xu1/i1g==} + /core-js-pure@3.33.0: + resolution: {integrity: sha512-FKSIDtJnds/YFIEaZ4HszRX7hkxGpNKM7FC9aJ9WLJbSd3lD4vOltFuVIBLR8asSx9frkTSqL0dw90SKQxgKrg==} requiresBuild: true dev: true - /core-js@3.32.0: - resolution: {integrity: sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==} + /core-js@3.33.0: + resolution: {integrity: sha512-HoZr92+ZjFEKar5HS6MC776gYslNOKHt75mEBKWKnPeFDpZ6nH5OeF3S6HFT1mUAUZKrzkez05VboaX8myjSuw==} requiresBuild: true dev: true @@ -4920,7 +4775,7 @@ packages: resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} engines: {node: '>=8'} dependencies: - '@types/parse-json': 4.0.0 + '@types/parse-json': 4.0.1 import-fresh: 3.3.0 parse-json: 5.2.0 path-type: 4.0.0 @@ -4931,20 +4786,26 @@ packages: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} dependencies: - '@types/parse-json': 4.0.0 + '@types/parse-json': 4.0.1 import-fresh: 3.3.0 parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 - /cosmiconfig@8.2.0: - resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==} + /cosmiconfig@8.3.6(typescript@4.9.5): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 + typescript: 4.9.5 dev: true /cp-file@10.0.0: @@ -5002,33 +4863,33 @@ packages: type-fest: 1.4.0 dev: true - /css-declaration-sorter@6.4.1(postcss@8.4.27): + /css-declaration-sorter@6.4.1(postcss@8.4.31): resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} engines: {node: ^10 || ^12 || >=14} peerDependencies: postcss: ^8.0.9 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /css-loader@6.8.1(webpack@5.88.2): + /css-loader@6.8.1(webpack@5.89.0): resolution: {integrity: sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.27) - postcss: 8.4.27 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.27) - postcss-modules-local-by-default: 4.0.3(postcss@8.4.27) - postcss-modules-scope: 3.0.0(postcss@8.4.27) - postcss-modules-values: 4.0.0(postcss@8.4.27) + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.31) + postcss-modules-local-by-default: 4.0.3(postcss@8.4.31) + postcss-modules-scope: 3.0.0(postcss@8.4.31) + postcss-modules-values: 4.0.0(postcss@8.4.31) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.88.2 + webpack: 5.89.0 dev: true - /css-minimizer-webpack-plugin@4.2.2(clean-css@5.3.2)(webpack@5.88.2): + /css-minimizer-webpack-plugin@4.2.2(clean-css@5.3.2)(webpack@5.89.0): resolution: {integrity: sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -5054,13 +4915,13 @@ packages: optional: true dependencies: clean-css: 5.3.2 - cssnano: 5.1.15(postcss@8.4.27) - jest-worker: 29.6.2 - postcss: 8.4.27 + cssnano: 5.1.15(postcss@8.4.31) + jest-worker: 29.7.0 + postcss: 8.4.31 schema-utils: 4.2.0 serialize-javascript: 6.0.1 source-map: 0.6.1 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /css-select@4.3.0: @@ -5092,77 +4953,77 @@ packages: hasBin: true dev: true - /cssnano-preset-advanced@5.3.10(postcss@8.4.27): + /cssnano-preset-advanced@5.3.10(postcss@8.4.31): resolution: {integrity: sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - autoprefixer: 10.4.14(postcss@8.4.27) - cssnano-preset-default: 5.2.14(postcss@8.4.27) - postcss: 8.4.27 - postcss-discard-unused: 5.1.0(postcss@8.4.27) - postcss-merge-idents: 5.1.1(postcss@8.4.27) - postcss-reduce-idents: 5.2.0(postcss@8.4.27) - postcss-zindex: 5.1.0(postcss@8.4.27) + autoprefixer: 10.4.16(postcss@8.4.31) + cssnano-preset-default: 5.2.14(postcss@8.4.31) + postcss: 8.4.31 + postcss-discard-unused: 5.1.0(postcss@8.4.31) + postcss-merge-idents: 5.1.1(postcss@8.4.31) + postcss-reduce-idents: 5.2.0(postcss@8.4.31) + postcss-zindex: 5.1.0(postcss@8.4.31) dev: true - /cssnano-preset-default@5.2.14(postcss@8.4.27): + /cssnano-preset-default@5.2.14(postcss@8.4.31): resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - css-declaration-sorter: 6.4.1(postcss@8.4.27) - cssnano-utils: 3.1.0(postcss@8.4.27) - postcss: 8.4.27 - postcss-calc: 8.2.4(postcss@8.4.27) - postcss-colormin: 5.3.1(postcss@8.4.27) - postcss-convert-values: 5.1.3(postcss@8.4.27) - postcss-discard-comments: 5.1.2(postcss@8.4.27) - postcss-discard-duplicates: 5.1.0(postcss@8.4.27) - postcss-discard-empty: 5.1.1(postcss@8.4.27) - postcss-discard-overridden: 5.1.0(postcss@8.4.27) - postcss-merge-longhand: 5.1.7(postcss@8.4.27) - postcss-merge-rules: 5.1.4(postcss@8.4.27) - postcss-minify-font-values: 5.1.0(postcss@8.4.27) - postcss-minify-gradients: 5.1.1(postcss@8.4.27) - postcss-minify-params: 5.1.4(postcss@8.4.27) - postcss-minify-selectors: 5.2.1(postcss@8.4.27) - postcss-normalize-charset: 5.1.0(postcss@8.4.27) - postcss-normalize-display-values: 5.1.0(postcss@8.4.27) - postcss-normalize-positions: 5.1.1(postcss@8.4.27) - postcss-normalize-repeat-style: 5.1.1(postcss@8.4.27) - postcss-normalize-string: 5.1.0(postcss@8.4.27) - postcss-normalize-timing-functions: 5.1.0(postcss@8.4.27) - postcss-normalize-unicode: 5.1.1(postcss@8.4.27) - postcss-normalize-url: 5.1.0(postcss@8.4.27) - postcss-normalize-whitespace: 5.1.1(postcss@8.4.27) - postcss-ordered-values: 5.1.3(postcss@8.4.27) - postcss-reduce-initial: 5.1.2(postcss@8.4.27) - postcss-reduce-transforms: 5.1.0(postcss@8.4.27) - postcss-svgo: 5.1.0(postcss@8.4.27) - postcss-unique-selectors: 5.1.1(postcss@8.4.27) - dev: true - - /cssnano-utils@3.1.0(postcss@8.4.27): + css-declaration-sorter: 6.4.1(postcss@8.4.31) + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-calc: 8.2.4(postcss@8.4.31) + postcss-colormin: 5.3.1(postcss@8.4.31) + postcss-convert-values: 5.1.3(postcss@8.4.31) + postcss-discard-comments: 5.1.2(postcss@8.4.31) + postcss-discard-duplicates: 5.1.0(postcss@8.4.31) + postcss-discard-empty: 5.1.1(postcss@8.4.31) + postcss-discard-overridden: 5.1.0(postcss@8.4.31) + postcss-merge-longhand: 5.1.7(postcss@8.4.31) + postcss-merge-rules: 5.1.4(postcss@8.4.31) + postcss-minify-font-values: 5.1.0(postcss@8.4.31) + postcss-minify-gradients: 5.1.1(postcss@8.4.31) + postcss-minify-params: 5.1.4(postcss@8.4.31) + postcss-minify-selectors: 5.2.1(postcss@8.4.31) + postcss-normalize-charset: 5.1.0(postcss@8.4.31) + postcss-normalize-display-values: 5.1.0(postcss@8.4.31) + postcss-normalize-positions: 5.1.1(postcss@8.4.31) + postcss-normalize-repeat-style: 5.1.1(postcss@8.4.31) + postcss-normalize-string: 5.1.0(postcss@8.4.31) + postcss-normalize-timing-functions: 5.1.0(postcss@8.4.31) + postcss-normalize-unicode: 5.1.1(postcss@8.4.31) + postcss-normalize-url: 5.1.0(postcss@8.4.31) + postcss-normalize-whitespace: 5.1.1(postcss@8.4.31) + postcss-ordered-values: 5.1.3(postcss@8.4.31) + postcss-reduce-initial: 5.1.2(postcss@8.4.31) + postcss-reduce-transforms: 5.1.0(postcss@8.4.31) + postcss-svgo: 5.1.0(postcss@8.4.31) + postcss-unique-selectors: 5.1.1(postcss@8.4.31) + dev: true + + /cssnano-utils@3.1.0(postcss@8.4.31): resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /cssnano@5.1.15(postcss@8.4.27): + /cssnano@5.1.15(postcss@8.4.31): resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-preset-default: 5.2.14(postcss@8.4.27) + cssnano-preset-default: 5.2.14(postcss@8.4.31) lilconfig: 2.1.0 - postcss: 8.4.27 + postcss: 8.4.31 yaml: 1.10.2 dev: true @@ -5261,8 +5122,8 @@ packages: resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} dev: true - /define-data-property@1.1.0: - resolution: {integrity: sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==} + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.1 @@ -5275,19 +5136,11 @@ packages: engines: {node: '>=8'} dev: true - /define-properties@1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} - engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - dev: true - /define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} dependencies: - define-data-property: 1.1.0 + define-data-property: 1.1.1 has-property-descriptors: 1.0.0 object-keys: 1.1.1 dev: true @@ -5312,7 +5165,7 @@ packages: hasBin: true dependencies: '@babel/parser': 7.16.4 - '@babel/traverse': 7.22.10 + '@babel/traverse': 7.23.2 '@vue/compiler-sfc': 3.3.4 camelcase: 6.3.0 cosmiconfig: 7.1.0 @@ -5329,8 +5182,8 @@ packages: query-ast: 1.0.5 readdirp: 3.6.0 require-package-name: 2.0.1 - resolve: 1.22.4 - sass: 1.65.1 + resolve: 1.22.8 + sass: 1.69.4 scss-parser: 1.0.6 semver: 7.5.4 yargs: 16.2.0 @@ -5409,8 +5262,8 @@ packages: resolution: {integrity: sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==} dev: true - /dns-packet@5.6.0: - resolution: {integrity: sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==} + /dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} dependencies: '@leichtgewicht/ip-codec': 2.0.4 @@ -5477,7 +5330,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /dot-prop@5.3.0: @@ -5516,12 +5369,12 @@ packages: encoding: 0.1.13 dev: false - /electron-to-chromium@1.4.490: - resolution: {integrity: sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A==} + /electron-to-chromium@1.4.557: + resolution: {integrity: sha512-6x0zsxyMXpnMJnHrondrD3SuAeKcwij9S+83j2qHAQPXbGTDDfgImzzwgGlzrIcXbHQ42tkG4qA6U860cImNhw==} dev: true - /emoji-regex@10.2.1: - resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==} + /emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} dev: true /emoji-regex@8.0.0: @@ -5586,22 +5439,22 @@ packages: dependencies: is-arrayish: 0.2.1 - /es-abstract@1.22.1: - resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + /es-abstract@1.22.2: + resolution: {integrity: sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 - arraybuffer.prototype.slice: 1.0.1 + arraybuffer.prototype.slice: 1.0.2 available-typed-arrays: 1.0.5 call-bind: 1.0.2 es-set-tostringtag: 2.0.1 es-to-primitive: 1.2.1 - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 get-intrinsic: 1.2.1 get-symbol-description: 1.0.0 globalthis: 1.0.3 gopd: 1.0.1 - has: 1.0.3 + has: 1.0.4 has-property-descriptors: 1.0.0 has-proto: 1.0.1 has-symbols: 1.0.3 @@ -5614,15 +5467,15 @@ packages: is-string: 1.0.7 is-typed-array: 1.1.12 is-weakref: 1.0.2 - object-inspect: 1.12.3 + object-inspect: 1.13.0 object-keys: 1.1.1 object.assign: 4.1.4 - regexp.prototype.flags: 1.5.0 - safe-array-concat: 1.0.0 + regexp.prototype.flags: 1.5.1 + safe-array-concat: 1.0.1 safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.7 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 typed-array-buffer: 1.0.0 typed-array-byte-length: 1.0.0 typed-array-byte-offset: 1.0.0 @@ -5631,8 +5484,27 @@ packages: which-typed-array: 1.1.11 dev: true - /es-module-lexer@1.3.0: - resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==} + /es-iterator-helpers@1.0.15: + resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + dependencies: + asynciterator.prototype: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.1 + es-abstract: 1.22.2 + es-set-tostringtag: 2.0.1 + function-bind: 1.1.2 + get-intrinsic: 1.2.1 + globalthis: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + iterator.prototype: 1.1.2 + safe-array-concat: 1.0.1 + dev: true + + /es-module-lexer@1.3.1: + resolution: {integrity: sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==} dev: true /es-set-tostringtag@2.0.1: @@ -5640,14 +5512,14 @@ packages: engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.1 - has: 1.0.3 + has: 1.0.4 has-tostringtag: 1.0.0 dev: true /es-shim-unscopables@1.0.0: resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} dependencies: - has: 1.0.3 + has: 1.0.4 dev: true /es-to-primitive@1.2.1: @@ -5724,26 +5596,26 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - /eslint-config-prettier@8.10.0(eslint@8.46.0): + /eslint-config-prettier@8.10.0(eslint@8.51.0): resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.46.0 + eslint: 8.51.0 dev: true - /eslint-config-standard-jsx@11.0.0(eslint-plugin-react@7.33.1)(eslint@8.46.0): + /eslint-config-standard-jsx@11.0.0(eslint-plugin-react@7.33.2)(eslint@8.51.0): resolution: {integrity: sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ==} peerDependencies: eslint: ^8.8.0 eslint-plugin-react: ^7.28.0 dependencies: - eslint: 8.46.0 - eslint-plugin-react: 7.33.1(eslint@8.49.0) + eslint: 8.51.0 + eslint-plugin-react: 7.33.2(eslint@8.51.0) dev: true - /eslint-config-standard-with-typescript@30.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0)(typescript@4.9.5): + /eslint-config-standard-with-typescript@30.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-/Ltst1BCZCWrGmqprLHBkTwuAbcoQrR8uMeSzZAv1vHKIVg+2nFje+DULA30SW01yCNhnx0a8yhZBkR0ZZPp+w==} peerDependencies: '@typescript-eslint/eslint-plugin': ^5.0.0 @@ -5753,19 +5625,19 @@ packages: eslint-plugin-promise: ^6.0.0 typescript: '*' dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0)(typescript@4.9.5) - '@typescript-eslint/parser': 5.62.0(eslint@8.46.0)(typescript@4.9.5) - eslint: 8.46.0 - eslint-config-standard: 17.0.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0) - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0) - eslint-plugin-n: 15.7.0(eslint@8.46.0) - eslint-plugin-promise: 6.1.1(eslint@8.46.0) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.51.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + eslint: 8.51.0 + eslint-config-standard: 17.0.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0) + eslint-plugin-n: 15.7.0(eslint@8.51.0) + eslint-plugin-promise: 6.1.1(eslint@8.51.0) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /eslint-config-standard-with-typescript@34.0.1(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0)(typescript@4.9.5): + /eslint-config-standard-with-typescript@34.0.1(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-J7WvZeLtd0Vr9F+v4dZbqJCLD16cbIy4U+alJMq4MiXdpipdBM3U5NkXaGUjePc4sb1ZE01U9g6VuTBpHHz1fg==} peerDependencies: '@typescript-eslint/eslint-plugin': ^5.43.0 @@ -5775,19 +5647,19 @@ packages: eslint-plugin-promise: ^6.0.0 typescript: '*' dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.49.0)(typescript@4.9.5) - '@typescript-eslint/parser': 5.62.0(eslint@8.46.0)(typescript@4.9.5) - eslint: 8.46.0 - eslint-config-standard: 17.0.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0) - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0) - eslint-plugin-n: 15.7.0(eslint@8.46.0) - eslint-plugin-promise: 6.1.1(eslint@8.46.0) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.51.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + eslint: 8.51.0 + eslint-config-standard: 17.0.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0) + eslint-plugin-n: 15.7.0(eslint@8.51.0) + eslint-plugin-promise: 6.1.1(eslint@8.51.0) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /eslint-config-standard@17.0.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0): + /eslint-config-standard@17.0.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0): resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==} peerDependencies: eslint: ^8.0.1 @@ -5795,13 +5667,13 @@ packages: eslint-plugin-n: ^15.0.0 eslint-plugin-promise: ^6.0.0 dependencies: - eslint: 8.46.0 - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.49.0) - eslint-plugin-n: 15.7.0(eslint@8.49.0) - eslint-plugin-promise: 6.1.1(eslint@8.49.0) + eslint: 8.51.0 + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0) + eslint-plugin-n: 15.7.0(eslint@8.51.0) + eslint-plugin-promise: 6.1.1(eslint@8.51.0) dev: true - /eslint-config-standard@17.1.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0): + /eslint-config-standard@17.1.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0): resolution: {integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==} engines: {node: '>=12.0.0'} peerDependencies: @@ -5810,20 +5682,20 @@ packages: eslint-plugin-n: '^15.0.0 || ^16.0.0 ' eslint-plugin-promise: ^6.0.0 dependencies: - eslint: 8.46.0 - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0) - eslint-plugin-n: 15.7.0(eslint@8.46.0) - eslint-plugin-promise: 6.1.1(eslint@8.46.0) + eslint: 8.51.0 + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0) + eslint-plugin-n: 15.7.0(eslint@8.51.0) + eslint-plugin-promise: 6.1.1(eslint@8.51.0) dev: true - /eslint-etc@5.2.1(eslint@8.46.0)(typescript@4.9.5): + /eslint-etc@5.2.1(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-lFJBSiIURdqQKq9xJhvSJFyPA+VeTh5xvk24e8pxVL7bwLBtGF60C/KRkLTMrvCZ6DA3kbPuYhLWY0TZMlqTsg==} peerDependencies: eslint: ^8.0.0 typescript: '>=4.0.0' dependencies: - '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.46.0)(typescript@4.9.5) - eslint: 8.46.0 + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + eslint: 8.51.0 tsutils: 3.21.0(typescript@4.9.5) tsutils-etc: 1.4.2(tsutils@3.21.0)(typescript@4.9.5) typescript: 4.9.5 @@ -5836,12 +5708,12 @@ packages: dependencies: debug: 3.2.7 is-core-module: 2.13.0 - resolve: 1.22.4 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.46.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.51.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -5862,121 +5734,45 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.46.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@4.9.5) debug: 3.2.7 - eslint: 8.46.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.49.0)(typescript@4.9.5) - debug: 3.2.7 - eslint: 8.49.0 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-es@4.1.0(eslint@8.46.0): + /eslint-plugin-es@4.1.0(eslint@8.51.0): resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} engines: {node: '>=8.10.0'} peerDependencies: eslint: '>=4.19.1' dependencies: - eslint: 8.46.0 + eslint: 8.51.0 eslint-utils: 2.1.0 regexpp: 3.2.0 dev: true - /eslint-plugin-es@4.1.0(eslint@8.49.0): - resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} - engines: {node: '>=8.10.0'} - peerDependencies: - eslint: '>=4.19.1' - dependencies: - eslint: 8.49.0 - eslint-utils: 2.1.0 - regexpp: 3.2.0 - dev: true - - /eslint-plugin-etc@2.0.3(eslint@8.46.0)(typescript@4.9.5): + /eslint-plugin-etc@2.0.3(eslint@8.51.0)(typescript@4.9.5): resolution: {integrity: sha512-o5RS/0YwtjlGKWjhKojgmm82gV1b4NQUuwk9zqjy9/EjxNFKKYCaF+0M7DkYBn44mJ6JYFZw3Ft249dkKuR1ew==} peerDependencies: eslint: ^8.0.0 typescript: '>=4.0.0' dependencies: '@phenomnomnominal/tsquery': 5.0.1(typescript@4.9.5) - '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.46.0)(typescript@4.9.5) - eslint: 8.46.0 - eslint-etc: 5.2.1(eslint@8.46.0)(typescript@4.9.5) + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + eslint: 8.51.0 + eslint-etc: 5.2.1(eslint@8.51.0)(typescript@4.9.5) requireindex: 1.2.0 - tslib: 2.6.1 + tslib: 2.6.2 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0): - resolution: {integrity: sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.46.0)(typescript@4.9.5) - array-includes: 3.1.6 - array.prototype.findlastindex: 1.2.2 - array.prototype.flat: 1.3.1 - array.prototype.flatmap: 1.3.1 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.46.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.46.0) - has: 1.0.3 - is-core-module: 2.13.0 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.6 - object.groupby: 1.0.0 - object.values: 1.1.6 - resolve: 1.22.4 - semver: 6.3.1 - tsconfig-paths: 3.14.2 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - dev: true - - /eslint-plugin-import@2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.49.0): - resolution: {integrity: sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==} + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0): + resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -5985,24 +5781,23 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.49.0)(typescript@4.9.5) - array-includes: 3.1.6 - array.prototype.findlastindex: 1.2.2 - array.prototype.flat: 1.3.1 - array.prototype.flatmap: 1.3.1 + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.3 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.49.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0) - has: 1.0.3 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.51.0) + has: 1.0.4 is-core-module: 2.13.0 is-glob: 4.0.3 minimatch: 3.1.2 - object.fromentries: 2.0.6 - object.groupby: 1.0.0 - object.values: 1.1.6 - resolve: 1.22.4 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 + object.values: 1.1.7 semver: 6.3.1 tsconfig-paths: 3.14.2 transitivePeerDependencies: @@ -6011,7 +5806,7 @@ packages: - supports-color dev: true - /eslint-plugin-jsdoc@39.9.1(eslint@8.46.0): + /eslint-plugin-jsdoc@39.9.1(eslint@8.51.0): resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} peerDependencies: @@ -6021,7 +5816,7 @@ packages: comment-parser: 1.3.1 debug: 4.3.4(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint: 8.46.0 + eslint: 8.51.0 esquery: 1.5.0 semver: 7.5.4 spdx-expression-parse: 3.0.1 @@ -6029,7 +5824,7 @@ packages: - supports-color dev: true - /eslint-plugin-jsdoc@40.3.0(eslint@8.46.0): + /eslint-plugin-jsdoc@40.3.0(eslint@8.51.0): resolution: {integrity: sha512-EhCqpzRkxoT2DUB4AnrU0ggBYvTh3bWrLZzQTupq6vSVE6XzNwJVKsOHa41GCoevnsWMBNmoDVjXWGqckjuG1g==} engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} peerDependencies: @@ -6039,7 +5834,7 @@ packages: comment-parser: 1.3.1 debug: 4.3.4(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint: 8.46.0 + eslint: 8.51.0 esquery: 1.5.0 semver: 7.5.4 spdx-expression-parse: 3.0.1 @@ -6047,37 +5842,20 @@ packages: - supports-color dev: true - /eslint-plugin-n@15.7.0(eslint@8.46.0): - resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==} - engines: {node: '>=12.22.0'} - peerDependencies: - eslint: '>=7.0.0' - dependencies: - builtins: 5.0.1 - eslint: 8.46.0 - eslint-plugin-es: 4.1.0(eslint@8.46.0) - eslint-utils: 3.0.0(eslint@8.46.0) - ignore: 5.2.4 - is-core-module: 2.13.0 - minimatch: 3.1.2 - resolve: 1.22.4 - semver: 7.5.4 - dev: true - - /eslint-plugin-n@15.7.0(eslint@8.49.0): + /eslint-plugin-n@15.7.0(eslint@8.51.0): resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==} engines: {node: '>=12.22.0'} peerDependencies: eslint: '>=7.0.0' dependencies: builtins: 5.0.1 - eslint: 8.49.0 - eslint-plugin-es: 4.1.0(eslint@8.49.0) - eslint-utils: 3.0.0(eslint@8.49.0) + eslint: 8.51.0 + eslint-plugin-es: 4.1.0(eslint@8.51.0) + eslint-utils: 3.0.0(eslint@8.51.0) ignore: 5.2.4 is-core-module: 2.13.0 minimatch: 3.1.2 - resolve: 1.22.4 + resolve: 1.22.8 semver: 7.5.4 dev: true @@ -6086,92 +5864,60 @@ packages: engines: {node: '>=5.0.0'} dev: true - /eslint-plugin-promise@6.1.1(eslint@8.46.0): + /eslint-plugin-promise@6.1.1(eslint@8.51.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.46.0 + eslint: 8.51.0 dev: true - /eslint-plugin-promise@6.1.1(eslint@8.49.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.49.0 - dev: true - - /eslint-plugin-react-hooks@4.6.0(eslint@8.46.0): + /eslint-plugin-react-hooks@4.6.0(eslint@8.51.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.46.0 - dev: true - - /eslint-plugin-react@7.33.1(eslint@8.46.0): - resolution: {integrity: sha512-L093k0WAMvr6VhNwReB8VgOq5s2LesZmrpPdKz/kZElQDzqS7G7+DnKoqT+w4JwuiGeAhAvHO0fvy0Eyk4ejDA==} - 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.46.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.1 - string.prototype.matchall: 4.0.8 + eslint: 8.51.0 dev: true - /eslint-plugin-react@7.33.1(eslint@8.49.0): - resolution: {integrity: sha512-L093k0WAMvr6VhNwReB8VgOq5s2LesZmrpPdKz/kZElQDzqS7G7+DnKoqT+w4JwuiGeAhAvHO0fvy0Eyk4ejDA==} + /eslint-plugin-react@7.33.2(eslint@8.51.0): + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} 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 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.2 doctrine: 2.1.0 - eslint: 8.49.0 + es-iterator-helpers: 1.0.15 + eslint: 8.51.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 prop-types: 15.8.1 - resolve: 2.0.0-next.4 + resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.8 + string.prototype.matchall: 4.0.10 dev: true - /eslint-plugin-unicorn@45.0.2(eslint@8.46.0): + /eslint-plugin-unicorn@45.0.2(eslint@8.51.0): resolution: {integrity: sha512-Y0WUDXRyGDMcKLiwgL3zSMpHrXI00xmdyixEGIg90gHnj0PcHY4moNv3Ppje/kDivdAy5vUeUr7z211ImPv2gw==} engines: {node: '>=14.18'} peerDependencies: eslint: '>=8.28.0' dependencies: - '@babel/helper-validator-identifier': 7.22.5 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.46.0) - ci-info: 3.8.0 + '@babel/helper-validator-identifier': 7.22.20 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + ci-info: 3.9.0 clean-regexp: 1.0.0 - eslint: 8.46.0 + eslint: 8.51.0 esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 @@ -6186,17 +5932,17 @@ packages: strip-indent: 3.0.0 dev: true - /eslint-plugin-unicorn@46.0.1(eslint@8.46.0): + /eslint-plugin-unicorn@46.0.1(eslint@8.51.0): resolution: {integrity: sha512-setGhMTiLAddg1asdwjZ3hekIN5zLznNa5zll7pBPwFOka6greCKDQydfqy4fqyUhndi74wpDzClSQMEcmOaew==} engines: {node: '>=14.18'} peerDependencies: eslint: '>=8.28.0' dependencies: - '@babel/helper-validator-identifier': 7.22.5 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.46.0) - ci-info: 3.8.0 + '@babel/helper-validator-identifier': 7.22.20 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + ci-info: 3.9.0 clean-regexp: 1.0.0 - eslint: 8.46.0 + eslint: 8.51.0 esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 @@ -6234,23 +5980,13 @@ packages: eslint-visitor-keys: 1.3.0 dev: true - /eslint-utils@3.0.0(eslint@8.46.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.46.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-utils@3.0.0(eslint@8.49.0): + /eslint-utils@3.0.0(eslint@8.51.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.49.0 + eslint: 8.51.0 eslint-visitor-keys: 2.1.0 dev: true @@ -6264,71 +6000,20 @@ packages: engines: {node: '>=10'} dev: true - /eslint-visitor-keys@3.4.2: - resolution: {integrity: sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.46.0: - resolution: {integrity: sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==} + /eslint@8.51.0: + resolution: {integrity: sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.46.0) - '@eslint-community/regexpp': 4.6.2 - '@eslint/eslintrc': 2.1.1 - '@eslint/js': 8.46.0 - '@humanwhocodes/config-array': 0.11.10 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.2 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.20.0 - graphemer: 1.4.0 - ignore: 5.2.4 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.3 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint@8.49.0: - resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) - '@eslint-community/regexpp': 4.8.1 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + '@eslint-community/regexpp': 4.9.1 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.49.0 + '@eslint/js': 8.51.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -6347,7 +6032,7 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.21.0 + globals: 13.23.0 graphemer: 1.4.0 ignore: 5.2.4 imurmurhash: 0.1.4 @@ -6372,7 +6057,7 @@ packages: dependencies: acorn: 8.10.0 acorn-jsx: 5.3.2(acorn@8.10.0) - eslint-visitor-keys: 3.4.2 + eslint-visitor-keys: 3.4.3 dev: true /esprima@4.0.1: @@ -6427,7 +6112,7 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 require-like: 0.1.2 dev: true @@ -6435,6 +6120,10 @@ packages: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} dev: true + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: true + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -6553,8 +6242,8 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - /fast-fifo@1.3.0: - resolution: {integrity: sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw==} + /fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} dev: false /fast-glob@3.3.1: @@ -6607,10 +6296,10 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.0.4 + flat-cache: 3.1.1 dev: true - /file-loader@6.2.0(webpack@5.88.2): + /file-loader@6.2.0(webpack@5.89.0): resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -6618,7 +6307,7 @@ packages: dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /filesize@8.0.7: @@ -6678,11 +6367,12 @@ packages: path-exists: 4.0.0 dev: true - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} - engines: {node: ^10.12.0 || >=12.0.0} + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} dependencies: - flatted: 3.2.7 + flatted: 3.2.9 + keyv: 4.5.4 rimraf: 3.0.2 dev: true @@ -6691,12 +6381,12 @@ packages: hasBin: true dev: true - /flatted@3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} dev: true - /follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + /follow-redirects@1.15.3: + resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -6719,7 +6409,7 @@ packages: signal-exit: 3.0.7 dev: true - /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.49.0)(typescript@4.9.5)(webpack@5.88.2): + /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.51.0)(typescript@4.9.5)(webpack@5.89.0): resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: @@ -6733,13 +6423,13 @@ packages: vue-template-compiler: optional: true dependencies: - '@babel/code-frame': 7.22.10 - '@types/json-schema': 7.0.12 + '@babel/code-frame': 7.22.13 + '@types/json-schema': 7.0.14 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 deepmerge: 4.3.1 - eslint: 8.49.0 + eslint: 8.51.0 fs-extra: 9.1.0 glob: 7.2.3 memfs: 3.5.3 @@ -6748,7 +6438,7 @@ packages: semver: 7.5.4 tapable: 1.1.3 typescript: 4.9.5 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /forwarded@0.2.0: @@ -6760,8 +6450,8 @@ packages: resolution: {integrity: sha512-+IpghzGszM7ebMuYzoILYvQMHXgtyZbIwlx2VYY4yLsT4SVU02ur6g7LCoLAxaD9GIv2oTAdiuHUxBH1liEQ7g==} dev: false - /fraction.js@4.2.0: - resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true /fresh@0.5.2: @@ -6788,8 +6478,8 @@ packages: universalify: 2.0.0 dev: true - /fs-monkey@1.0.4: - resolution: {integrity: sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==} + /fs-monkey@1.0.5: + resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} dev: true /fs.realpath@1.0.0: @@ -6803,16 +6493,17 @@ packages: requiresBuild: true optional: true - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 functions-have-names: 1.2.3 dev: true @@ -6832,8 +6523,8 @@ packages: /get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} dependencies: - function-bind: 1.1.1 - has: 1.0.3 + function-bind: 1.1.2 + has: 1.0.4 has-proto: 1.0.1 has-symbols: 1.0.3 dev: true @@ -6953,15 +6644,8 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - /globals@13.20.0: - resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: true - - /globals@13.21.0: - resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -6971,7 +6655,7 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} dependencies: - define-properties: 1.2.0 + define-properties: 1.2.1 dev: true /globby@11.1.0: @@ -7010,7 +6694,7 @@ packages: '@sindresorhus/is': 0.14.0 '@szmarczak/http-timer': 1.1.2 '@types/keyv': 3.1.4 - '@types/responselike': 1.0.0 + '@types/responselike': 1.0.1 cacheable-request: 6.1.0 decompress-response: 3.3.0 duplexer3: 0.1.5 @@ -7052,7 +6736,7 @@ packages: engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: sparse-array: 1.3.2 - uint8arrays: 4.0.6 + uint8arrays: 4.0.3 dev: true /handle-thing@2.0.1: @@ -7112,16 +6796,14 @@ packages: engines: {node: '>=8'} dev: true - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + /has@1.0.4: + resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 /hast-to-hyperscript@9.0.1: resolution: {integrity: sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 comma-separated-tokens: 1.0.8 property-information: 5.6.0 space-separated-tokens: 1.1.5 @@ -7148,7 +6830,7 @@ packages: /hast-util-raw@6.0.1: resolution: {integrity: sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==} dependencies: - '@types/hast': 2.3.5 + '@types/hast': 2.3.7 hast-util-from-parse5: 6.0.1 hast-util-to-parse5: 6.0.0 html-void-elements: 1.0.5 @@ -7173,7 +6855,7 @@ packages: /hastscript@6.0.0: resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} dependencies: - '@types/hast': 2.3.5 + '@types/hast': 2.3.7 comma-separated-tokens: 1.0.8 hast-util-parse-selector: 2.2.5 property-information: 5.6.0 @@ -7184,22 +6866,22 @@ packages: resolution: {integrity: sha512-nDWeib3SxaHZRz0YhRkOnBDT5LAyMx6BXITO5xsocUJh4bSaqn7ha/h9Zlhw0WLtfxSVEXv96kjp/LQts12B9A==} engines: {node: '>=14'} dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0)(typescript@4.9.5) - '@typescript-eslint/parser': 5.62.0(eslint@8.46.0)(typescript@4.9.5) - eslint: 8.46.0 - eslint-config-prettier: 8.10.0(eslint@8.46.0) - eslint-config-standard: 17.1.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0) - eslint-config-standard-with-typescript: 30.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0)(typescript@4.9.5) - eslint-plugin-etc: 2.0.3(eslint@8.46.0)(typescript@4.9.5) - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0) - eslint-plugin-jsdoc: 39.9.1(eslint@8.46.0) - eslint-plugin-n: 15.7.0(eslint@8.46.0) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.51.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + eslint: 8.51.0 + eslint-config-prettier: 8.10.0(eslint@8.51.0) + eslint-config-standard: 17.1.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0) + eslint-config-standard-with-typescript: 30.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0)(typescript@4.9.5) + eslint-plugin-etc: 2.0.3(eslint@8.51.0)(typescript@4.9.5) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0) + eslint-plugin-jsdoc: 39.9.1(eslint@8.51.0) + eslint-plugin-n: 15.7.0(eslint@8.51.0) eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-promise: 6.1.1(eslint@8.46.0) - eslint-plugin-react: 7.33.1(eslint@8.46.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.46.0) - eslint-plugin-unicorn: 45.0.2(eslint@8.46.0) - lint-staged: 13.2.3 + eslint-plugin-promise: 6.1.1(eslint@8.51.0) + eslint-plugin-react: 7.33.2(eslint@8.51.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.51.0) + eslint-plugin-unicorn: 45.0.2(eslint@8.51.0) + lint-staged: 13.2.0 prettier: 2.8.3 simple-git-hooks: 2.9.0 typescript: 4.9.5 @@ -7214,22 +6896,22 @@ packages: resolution: {integrity: sha512-wFecqDH+tW/Ajg993eFGLe1i7rVGrZkSZv1Zitsj3dGnvURLa/+Up+L46+MPFFCq7qhwBLoooL/Rs3cpjqbTVg==} engines: {node: '>=14'} dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.49.0)(typescript@4.9.5) - '@typescript-eslint/parser': 5.62.0(eslint@8.49.0)(typescript@4.9.5) - eslint: 8.46.0 - eslint-config-prettier: 8.10.0(eslint@8.46.0) - eslint-config-standard: 17.1.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0) - eslint-config-standard-with-typescript: 34.0.1(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0)(typescript@4.9.5) - eslint-plugin-etc: 2.0.3(eslint@8.46.0)(typescript@4.9.5) - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0) - eslint-plugin-jsdoc: 40.3.0(eslint@8.46.0) - eslint-plugin-n: 15.7.0(eslint@8.46.0) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.51.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.51.0)(typescript@4.9.5) + eslint: 8.51.0 + eslint-config-prettier: 8.10.0(eslint@8.51.0) + eslint-config-standard: 17.1.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0) + eslint-config-standard-with-typescript: 34.0.1(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0)(typescript@4.9.5) + eslint-plugin-etc: 2.0.3(eslint@8.51.0)(typescript@4.9.5) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0) + eslint-plugin-jsdoc: 40.3.0(eslint@8.51.0) + eslint-plugin-n: 15.7.0(eslint@8.51.0) eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-promise: 6.1.1(eslint@8.46.0) - eslint-plugin-react: 7.33.1(eslint@8.46.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.46.0) - eslint-plugin-unicorn: 46.0.1(eslint@8.46.0) - lint-staged: 13.2.3 + eslint-plugin-promise: 6.1.1(eslint@8.51.0) + eslint-plugin-react: 7.33.2(eslint@8.51.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.51.0) + eslint-plugin-unicorn: 46.0.1(eslint@8.51.0) + lint-staged: 13.2.0 prettier: 2.8.8 simple-git-hooks: 2.9.0 typescript: 4.9.5 @@ -7248,7 +6930,7 @@ packages: /history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.2 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.1 @@ -7294,7 +6976,7 @@ packages: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.19.2 + terser: 5.22.0 dev: true /html-tags@3.3.1: @@ -7306,7 +6988,7 @@ packages: resolution: {integrity: sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==} dev: true - /html-webpack-plugin@5.5.3(webpack@5.88.2): + /html-webpack-plugin@5.5.3(webpack@5.89.0): resolution: {integrity: sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==} engines: {node: '>=10.13.0'} peerDependencies: @@ -7317,7 +6999,7 @@ packages: lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /htmlparser2@6.1.0: @@ -7362,7 +7044,7 @@ packages: resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} dev: true - /http-proxy-middleware@2.0.6(@types/express@4.17.17): + /http-proxy-middleware@2.0.6(@types/express@4.17.20): resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} engines: {node: '>=12.0.0'} peerDependencies: @@ -7371,8 +7053,8 @@ packages: '@types/express': optional: true dependencies: - '@types/express': 4.17.17 - '@types/http-proxy': 1.17.11 + '@types/express': 4.17.20 + '@types/http-proxy': 1.17.13 http-proxy: 1.18.1 is-glob: 4.0.3 is-plain-obj: 3.0.0 @@ -7386,7 +7068,7 @@ packages: engines: {node: '>=8.0.0'} dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.2 + follow-redirects: 1.15.3 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -7427,13 +7109,13 @@ packages: safer-buffer: 2.1.2 dev: false - /icss-utils@5.1.0(postcss@8.4.27): + /icss-utils@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true /ieee754@1.2.1: @@ -7455,8 +7137,8 @@ packages: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} dev: true - /immutable@4.3.2: - resolution: {integrity: sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==} + /immutable@4.3.4: + resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} dev: false /import-fresh@3.3.0: @@ -7552,7 +7234,7 @@ packages: engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.1 - has: 1.0.3 + has: 1.0.4 side-channel: 1.0.4 dev: true @@ -7580,21 +7262,21 @@ packages: resolution: {integrity: sha512-ZOBZzTlTctLfvzMTVNz4EGnY4Bj4C8SqgbkarVN0Q0A5XEAgT4Rx4lTb9HlfJxvk44TTOz4RmvBTzcZWhaGs4g==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: - '@ipld/dag-cbor': 9.0.4 - '@ipld/dag-pb': 4.0.5 - '@multiformats/murmur3': 2.1.6 + '@ipld/dag-cbor': 9.0.0 + '@ipld/dag-pb': 4.0.6 + '@multiformats/murmur3': 2.1.7 err-code: 3.0.1 hamt-sharding: 3.0.2 interface-blockstore: 4.0.1 ipfs-unixfs: 9.0.1 it-last: 2.0.1 it-map: 2.0.1 - it-parallel: 3.0.3 + it-parallel: 3.0.4 it-pipe: 2.0.5 it-pushable: 3.2.1 multiformats: 11.0.2 - p-queue: 7.3.0 - uint8arrays: 4.0.6 + p-queue: 7.4.1 + uint8arrays: 4.0.3 dev: true /ipfs-unixfs@9.0.1: @@ -7602,7 +7284,7 @@ packages: engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: err-code: 3.0.1 - protobufjs: 7.2.4 + protobufjs: 7.2.5 dev: true /ipfs-utils@9.0.14: @@ -7621,8 +7303,8 @@ packages: it-to-stream: 1.0.0 merge-options: 3.0.4 nanoid: 3.3.6 - native-fetch: 3.0.0(node-fetch@2.6.12) - node-fetch: 2.6.12 + native-fetch: 3.0.0(node-fetch@2.7.0) + node-fetch: 2.7.0 react-native-fetch-api: 3.0.0 stream-to-it: 0.2.4 transitivePeerDependencies: @@ -7659,6 +7341,13 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: @@ -7706,7 +7395,7 @@ packages: /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: - has: 1.0.3 + has: 1.0.4 /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} @@ -7738,6 +7427,12 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.2 + dev: true + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -7776,12 +7471,16 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} + /is-map@2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true + /is-nan@1.3.2: resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 + define-properties: 1.2.1 dev: true /is-negative-zero@2.0.2: @@ -7840,6 +7539,11 @@ packages: isobject: 3.0.1 dev: true + /is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: true + /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -7858,6 +7562,10 @@ packages: engines: {node: '>=6'} dev: true + /is-set@2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true + /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: @@ -7912,12 +7620,23 @@ packages: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} + /is-weakmap@2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: call-bind: 1.0.2 dev: true + /is-weakset@2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + dev: true + /is-whitespace-character@1.0.4: resolution: {integrity: sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==} dev: true @@ -8036,9 +7755,8 @@ packages: it-pushable: 3.2.1 dev: true - /it-parallel@3.0.3: - resolution: {integrity: sha512-Q5KmdvERHCOLDcgKqrzQ+yiMCdG6H9h7ZL3Zjx/Tx9xhZy8txSKoy+EiCgWZFs0rfYvxJhk6UkOpKLzJ1zM9ZA==} - engines: {node: '>=16.0.0', npm: '>=7.0.0'} + /it-parallel@3.0.4: + resolution: {integrity: sha512-fuA+SysGxbZc+Yl7EUvzQqZ8bNYQghZ0Mq9zA+fxMQ5eQYzatNg6oJk1y1PvPvNqLgKJMzEInpRO6PbLC3hGAg==} dependencies: p-defer: 4.0.0 dev: true @@ -8073,21 +7791,31 @@ packages: resolution: {integrity: sha512-pLULMZMAB/+vbdvbZtebC0nWBTbG581lk6w8P7DfIIIKUfa8FbY7Oi0FxZcFPbxvISs7A9E+cMpLDBc1XhpAOA==} dependencies: buffer: 6.0.3 - fast-fifo: 1.3.0 + fast-fifo: 1.3.2 get-iterator: 1.0.2 p-defer: 3.0.0 p-fifo: 1.0.0 readable-stream: 3.6.2 dev: false - /jest-util@29.6.2: - resolution: {integrity: sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==} + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.4 + set-function-name: 2.0.1 + dev: true + + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.6.1 - '@types/node': 18.17.4 + '@jest/types': 29.6.3 + '@types/node': 18.11.18 chalk: 4.1.2 - ci-info: 3.8.0 + ci-info: 3.9.0 graceful-fs: 4.2.11 picomatch: 2.3.1 dev: true @@ -8096,28 +7824,28 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.17.4 + '@types/node': 18.11.18 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest-worker@29.6.2: - resolution: {integrity: sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==} + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.17.4 - jest-util: 29.6.2 + '@types/node': 18.11.18 + jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jiti@1.19.1: - resolution: {integrity: sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==} + /jiti@1.20.0: + resolution: {integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==} hasBin: true dev: true - /joi@17.9.2: - resolution: {integrity: sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==} + /joi@17.11.0: + resolution: {integrity: sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==} dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 @@ -8173,6 +7901,10 @@ packages: resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} dev: true + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + /json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} dev: true @@ -8222,10 +7954,10 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} dependencies: - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 object.assign: 4.1.4 - object.values: 1.1.6 + object.values: 1.1.7 dev: true /junk@4.0.1: @@ -8243,6 +7975,12 @@ packages: json-buffer: 3.0.0 dev: true + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -8270,8 +8008,8 @@ packages: package-json: 6.5.0 dev: true - /launch-editor@2.6.0: - resolution: {integrity: sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==} + /launch-editor@2.6.1: + resolution: {integrity: sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==} dependencies: picocolors: 1.0.0 shell-quote: 1.8.1 @@ -8312,33 +8050,10 @@ packages: listr2: 5.0.8 micromatch: 4.0.5 normalize-path: 3.0.0 - object-inspect: 1.12.3 + object-inspect: 1.13.0 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.3.1 - transitivePeerDependencies: - - enquirer - - supports-color - dev: true - - /lint-staged@13.2.3: - resolution: {integrity: sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==} - engines: {node: ^14.13.1 || >=16.0.0} - hasBin: true - dependencies: - chalk: 5.2.0 - cli-truncate: 3.1.0 - commander: 10.0.1 - debug: 4.3.4(supports-color@8.1.1) - execa: 7.2.0 - lilconfig: 2.1.0 - listr2: 5.0.8 - micromatch: 4.0.5 - normalize-path: 3.0.0 - object-inspect: 1.12.3 - pidtree: 0.6.0 - string-argv: 0.3.2 - yaml: 2.3.1 + yaml: 2.3.3 transitivePeerDependencies: - enquirer - supports-color @@ -8428,10 +8143,22 @@ packages: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true + /lodash.escape@4.0.1: + resolution: {integrity: sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==} + dev: true + + /lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + dev: true + /lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} dev: true + /lodash.invokemap@4.6.0: + resolution: {integrity: sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w==} + dev: true + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -8440,10 +8167,18 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true + /lodash.pullall@4.2.0: + resolution: {integrity: sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==} + dev: true + /lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} dev: true + /lodash.uniqby@4.7.0: + resolution: {integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==} + dev: true + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -8484,7 +8219,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 dev: true /lowercase-keys@1.0.1: @@ -8512,8 +8247,8 @@ packages: /lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - /magic-string@0.30.2: - resolution: {integrity: sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==} + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -8564,8 +8299,8 @@ packages: /mdast-util-to-hast@10.0.1: resolution: {integrity: sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==} dependencies: - '@types/mdast': 3.0.12 - '@types/unist': 2.0.7 + '@types/mdast': 3.0.14 + '@types/unist': 2.0.8 mdast-util-definitions: 4.0.0 mdurl: 1.0.1 unist-builder: 2.0.3 @@ -8595,7 +8330,7 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} dependencies: - fs-monkey: 1.0.4 + fs-monkey: 1.0.5 dev: true /memorystream@0.3.1: @@ -8693,14 +8428,14 @@ packages: engines: {node: '>=4'} dev: true - /mini-css-extract-plugin@2.7.6(webpack@5.88.2): + /mini-css-extract-plugin@2.7.6(webpack@5.89.0): resolution: {integrity: sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: schema-utils: 4.2.0 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /minimalistic-assert@1.0.1: @@ -8782,7 +8517,7 @@ packages: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true dependencies: - dns-packet: 5.6.0 + dns-packet: 5.6.1 thunky: 1.1.0 dev: true @@ -8790,8 +8525,8 @@ packages: resolution: {integrity: sha512-b5mYMkOkARIuVZCpvijFj9a6m5wMVLC7cf/jIPd5D/ARDOfLC5+IFkbgDXQgcU2goIsTD/O9NY4DI/Mt4OGvlg==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} - /multiformats@12.1.1: - resolution: {integrity: sha512-GBSToTmri2vJYs8wqcZQ8kB21dCaeTOzHTIAlr8J06C1eL6UbzqURXFZ5Fl0EYm9GAFz1IlYY8SxGOs9G9NJRg==} + /multiformats@12.1.2: + resolution: {integrity: sha512-6mRIsrZXyw5xNPO31IGBMmxgDXBSgCGDsBAtazkZ02ip4hMwZNrQvfxXZtytRoBSWuzSq5f9VmMnXj76fIz5FQ==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} /multimatch@5.0.0: @@ -8824,18 +8559,18 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /nanoid@5.0.1: - resolution: {integrity: sha512-vWeVtV5Cw68aML/QaZvqN/3QQXc6fBfIieAlu05m7FZW2Dgb+3f0xc0TTxuJW+7u30t7iSDTV/j3kVI0oJqIfQ==} + /nanoid@5.0.2: + resolution: {integrity: sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==} engines: {node: ^18 || >=20} hasBin: true dev: true - /native-fetch@3.0.0(node-fetch@2.6.12): + /native-fetch@3.0.0(node-fetch@2.7.0): resolution: {integrity: sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==} peerDependencies: node-fetch: '*' dependencies: - node-fetch: 2.6.12 + node-fetch: 2.7.0 dev: false /natural-compare-lite@1.4.0: @@ -8877,7 +8612,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /node-emoji@1.11.0: @@ -8886,8 +8621,8 @@ packages: lodash: 4.17.21 dev: true - /node-fetch@2.6.12: - resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==} + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 @@ -8911,7 +8646,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.4 + resolve: 1.22.8 semver: 5.7.2 validate-npm-package-license: 3.0.4 dev: true @@ -8948,7 +8683,7 @@ packages: pidtree: 0.3.1 read-pkg: 3.0.0 shell-quote: 1.8.1 - string.prototype.padend: 3.1.4 + string.prototype.padend: 3.1.5 dev: true /npm-run-path@4.0.1: @@ -8976,8 +8711,8 @@ packages: engines: {node: '>=0.10.0'} dev: true - /object-inspect@1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + /object-inspect@1.13.0: + resolution: {integrity: sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==} dev: true /object-is@1.1.5: @@ -8985,7 +8720,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 + define-properties: 1.2.1 dev: true /object-keys@1.1.1: @@ -9003,47 +8738,47 @@ packages: object-keys: 1.1.1 dev: true - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true - /object.groupby@1.0.0: - resolution: {integrity: sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==} + /object.groupby@1.0.1: + resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 get-intrinsic: 1.2.1 dev: true - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} dependencies: - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true /obuf@1.1.2: @@ -9117,7 +8852,7 @@ packages: bl: 5.1.0 chalk: 5.3.0 cli-cursor: 4.0.0 - cli-spinners: 2.9.0 + cli-spinners: 2.9.1 is-interactive: 2.0.0 is-unicode-supported: 1.3.0 log-symbols: 5.1.0 @@ -9169,7 +8904,7 @@ packages: /p-fifo@1.0.0: resolution: {integrity: sha512-IjoCxXW48tqdtDFz6fqo5q1UfFVjjVZe8TC1QRflvNUJtNfCUhxOUw6MOVZhDPjqhSzc26xKdugsO17gmzd5+A==} dependencies: - fast-fifo: 1.3.0 + fast-fifo: 1.3.2 p-defer: 3.0.0 dev: false @@ -9232,11 +8967,11 @@ packages: engines: {node: '>=16'} dev: true - /p-queue@7.3.0: - resolution: {integrity: sha512-5fP+yVQ0qp0rEfZoDTlP2c3RYBgxvRsw30qO+VtPPc95lyvSG+x6USSh1TuLB4n96IO6I8/oXQGsTgtna4q2nQ==} + /p-queue@7.4.1: + resolution: {integrity: sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA==} engines: {node: '>=12'} dependencies: - eventemitter3: 4.0.7 + eventemitter3: 5.0.1 p-timeout: 5.1.0 dev: true @@ -9293,7 +9028,7 @@ packages: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /parent-module@1.0.1: @@ -9325,7 +9060,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.22.10 + '@babel/code-frame': 7.22.13 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -9343,7 +9078,7 @@ packages: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.6.1 + tslib: 2.6.2 dev: true /path-browserify@1.0.1: @@ -9488,7 +9223,7 @@ packages: lilconfig: 2.1.0 lodash: 4.17.21 merge-options: 3.0.4 - nanoid: 5.0.1 + nanoid: 5.0.2 ora: 7.0.1 p-timeout: 6.1.2 path-browserify: 1.0.1 @@ -9504,7 +9239,7 @@ packages: tempy: 3.1.0 test-exclude: 6.0.0 util: 0.12.5 - v8-to-istanbul: 9.1.0 + v8-to-istanbul: 9.1.3 dev: true /please-upgrade-node@3.2.0: @@ -9525,350 +9260,352 @@ packages: trouter: 2.0.1 dev: true - /postcss-calc@8.2.4(postcss@8.4.27): + /postcss-calc@8.2.4(postcss@8.4.31): resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} peerDependencies: postcss: ^8.2.2 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: true - /postcss-colormin@5.3.1(postcss@8.4.27): + /postcss-colormin@5.3.1(postcss@8.4.31): resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-convert-values@5.1.3(postcss@8.4.27): + /postcss-convert-values@5.1.3(postcss@8.4.31): resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 - postcss: 8.4.27 + browserslist: 4.22.1 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-discard-comments@5.1.2(postcss@8.4.27): + /postcss-discard-comments@5.1.2(postcss@8.4.31): resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss-discard-duplicates@5.1.0(postcss@8.4.27): + /postcss-discard-duplicates@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss-discard-empty@5.1.1(postcss@8.4.27): + /postcss-discard-empty@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss-discard-overridden@5.1.0(postcss@8.4.27): + /postcss-discard-overridden@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss-discard-unused@5.1.0(postcss@8.4.27): + /postcss-discard-unused@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true - /postcss-loader@7.3.3(postcss@8.4.27)(webpack@5.88.2): + /postcss-loader@7.3.3(postcss@8.4.31)(typescript@4.9.5)(webpack@5.89.0): resolution: {integrity: sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==} engines: {node: '>= 14.15.0'} peerDependencies: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 dependencies: - cosmiconfig: 8.2.0 - jiti: 1.19.1 - postcss: 8.4.27 + cosmiconfig: 8.3.6(typescript@4.9.5) + jiti: 1.20.0 + postcss: 8.4.31 semver: 7.5.4 - webpack: 5.88.2 + webpack: 5.89.0 + transitivePeerDependencies: + - typescript dev: true - /postcss-merge-idents@5.1.1(postcss@8.4.27): + /postcss-merge-idents@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-utils: 3.1.0(postcss@8.4.27) - postcss: 8.4.27 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-merge-longhand@5.1.7(postcss@8.4.27): + /postcss-merge-longhand@5.1.7(postcss@8.4.31): resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.4.27) + stylehacks: 5.1.1(postcss@8.4.31) dev: true - /postcss-merge-rules@5.1.4(postcss@8.4.27): + /postcss-merge-rules@5.1.4(postcss@8.4.31): resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.4.27) - postcss: 8.4.27 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true - /postcss-minify-font-values@5.1.0(postcss@8.4.27): + /postcss-minify-font-values@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-minify-gradients@5.1.1(postcss@8.4.27): + /postcss-minify-gradients@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.4.27) - postcss: 8.4.27 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-minify-params@5.1.4(postcss@8.4.27): + /postcss-minify-params@5.1.4(postcss@8.4.31): resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 - cssnano-utils: 3.1.0(postcss@8.4.27) - postcss: 8.4.27 + browserslist: 4.22.1 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-minify-selectors@5.2.1(postcss@8.4.27): + /postcss-minify-selectors@5.2.1(postcss@8.4.31): resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true - /postcss-modules-extract-imports@3.0.0(postcss@8.4.27): + /postcss-modules-extract-imports@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss-modules-local-by-default@4.0.3(postcss@8.4.27): + /postcss-modules-local-by-default@4.0.3(postcss@8.4.31): resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.27) - postcss: 8.4.27 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: true - /postcss-modules-scope@3.0.0(postcss@8.4.27): + /postcss-modules-scope@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true - /postcss-modules-values@4.0.0(postcss@8.4.27): + /postcss-modules-values@4.0.0(postcss@8.4.31): resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.27) - postcss: 8.4.27 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 dev: true - /postcss-normalize-charset@5.1.0(postcss@8.4.27): + /postcss-normalize-charset@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss-normalize-display-values@5.1.0(postcss@8.4.27): + /postcss-normalize-display-values@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-normalize-positions@5.1.1(postcss@8.4.27): + /postcss-normalize-positions@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-normalize-repeat-style@5.1.1(postcss@8.4.27): + /postcss-normalize-repeat-style@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-normalize-string@5.1.0(postcss@8.4.27): + /postcss-normalize-string@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-normalize-timing-functions@5.1.0(postcss@8.4.27): + /postcss-normalize-timing-functions@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-normalize-unicode@5.1.1(postcss@8.4.27): + /postcss-normalize-unicode@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 - postcss: 8.4.27 + browserslist: 4.22.1 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-normalize-url@5.1.0(postcss@8.4.27): + /postcss-normalize-url@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: normalize-url: 6.1.0 - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-normalize-whitespace@5.1.1(postcss@8.4.27): + /postcss-normalize-whitespace@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-ordered-values@5.1.3(postcss@8.4.27): + /postcss-ordered-values@5.1.3(postcss@8.4.31): resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-utils: 3.1.0(postcss@8.4.27) - postcss: 8.4.27 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-reduce-idents@5.2.0(postcss@8.4.27): + /postcss-reduce-idents@5.2.0(postcss@8.4.31): resolution: {integrity: sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true - /postcss-reduce-initial@5.1.2(postcss@8.4.27): + /postcss-reduce-initial@5.1.2(postcss@8.4.31): resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 caniuse-api: 3.0.0 - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss-reduce-transforms@5.1.0(postcss@8.4.27): + /postcss-reduce-transforms@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true @@ -9880,34 +9617,34 @@ packages: util-deprecate: 1.0.2 dev: true - /postcss-sort-media-queries@4.4.1(postcss@8.4.27): + /postcss-sort-media-queries@4.4.1(postcss@8.4.31): resolution: {integrity: sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==} engines: {node: '>=10.0.0'} peerDependencies: postcss: ^8.4.16 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 sort-css-media-queries: 2.1.0 dev: true - /postcss-svgo@5.1.0(postcss@8.4.27): + /postcss-svgo@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 svgo: 2.8.0 dev: true - /postcss-unique-selectors@5.1.1(postcss@8.4.27): + /postcss-unique-selectors@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true @@ -9915,17 +9652,17 @@ packages: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true - /postcss-zindex@5.1.0(postcss@8.4.27): + /postcss-zindex@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 dev: true - /postcss@8.4.27: - resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==} + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -10003,8 +9740,8 @@ packages: xtend: 4.0.2 dev: true - /protobufjs@7.2.4: - resolution: {integrity: sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==} + /protobufjs@7.2.5: + resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==} engines: {node: '>=12.0.0'} requiresBuild: true dependencies: @@ -10018,7 +9755,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 18.17.4 + '@types/node': 18.11.18 long: 5.2.3 /proxy-addr@2.0.7: @@ -10115,7 +9852,7 @@ packages: strip-json-comments: 2.0.1 dev: true - /react-dev-utils@12.0.1(eslint@8.49.0)(typescript@4.9.5)(webpack@5.88.2): + /react-dev-utils@12.0.1(eslint@8.51.0)(typescript@4.9.5)(webpack@5.89.0): resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} engines: {node: '>=14'} peerDependencies: @@ -10125,16 +9862,16 @@ packages: typescript: optional: true dependencies: - '@babel/code-frame': 7.22.10 + '@babel/code-frame': 7.22.13 address: 1.2.2 - browserslist: 4.21.10 + browserslist: 4.22.1 chalk: 4.1.2 cross-spawn: 7.0.3 detect-port-alt: 1.1.6 escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.49.0)(typescript@4.9.5)(webpack@5.88.2) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.51.0)(typescript@4.9.5)(webpack@5.89.0) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -10150,7 +9887,7 @@ packages: strip-ansi: 6.0.1 text-table: 0.2.0 typescript: 4.9.5 - webpack: 5.88.2 + webpack: 5.89.0 transitivePeerDependencies: - eslint - supports-color @@ -10176,7 +9913,7 @@ packages: react-dom: optional: true dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.2 invariant: 2.2.4 prop-types: 15.8.1 react-fast-compare: 3.2.2 @@ -10187,7 +9924,7 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true - /react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.88.2): + /react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.89.0): resolution: {integrity: sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==} engines: {node: '>=10.13.0'} peerDependencies: @@ -10197,9 +9934,9 @@ packages: react-loadable: optional: true dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.2 react-loadable: /@docusaurus/react-loadable@5.5.2 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /react-native-fetch-api@3.0.0: @@ -10219,7 +9956,7 @@ packages: react-router: optional: true dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.2 react-router: 5.3.4 dev: true @@ -10231,7 +9968,7 @@ packages: react: optional: true dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.2 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -10248,7 +9985,7 @@ packages: react: optional: true dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.2 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -10281,7 +10018,7 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} dependencies: - '@types/normalize-package-data': 2.4.1 + '@types/normalize-package-data': 2.4.3 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 @@ -10317,7 +10054,7 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} dependencies: - resolve: 1.22.4 + resolve: 1.22.8 dev: true /recursive-readdir@2.2.3: @@ -10327,8 +10064,20 @@ packages: minimatch: 3.1.2 dev: true - /regenerate-unicode-properties@10.1.0: - resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} + /reflect.getprototypeof@1.0.4: + resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.1 + es-abstract: 1.22.2 + get-intrinsic: 1.2.1 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 + dev: true + + /regenerate-unicode-properties@10.1.1: + resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} engines: {node: '>=4'} dependencies: regenerate: 1.4.2 @@ -10345,7 +10094,7 @@ packages: /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.2 dev: true /regexp-tree@0.1.27: @@ -10353,13 +10102,13 @@ packages: hasBin: true dev: true - /regexp.prototype.flags@1.5.0: - resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + /regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - functions-have-names: 1.2.3 + define-properties: 1.2.1 + set-function-name: 2.0.1 dev: true /regexpp@3.2.0: @@ -10373,7 +10122,7 @@ packages: dependencies: '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 - regenerate-unicode-properties: 10.1.0 + regenerate-unicode-properties: 10.1.1 regjsparser: 0.9.1 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.1.0 @@ -10507,16 +10256,16 @@ packages: resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} dev: true - /resolve@1.22.4: - resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true dependencies: is-core-module: 2.13.0 @@ -10583,7 +10332,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 /sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} @@ -10592,8 +10341,8 @@ packages: mri: 1.2.0 dev: true - /safe-array-concat@1.0.0: - resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} engines: {node: '>=0.4'} dependencies: call-bind: 1.0.2 @@ -10626,13 +10375,13 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sass@1.65.1: - resolution: {integrity: sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==} + /sass@1.69.4: + resolution: {integrity: sha512-+qEreVhqAy8o++aQfCJwp0sklr2xyEzkm9Pp/Igu9wNPoe7EZEQ8X/MBvvXggI2ql607cxKg/RKOwDj6pp2XDA==} engines: {node: '>=14.0.0'} hasBin: true dependencies: chokidar: 3.5.3 - immutable: 4.3.2 + immutable: 4.3.4 source-map-js: 1.0.2 dev: false @@ -10640,7 +10389,7 @@ packages: resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} engines: {node: '>= 8.9.0'} dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.14 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) dev: true @@ -10649,7 +10398,7 @@ packages: resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} engines: {node: '>= 8.9.0'} dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.14 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) dev: true @@ -10658,7 +10407,7 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.14 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) dev: true @@ -10667,7 +10416,7 @@ packages: resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} engines: {node: '>= 12.13.0'} dependencies: - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.14 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) ajv-keywords: 5.1.0(ajv@8.12.0) @@ -10801,6 +10550,15 @@ packages: - supports-color dev: true + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.0 + dev: true + /setprototypeof@1.1.0: resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} dev: true @@ -10858,8 +10616,8 @@ packages: rechoir: 0.6.2 dev: true - /shiki@0.14.3: - resolution: {integrity: sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==} + /shiki@0.14.5: + resolution: {integrity: sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==} dependencies: ansi-sequence-parser: 1.1.1 jsonc-parser: 3.2.0 @@ -10871,7 +10629,7 @@ packages: dependencies: call-bind: 1.0.2 get-intrinsic: 1.2.1 - object-inspect: 1.12.3 + object-inspect: 1.13.0 dev: true /signal-exit@3.0.7: @@ -10899,15 +10657,6 @@ packages: supports-color: 7.2.0 dev: true - /sirv@1.0.19: - resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==} - engines: {node: '>= 10'} - dependencies: - '@polka/url': 1.0.0-next.23 - mrmime: 1.0.1 - totalist: 1.1.0 - dev: true - /sirv@2.0.3: resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} engines: {node: '>= 10'} @@ -11003,7 +10752,7 @@ packages: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.13 + spdx-license-ids: 3.0.16 dev: true /spdx-exceptions@2.3.0: @@ -11014,11 +10763,11 @@ packages: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.13 + spdx-license-ids: 3.0.16 dev: true - /spdx-license-ids@3.0.13: - resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} + /spdx-license-ids@3.0.16: + resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} dev: true /spdy-transport@3.0.0: @@ -11070,13 +10819,13 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - eslint: 8.46.0 - eslint-config-standard: 17.0.0(eslint-plugin-import@2.28.0)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.46.0) - eslint-config-standard-jsx: 11.0.0(eslint-plugin-react@7.33.1)(eslint@8.46.0) - eslint-plugin-import: 2.28.0(@typescript-eslint/parser@5.62.0)(eslint@8.49.0) - eslint-plugin-n: 15.7.0(eslint@8.49.0) - eslint-plugin-promise: 6.1.1(eslint@8.49.0) - eslint-plugin-react: 7.33.1(eslint@8.49.0) + eslint: 8.51.0 + eslint-config-standard: 17.0.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0) + eslint-config-standard-jsx: 11.0.0(eslint-plugin-react@7.33.2)(eslint@8.51.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.51.0) + eslint-plugin-n: 15.7.0(eslint@8.51.0) + eslint-plugin-promise: 6.1.1(eslint@8.51.0) + eslint-plugin-react: 7.33.2(eslint@8.51.0) standard-engine: 15.1.0 transitivePeerDependencies: - '@typescript-eslint/parser' @@ -11099,8 +10848,8 @@ packages: engines: {node: '>= 0.8'} dev: true - /std-env@3.3.3: - resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} + /std-env@3.4.3: + resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} dev: true /stdin-discarder@0.1.0: @@ -11149,55 +10898,56 @@ packages: engines: {node: '>=16'} dependencies: eastasianwidth: 0.2.0 - emoji-regex: 10.2.1 + emoji-regex: 10.3.0 strip-ansi: 7.1.0 dev: true - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 get-intrinsic: 1.2.1 has-symbols: 1.0.3 internal-slot: 1.0.5 - regexp.prototype.flags: 1.5.0 + regexp.prototype.flags: 1.5.1 + set-function-name: 2.0.1 side-channel: 1.0.4 dev: true - /string.prototype.padend@3.1.4: - resolution: {integrity: sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==} + /string.prototype.padend@3.1.5: + resolution: {integrity: sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true - /string.prototype.trim@1.2.7: - resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + define-properties: 1.2.1 + es-abstract: 1.22.2 dev: true /string_decoder@1.1.1: @@ -11275,14 +11025,14 @@ packages: inline-style-parser: 0.1.1 dev: true - /stylehacks@5.1.1(postcss@8.4.27): + /stylehacks@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.21.10 - postcss: 8.4.27 + browserslist: 4.22.1 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true @@ -11330,7 +11080,7 @@ packages: /sync-multihash-sha2@1.0.0: resolution: {integrity: sha512-A5gVpmtKF0ov+/XID0M0QRJqF2QxAsj3x/LlDC8yivzgoYCoWkV+XaZPfVu7Vj1T/hYzYS1tfjwboSbXjqocug==} dependencies: - '@noble/hashes': 1.3.1 + '@noble/hashes': 1.3.2 /tapable@1.1.3: resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} @@ -11357,7 +11107,7 @@ packages: unique-string: 3.0.0 dev: true - /terser-webpack-plugin@5.3.9(webpack@5.88.2): + /terser-webpack-plugin@5.3.9(webpack@5.89.0): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -11373,16 +11123,16 @@ packages: uglify-js: optional: true dependencies: - '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/trace-mapping': 0.3.20 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.1 - terser: 5.19.2 - webpack: 5.88.2 + terser: 5.22.0 + webpack: 5.89.0 dev: true - /terser@5.19.2: - resolution: {integrity: sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==} + /terser@5.22.0: + resolution: {integrity: sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==} engines: {node: '>=10'} hasBin: true dependencies: @@ -11447,11 +11197,6 @@ packages: engines: {node: '>=0.6'} dev: true - /totalist@1.1.0: - resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==} - engines: {node: '>=6'} - dev: true - /totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -11494,8 +11239,8 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib@2.6.1: - resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==} + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} /tsutils-etc@1.4.2(tsutils@3.21.0)(typescript@4.9.5): resolution: {integrity: sha512-2Dn5SxTDOu6YWDNKcx1xu2YUy6PUeKrWZB/x2cQ8vY2+iz3JRembKn/iZ0JLT1ZudGNwQQvtFX9AwvRHbXuPUg==} @@ -11504,7 +11249,7 @@ packages: tsutils: ^3.0.0 typescript: '>=4.0.0' dependencies: - '@types/yargs': 17.0.24 + '@types/yargs': 17.0.28 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 yargs: 17.7.2 @@ -11649,7 +11394,7 @@ packages: lunr: 2.3.9 marked: 4.3.0 minimatch: 7.4.6 - shiki: 0.14.3 + shiki: 0.14.5 typescript: 4.9.5 /typescript@4.9.5: @@ -11670,13 +11415,6 @@ packages: engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: multiformats: 11.0.2 - dev: false - - /uint8arrays@4.0.6: - resolution: {integrity: sha512-4ZesjQhqOU2Ip6GPReIwN60wRxIupavL8T0Iy36BBHr2qyMrNxsPJvr7vpS4eFt8F8kSguWUPad6ZM9izs/vyw==} - dependencies: - multiformats: 12.1.1 - dev: true /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -11720,7 +11458,7 @@ packages: /unified@9.2.0: resolution: {integrity: sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 bail: 1.0.5 extend: 3.0.2 is-buffer: 2.0.5 @@ -11732,7 +11470,7 @@ packages: /unified@9.2.2: resolution: {integrity: sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 bail: 1.0.5 extend: 3.0.2 is-buffer: 2.0.5 @@ -11786,20 +11524,20 @@ packages: /unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 dev: true /unist-util-visit-parents@3.1.1: resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 unist-util-is: 4.1.0 dev: true /unist-util-visit@2.0.3: resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 unist-util-is: 4.1.0 unist-util-visit-parents: 3.1.1 dev: true @@ -11814,13 +11552,13 @@ packages: engines: {node: '>= 0.8'} dev: true - /update-browserslist-db@1.0.11(browserslist@4.21.10): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 escalade: 3.1.1 picocolors: 1.0.0 dev: true @@ -11850,7 +11588,7 @@ packages: dependencies: punycode: 2.3.0 - /url-loader@4.1.1(file-loader@6.2.0)(webpack@5.88.2): + /url-loader@4.1.1(file-loader@6.2.0)(webpack@5.89.0): resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -11860,11 +11598,11 @@ packages: file-loader: optional: true dependencies: - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.88.2 + webpack: 5.89.0 dev: true /url-parse-lax@3.0.0: @@ -11901,13 +11639,13 @@ packages: hasBin: true dev: true - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + /v8-to-istanbul@9.1.3: + resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} engines: {node: '>=10.12.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.19 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 + '@jridgewell/trace-mapping': 0.3.20 + '@types/istanbul-lib-coverage': 2.0.5 + convert-source-map: 2.0.0 dev: true /validate-npm-package-license@3.0.4: @@ -11936,14 +11674,14 @@ packages: /vfile-message@2.0.4: resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 unist-util-stringify-position: 2.0.3 dev: true /vfile@4.2.1: resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} dependencies: - '@types/unist': 2.0.7 + '@types/unist': 2.0.8 is-buffer: 2.0.5 unist-util-stringify-position: 2.0.3 vfile-message: 2.0.4 @@ -11961,7 +11699,7 @@ packages: hasBin: true dependencies: axios: 0.25.0 - joi: 17.9.2 + joi: 17.11.0 lodash: 4.17.21 minimist: 1.2.8 rxjs: 7.8.1 @@ -12018,27 +11756,34 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false - /webpack-bundle-analyzer@4.9.0: - resolution: {integrity: sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw==} + /webpack-bundle-analyzer@4.9.1: + resolution: {integrity: sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w==} engines: {node: '>= 10.13.0'} hasBin: true dependencies: '@discoveryjs/json-ext': 0.5.7 acorn: 8.10.0 acorn-walk: 8.2.0 - chalk: 4.1.2 commander: 7.2.0 + escape-string-regexp: 4.0.0 gzip-size: 6.0.0 - lodash: 4.17.21 + is-plain-object: 5.0.0 + lodash.debounce: 4.0.8 + lodash.escape: 4.0.1 + lodash.flatten: 4.4.0 + lodash.invokemap: 4.6.0 + lodash.pullall: 4.2.0 + lodash.uniqby: 4.7.0 opener: 1.5.2 - sirv: 1.0.19 + picocolors: 1.0.0 + sirv: 2.0.3 ws: 7.5.9 transitivePeerDependencies: - bufferutil - utf-8-validate dev: true - /webpack-dev-middleware@5.3.3(webpack@5.88.2): + /webpack-dev-middleware@5.3.3(webpack@5.89.0): resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -12049,10 +11794,10 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.88.2 + webpack: 5.89.0 dev: true - /webpack-dev-server@4.15.1(webpack@5.88.2): + /webpack-dev-server@4.15.1(webpack@5.89.0): resolution: {integrity: sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==} engines: {node: '>= 12.13.0'} hasBin: true @@ -12065,13 +11810,13 @@ packages: webpack-cli: optional: true dependencies: - '@types/bonjour': 3.5.10 - '@types/connect-history-api-fallback': 1.5.0 - '@types/express': 4.17.17 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.2 - '@types/sockjs': 0.3.33 - '@types/ws': 8.5.5 + '@types/bonjour': 3.5.12 + '@types/connect-history-api-fallback': 1.5.2 + '@types/express': 4.17.20 + '@types/serve-index': 1.9.2 + '@types/serve-static': 1.15.3 + '@types/sockjs': 0.3.34 + '@types/ws': 8.5.7 ansi-html-community: 0.0.8 bonjour-service: 1.1.1 chokidar: 3.5.3 @@ -12082,9 +11827,9 @@ packages: express: 4.18.2 graceful-fs: 4.2.11 html-entities: 2.4.0 - http-proxy-middleware: 2.0.6(@types/express@4.17.17) + http-proxy-middleware: 2.0.6(@types/express@4.17.20) ipaddr.js: 2.1.0 - launch-editor: 2.6.0 + launch-editor: 2.6.1 open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 @@ -12093,9 +11838,9 @@ packages: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.88.2 - webpack-dev-middleware: 5.3.3(webpack@5.88.2) - ws: 8.13.0 + webpack: 5.89.0 + webpack-dev-middleware: 5.3.3(webpack@5.89.0) + ws: 8.14.2 transitivePeerDependencies: - bufferutil - debug @@ -12103,11 +11848,12 @@ packages: - utf-8-validate dev: true - /webpack-merge@5.9.0: - resolution: {integrity: sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==} + /webpack-merge@5.10.0: + resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} engines: {node: '>=10.0.0'} dependencies: clone-deep: 4.0.1 + flat: 5.0.2 wildcard: 2.0.1 dev: true @@ -12116,8 +11862,8 @@ packages: engines: {node: '>=10.13.0'} dev: true - /webpack@5.88.2: - resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==} + /webpack@5.89.0: + resolution: {integrity: sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -12126,17 +11872,17 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 1.0.1 + '@types/eslint-scope': 3.7.6 + '@types/estree': 1.0.3 '@webassemblyjs/ast': 1.11.6 '@webassemblyjs/wasm-edit': 1.11.6 '@webassemblyjs/wasm-parser': 1.11.6 acorn: 8.10.0 acorn-import-assertions: 1.9.0(acorn@8.10.0) - browserslist: 4.21.10 + browserslist: 4.22.1 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 - es-module-lexer: 1.3.0 + es-module-lexer: 1.3.1 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -12147,7 +11893,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(webpack@5.88.2) + terser-webpack-plugin: 5.3.9(webpack@5.89.0) watchpack: 2.4.0 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -12156,7 +11902,7 @@ packages: - uglify-js dev: true - /webpackbar@5.0.2(webpack@5.88.2): + /webpackbar@5.0.2(webpack@5.89.0): resolution: {integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==} engines: {node: '>=12'} peerDependencies: @@ -12165,8 +11911,8 @@ packages: chalk: 4.1.2 consola: 2.15.3 pretty-time: 1.1.0 - std-env: 3.3.3 - webpack: 5.88.2 + std-env: 3.4.3 + webpack: 5.89.0 dev: true /websocket-driver@0.7.4: @@ -12200,6 +11946,33 @@ packages: is-symbol: 1.0.4 dev: true + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.0 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.11 + dev: true + + /which-collection@1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + /which-typed-array@1.1.11: resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} engines: {node: '>= 0.4'} @@ -12316,8 +12089,8 @@ packages: optional: true dev: false - /ws@8.13.0: - resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + /ws@8.14.2: + resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -12354,8 +12127,8 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - /yaml@2.3.1: - resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + /yaml@2.3.3: + resolution: {integrity: sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==} engines: {node: '>= 14'} dev: true