diff --git a/packages/nest/.mocharc.json b/packages/nest/.mocharc.json new file mode 100644 index 0000000..e8ecf2c --- /dev/null +++ b/packages/nest/.mocharc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://json.schemastore.org/mocharc.json", + "require": "tsx" +} diff --git a/packages/nest/package.json b/packages/nest/package.json index 3987da2..b2aea2a 100644 --- a/packages/nest/package.json +++ b/packages/nest/package.json @@ -29,27 +29,23 @@ }, "typesVersions": { "*": { - "module1": [ - "dist/src/crypto" - ] + "module1": ["dist/src/crypto"] } }, - "files": [ - "src", - "dist/src/*.d.ts", - "dist/src/*.d.ts.map" - ], + "files": ["src", "dist/src/*.d.ts", "dist/src/*.d.ts.map"], "scripts": { "lint": "tsc --build && eslint . && prettier --check '**/*.{js,ts,yml,json}' --ignore-path ../../.gitignore", "build": "tsc --build", "test": "pnpm run test:node && pnpm run test:browser", - "test:node": "mocha 'test/**/!(*.browser).test.js'", - "test:browser": "playwright-test 'test/**/!(*.node).test.js'" + "test:node": "mocha 'test/**/!(*.browser).test.ts'", + "test:browser": "playwright-test 'test/**/!(*.node).test.ts'" }, "dependencies": { "@ipld/dag-cbor": "^9.0.6", "@ipld/dag-pb": "^4.0.6", "compare-versions": "^6.1.0", + "debounce-promise": "^3.1.2", + "emittery": "^1.0.1", "interface-blockstore": "^5.2.7", "ipfs-unixfs": "^11.1.0", "ipfs-unixfs-exporter": "^13.2.2", @@ -65,22 +61,21 @@ "@types/mocha": "^10.0.6", "@types/node": "^20.10.4", "assert": "^2.1.0", + "fast-check": "^3.14.0", "mocha": "^10.2.0", - "playwright-test": "^14.0.0" + "playwright-test": "^14.0.0", + "tsx": "^4.6.2" }, "publishConfig": { "provenance": true }, "eslintConfig": { - "extends": [ - "@fission-codes" - ], + "extends": ["@fission-codes"], + "reportUnusedDisableDirectives": false, "env": { "mocha": true }, - "ignorePatterns": [ - "dist" - ], + "ignorePatterns": ["dist"], "rules": { "@typescript-eslint/no-unused-vars": [ "error", @@ -90,18 +85,11 @@ "varsIgnorePattern": "^_" } ], - "unicorn/no-array-reduce": [ - "off" - ] + "unicorn/no-array-reduce": ["off"] } }, "depcheck": { - "specials": [ - "bin" - ], - "ignores": [ - "@types/*", - "assert" - ] + "specials": ["bin"], + "ignores": ["@types/*", "assert"] } } diff --git a/packages/nest/src/app-info.ts b/packages/nest/src/app-info.ts new file mode 100644 index 0000000..5bfe74e --- /dev/null +++ b/packages/nest/src/app-info.ts @@ -0,0 +1,9 @@ +/** + * Information about your app. + * + * @group Configuration + */ +export interface AppInfo { + name: string + creator: string +} diff --git a/packages/nest/src/class.ts b/packages/nest/src/class.ts index 6246016..7d3daa4 100644 --- a/packages/nest/src/class.ts +++ b/packages/nest/src/class.ts @@ -1,44 +1,17 @@ -import debounce from 'debounce-promise' +import type { BlockStore, PublicDirectory, PublicFile } from 'wnfs' + import { CID } from 'multiformats/cid' -import { - AccessKey, - BlockStore, - PrivateDirectory, - PrivateFile, - PrivateNode, - PublicDirectory, - PublicFile, -} from 'wnfs' - -import type { Repo as CIDLog } from '../repositories/cid-log.js' - -import * as Events from '../events/fileSystem.js' -import * as Path from '../path/index.js' -import * as Rng from './rng.js' -import * as RootTree from './root-tree.js' -import * as Store from './store.js' +import { AccessKey, PrivateDirectory, PrivateFile, PrivateNode } from 'wnfs' -import { EventEmitter, createEmitter } from '../events/emitter.js' -import { EventListener } from '../events/listen.js' -import { Inventory } from '../inventory.js' -import { - Partition, - Partitioned, - PartitionedNonEmpty, - Private, - Public, -} from '../path/index.js' -import { Ticket } from '../ticket/types.js' -import { searchLatest } from './common.js' -import { findPrivateNode, partition as determinePartition } from './mounts.js' -import { TransactionContext } from './transaction.js' -import { +import debounce from 'debounce-promise' +import Emittery from 'emittery' + +import type { AnySupportedDataType, DataForType, DataRootChange, DataRootUpdater, DataType, - Dependencies, DirectoryItem, DirectoryItemWithKind, MutationOptions, @@ -48,12 +21,31 @@ import { PublishingStatus, TransactionResult, } from './types.js' -import { MountedPrivateNode, MountedPrivateNodes } from './types/internal.js' + +import type { + MountedPrivateNode, + MountedPrivateNodes, +} from './types/internal.js' + +import * as Path from './path.js' +import * as Rng from './rng.js' +import * as RootTree from './root-tree.js' + +import type { + Partition, + Partitioned, + PartitionedNonEmpty, + Private, + Public, +} from './path.js' + +import { searchLatest } from './common.js' +import { partition as determinePartition, findPrivateNode } from './mounts.js' +import { TransactionContext } from './transaction.js' /** @internal */ -export type FileSystemOptions = { +export interface FileSystemOptions { cidLog: CIDLog - dependencies: Dependencies did: string inventory: Inventory settleTimeBeforePublish?: number @@ -61,17 +53,26 @@ export type FileSystemOptions = { } /** @group File System */ +@Emittery.mixin('eventEmitter', [ + 'on', + 'off', + 'once', + 'events', + 'onAny', + 'offAny', + 'anyEvent', + 'clearListeners', + 'listenerCount', +]) export class FileSystem { - #blockStore: BlockStore - #eventEmitter: EventEmitter - #inventory: Inventory - #rootTree: RootTree.RootTree - #settleTimeBeforePublish: number + readonly #blockstore: Blockstore + readonly #rng: Rng.Rng + readonly #settleTimeBeforePublish: number #privateNodes: MountedPrivateNodes = {} - #rng: Rng.Rng + #rootTree: RootTree.RootTree - did: string + readonly did: string /** @hidden */ constructor( @@ -82,12 +83,9 @@ export class FileSystem { settleTimeBeforePublish: number ) { this.#blockStore = blockStore - this.#inventory = inventory - this.#settleTimeBeforePublish = settleTimeBeforePublish - this.#rootTree = rootTree - - this.#eventEmitter = createEmitter() this.#rng = Rng.makeRngInterface() + this.#rootTree = rootTree + this.#settleTimeBeforePublish = settleTimeBeforePublish this.did = did } @@ -97,6 +95,7 @@ export class FileSystem { /** * Creates a file system with an empty public tree & an empty private tree at the root. + * * @internal */ static async empty(opts: FileSystemOptions): Promise { @@ -108,12 +107,13 @@ export class FileSystem { blockStore, did, rootTree, - settleTimeBeforePublish || 2500 + settleTimeBeforePublish ?? 2500 ) } /** * Loads an existing file system from a CID. + * * @internal */ static async fromCID( @@ -131,78 +131,16 @@ export class FileSystem { blockStore, did, rootTree, - settleTimeBeforePublish || 2500 + settleTimeBeforePublish ?? 2500 ) } - // EVENTS - // ------ - - /** - * {@inheritDoc events.EmitterClass.on} - * @group Events - */ - on = ( - eventName: Name, - listener: EventListener - ) => this.#eventEmitter.on(eventName, listener) - - /** - * {@inheritDoc events.EmitterClass.onAny} - * @group Events - */ - onAny = ( - listener: ( - eventName: keyof Events.FileSystem, - eventData: Events.FileSystem[keyof Events.FileSystem] - ) => void | Promise - ) => this.#eventEmitter.onAny(listener) - - /** - * {@inheritDoc events.EmitterClass.off} - * @group Events - */ - off = ( - eventName: Name, - listener: EventListener - ) => this.#eventEmitter.off(eventName, listener) - - /** - * {@inheritDoc events.EmitterClass.offAny} - * @group Events - */ - offAny = ( - listener: ( - eventName: keyof Events.FileSystem, - eventData: Events.FileSystem[keyof Events.FileSystem] - ) => void | Promise - ) => this.#eventEmitter.offAny(listener) - - /** - * {@inheritDoc events.EmitterClass.once} - * @group Events - */ - once = (eventName: Name) => - this.#eventEmitter.once(eventName) - - /** - * {@inheritDoc events.EmitterClass.anyEvent} - * @group Events - */ - anyEvent = () => this.#eventEmitter.anyEvent() - - /** - * {@inheritDoc events.EmitterClass.events} - * @group Events - */ - events = (eventName: Name) => - this.#eventEmitter.events(eventName) - // MOUNTS // ------ /** * Mount a private node onto the file system. + * * @group Mounting */ async mountPrivateNode(node: { @@ -218,25 +156,26 @@ export class FileSystem { /** * Mount private nodes onto the file system. + * * @group Mounting */ async mountPrivateNodes( - nodes: { + nodes: Array<{ path: Path.Distinctive capsuleKey?: Uint8Array - }[] + }> ): Promise< - { + Array<{ path: Path.Distinctive capsuleKey: Uint8Array - }[] + }> > { const newNodes = await Promise.all( nodes.map( async ({ path, capsuleKey }): Promise<[string, MountedPrivateNode]> => { let privateNode: PrivateNode - if (capsuleKey) { + if (capsuleKey === null || capsuleKey === undefined) { const accessKey = AccessKey.fromBytes(capsuleKey) privateNode = await PrivateNode.load( accessKey, @@ -271,7 +210,7 @@ export class FileSystem { ...Object.fromEntries(newNodes), } - return Promise.all( + return await Promise.all( newNodes.map(async ([_, n]: [string, MountedPrivateNode]) => { const storeResult = await n.node.store( this.#rootTree.privateForest, @@ -292,9 +231,11 @@ export class FileSystem { /** * Unmount a private node from the file system. + * * @group Mounting */ unmountPrivateNode(path: Path.Distinctive): void { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete this.#privateNodes[Path.toPosix(path)] } @@ -303,28 +244,28 @@ export class FileSystem { /** @group Querying */ async contentCID(path: Path.File>): Promise { - return this.#transactionContext().contentCID(path) + return await this.#transactionContext().contentCID(path) } /** @group Querying */ async capsuleCID( path: Path.Distinctive> ): Promise { - return this.#transactionContext().capsuleCID(path) + return await this.#transactionContext().capsuleCID(path) } /** @group Querying */ async capsuleKey( path: Path.Distinctive> ): Promise { - return this.#transactionContext().capsuleKey(path) + return await this.#transactionContext().capsuleKey(path) } /** @group Querying */ async exists( path: Path.Distinctive> ): Promise { - return this.#transactionContext().exists(path) + return await this.#transactionContext().exists(path) } /** @group Querying */ @@ -341,9 +282,9 @@ export class FileSystem { ): Promise async listDirectory( path: Path.Directory>, - listOptions: { withItemKind: boolean } = { withItemKind: false } + listOptions?: { withItemKind?: boolean } ): Promise { - return this.#transactionContext().listDirectory(path, listOptions) + return await this.#transactionContext().listDirectory(path, listOptions) } /** @group Querying */ @@ -372,22 +313,11 @@ export class FileSystem { dataType: DataType, options?: { offset: number; length: number } ): Promise> { - return this.#transactionContext().read(path, dataType, options) - } - - /** - * Create a permalink to some public file system content. - * @group Querying - */ - permalink( - dataRoot: CID, - path: Path.Distinctive> - ): string { - if (this.#dependencies.depot.permalink) { - return this.#dependencies.depot.permalink(dataRoot, path) - } else { - throw new Error('Not implemented in the used depot component') - } + return await this.#transactionContext().read( + path, + dataType, + options + ) } // MUTATIONS @@ -406,7 +336,7 @@ export class FileSystem { | Path.Directory>, mutationOptions: MutationOptions = {} ): Promise | null> { - return this.#infusedTransaction( + return await this.#infusedTransaction( (t) => t.copy(from, to), to, mutationOptions @@ -493,7 +423,7 @@ export class FileSystem { path: Path.Directory>, mutationOptions: MutationOptions = {} ): Promise> { - return this.#infusedTransaction( + return await this.#infusedTransaction( (t) => t.ensureDirectory(path), path, mutationOptions @@ -516,7 +446,7 @@ export class FileSystem { | Path.Directory>, mutationOptions: MutationOptions = {} ): Promise> { - return this.#infusedTransaction( + return await this.#infusedTransaction( (t) => t.move(from, to), to, mutationOptions @@ -531,10 +461,9 @@ export class FileSystem { path: Path.Distinctive>, mutationOptions: MutationOptions = {} ): Promise { - const transactionResult = await this.transaction( - (t) => t.remove(path), - mutationOptions - ) + const transactionResult = await this.transaction(async (t) => { + await t.remove(path) + }, mutationOptions) return { dataRoot: transactionResult.dataRoot, @@ -556,7 +485,7 @@ export class FileSystem { newName: string, mutationOptions: MutationOptions = {} ): Promise> { - return this.#infusedTransaction( + return await this.#infusedTransaction( (t) => t.rename(path, newName), Path.replaceTerminus(path, newName), mutationOptions @@ -576,7 +505,7 @@ export class FileSystem { data: DataForType, mutationOptions: MutationOptions = {} ): Promise> { - return this.#infusedTransaction( + return await this.#infusedTransaction( (t) => t.write(path, dataType, data), path, mutationOptions @@ -597,7 +526,7 @@ export class FileSystem { await handler(context) // Commit transaction - const { changes, privateNodes, proofs, rootTree } = + const { changes, privateNodes, rootTree } = await TransactionContext.commit(context) this.#privateNodes = privateNodes @@ -607,18 +536,18 @@ export class FileSystem { const dataRoot = await this.calculateDataRoot() // Emit events - changes.forEach(async (change) => { + for (const change of changes) { await this.#eventEmitter.emit('local-change', { dataRoot, ...change, }) - }) + } // Publish const signal = mutationOptions.skipPublish === true ? Promise.resolve(FileSystem.#statusNotPublishing) - : this.#publish(dataRoot, proofs) + : this.#publish(dataRoot) // Fin return { @@ -631,8 +560,8 @@ export class FileSystem { // 🛠️ /** @group Misc */ - calculateDataRoot(): Promise { - return RootTree.store({ + async calculateDataRoot(): Promise { + return rootTree.store({ blockStore: this.#blockStore, depot: this.#dependencies.depot, rootTree: this.#rootTree, @@ -673,19 +602,19 @@ export class FileSystem { partition.segments, this.#blockStore ) - if (!node) + if (node === null || node === undefined) throw new Error('Failed to find needed public node for infusion') - const fileOrDir: PublicFile | PublicDirectory = node.isFile() - ? node.asFile() - : node.asDir() + const fileOrDir: PublicFile | PublicDirectory = + node.isFile() === true ? node.asFile() : node.asDir() const capsuleCID = await fileOrDir .store(this.#blockStore) .then((a) => CID.decode(a)) - const contentCID = node.isFile() - ? CID.decode(node.asFile().contentCid()) - : capsuleCID + const contentCID = + node.isFile() === true + ? CID.decode(node.asFile().contentCid()) + : capsuleCID return { dataRoot: transactionResult.dataRoot, @@ -713,7 +642,7 @@ export class FileSystem { ) ) .then((node) => { - if (!node) + if (node === null || node === undefined) throw new Error( 'Failed to find needed private node for infusion' ) @@ -744,41 +673,13 @@ export class FileSystem { // ㊙️ ▒▒ PUBLISHING - static readonly #statusNotPublishing: PublishingStatus = { - persisted: false, - reason: 'DISABLED_BY_OPTIONS', - } - readonly #debouncedDataRootUpdate = debounce( - async ( - args: Array<[dataRoot: CID, proofs: Ticket[]]> - ): Promise => { - const [dataRoot, proofs] = args[args.length - 1] - - await this.#dependencies.depot.flush(dataRoot, proofs) - - let status: PublishingStatus - - if (!this.#updateDataRoot) { - status = { - persisted: false, - reason: 'DATA_ROOT_UPDATER_NOT_CONFIGURED', - } - - return args.map((_) => status) - } - - const rootUpdate = await this.#updateDataRoot(dataRoot, proofs) - - if (rootUpdate.updated) { - await this.#eventEmitter.emit('publish', { dataRoot, proofs }) - status = { persisted: true } - } else { - status = { persisted: false, reason: rootUpdate.reason } + (() => { + return async (args: Array<[dataRoot: CID]>): Promise => { + const [dataRoot] = args.at(-1) + await this.emit('publish', { dataRoot }) } - - return args.map((_) => status) - }, + })(), (() => this.#settleTimeBeforePublish) as any, { accumulate: true, @@ -786,11 +687,8 @@ export class FileSystem { } ) - /** - * Updates the user's data root if it can find a UCAN that allows them to do so. - */ - async #publish(dataRoot: CID, proofs: Ticket[]): Promise { - const debounceResult = await this.#debouncedDataRootUpdate(dataRoot, proofs) + async #publish(dataRoot: CID): Promise { + const debounceResult = await this.#debouncedDataRootUpdate(dataRoot) // The type of `debounceResult` is not correct, issue with `@types/debounce-promise` return debounceResult as unknown as PublishingStatus diff --git a/packages/nest/src/index.ts b/packages/nest/src/index.ts new file mode 100644 index 0000000..67e7d05 --- /dev/null +++ b/packages/nest/src/index.ts @@ -0,0 +1,5 @@ +export * from './app-info.js' +export * from './class.js' +export * from './root-tree.js' +export * from './root-tree/basic.js' +export * from './types.js' diff --git a/packages/nest/src/path.ts b/packages/nest/src/path.ts index 1ed8c77..8d10607 100644 --- a/packages/nest/src/path.ts +++ b/packages/nest/src/path.ts @@ -163,6 +163,37 @@ export function root(): DirectoryPath { return { directory: [] } } +/** + * Utility function create an app data path. + */ +export function appData

( + partition: P, + app: AppInfo +): DirectoryPath> +export function appData

( + partition: P, + app: AppInfo, + suffix: FilePath +): FilePath> +export function appData

( + partition: P, + app: AppInfo, + suffix: DirectoryPath +): DirectoryPath> +export function appData

( + partition: P, + app: AppInfo, + suffix: DistinctivePath +): DistinctivePath> +export function appData

( + partition: P, + app: AppInfo, + suffix?: DistinctivePath +): DistinctivePath> { + const appDir = directory(partition, 'Apps', app.creator, app.name) + return suffix === undefined ? appDir : combine(appDir, suffix) +} + // POSIX /** diff --git a/packages/nest/src/types.ts b/packages/nest/src/types.ts index 5f4e40a..263b4a3 100644 --- a/packages/nest/src/types.ts +++ b/packages/nest/src/types.ts @@ -1,22 +1,17 @@ import type { CID } from 'multiformats' import type * as Path from './path.js' -/** @group File System */ export type AnySupportedDataType = | Uint8Array | Record | string -/** @group File System */ export interface DataRootChange { dataRoot: CID - publishingStatus: Promise } -/** @group File System */ export type DataType = 'bytes' | 'json' | 'utf8' -/** @group File System */ export type DataForType = D extends 'bytes' ? Uint8Array : D extends 'json' @@ -25,40 +20,33 @@ export type DataForType = D extends 'bytes' ? string : never -/** @group File System */ export interface DirectoryItem { metadata: { created: number; modified: number } name: string } -/** @group File System */ export type DirectoryItemWithKind = DirectoryItem & { kind: Path.Kind path: Path.Distinctive> } -/** @group File System */ export interface FileSystemChange { path: Path.Distinctive> type: MutationType } -/** @group File System */ export interface MutationOptions { skipPublish?: boolean } -/** @group File System */ export type MutationResult

= P extends Path.Public ? PublicMutationResult : P extends Path.Private ? PrivateMutationResult : never -/** @group File System */ export type MutationType = 'added-or-updated' | 'removed' -/** @group File System */ export type PartitionDiscovery

= P extends Path.Public ? { name: 'public' @@ -73,7 +61,6 @@ export type PartitionDiscovery

= P extends Path.Public } : never -/** @group File System */ export type PartitionDiscoveryNonEmpty

= P extends Path.Public ? { @@ -89,25 +76,17 @@ export type PartitionDiscoveryNonEmpty

= } : never -/** @group File System */ export type PublicMutationResult = DataRootChange & { capsuleCID: CID contentCID: CID } -/** @group File System */ export type PrivateMutationResult = DataRootChange & { capsuleKey: Uint8Array } -/** @group File System */ export interface TransactionResult { changes: FileSystemChange[] dataRoot: CID publishingStatus: Promise } - -/** @group File System */ -export type PublishingStatus = - | { persisted: true } - | { persisted: false; reason: string } diff --git a/packages/nest/src/types/params.ts b/packages/nest/src/types/params.ts deleted file mode 100644 index 168d375..0000000 --- a/packages/nest/src/types/params.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface RecoverFileSystemParams { - newUsername: string - oldUsername: string - readKey: Uint8Array -} diff --git a/packages/nest/test/class.test.ts b/packages/nest/test/class.test.ts index 013c195..c2ab99c 100644 --- a/packages/nest/test/class.test.ts +++ b/packages/nest/test/class.test.ts @@ -1,26 +1,16 @@ import assert from 'assert' +import type { CID } from 'multiformats' + +import * as Path from '../src/path.js' +import * as Unix from '../src/unix.js' + +import { FileSystem } from '../src/class.js' + import { assertUnixFsDirectory, assertUnixFsFile, assertUnixNodeRemoval, -} from '../../tests/helpers/filesystem.js' - -import * as Path from '../src/path/index.js' -import * as Unix from './unix.js' - -import { - account, - agent, - authority, - depot, - identifier, - manners, - storage, -} from '../../tests/helpers/components.js' -import { CID } from '../common/cid.js' -import { Inventory } from '../inventory.js' -import { Ticket } from '../ticket/types.js' -import { FileSystem } from './class.js' +} from './helpers/index.js' describe('File System Class', async () => { let fs: FileSystem diff --git a/packages/nest/test/helpers/index.ts b/packages/nest/test/helpers/index.ts new file mode 100644 index 0000000..2e466be --- /dev/null +++ b/packages/nest/test/helpers/index.ts @@ -0,0 +1,118 @@ +import assert from 'assert' +import all from 'it-all' + +import * as fc from 'fast-check' +import * as UnixExporter from 'ipfs-unixfs-exporter' +import * as Uint8Arrays from 'uint8arrays' + +import type { FileSystem } from '../../src/class.js' +import { linksFromCID } from '../../src/root-tree.js' +import * as Path from '../../src/path.js' + +// PATHS + +export function arbitraryDirectoryPath

( + partition: P +): fc.Arbitrary>> { + return fc + .array(arbitraryPathSegment(), { minLength: 1, maxLength: 8 }) + .map((array) => { + const path: Path.Directory> = { + directory: [partition, ...array], + } + return path + }) +} + +export function arbitraryFilePath

( + partition: P +): fc.Arbitrary>> { + return fc + .array(arbitraryPathSegment(), { minLength: 1, maxLength: 8 }) + .map((array) => { + const path: Path.File> = { + file: [partition, ...array], + } + return path + }) +} + +export function arbitraryPathSegment(): fc.Arbitrary { + return fc.oneof( + fc.webSegment().filter((segment) => segment.length > 0), + fc.constantFrom('a', 'b', 'c') // to generate more 'collisions' + ) +} + +// UNIX + +export async function assertUnixFsDirectory( + opts: { dependencies: { depot: Depot.Implementation } }, + fs: FileSystem, + path: Path.Directory> +): void { + const { depot } = opts.dependencies + const dataRoot = await fs.calculateDataRoot() + + const rootTree = await linksFromCID(depot, dataRoot) + const unixRoot = rootTree.unix + + const pathString = Path.toPosix(Path.removePartition(path), { + absolute: true, + }) + const entry = await UnixExporter.exporter( + `${unixRoot}${pathString}`, + depot.blockstore + ) + + assert.equal(entry.type, 'directory') +} + +export async function assertUnixFsFile( + opts: { dependencies: { depot: Depot.Implementation } }, + fs: FileSystem, + path: Path.File>, + bytes: Uint8Array +): void { + const { depot } = opts.dependencies + const dataRoot = await fs.calculateDataRoot() + + const rootTree = await linksFromCID(depot, dataRoot) + const unixRoot = rootTree.unix + + const pathString = Path.toPosix(Path.removePartition(path), { + absolute: true, + }) + const entry = await UnixExporter.exporter( + `${unixRoot}${pathString}`, + depot.blockstore + ) + const unixBytes = Uint8Arrays.concat(await all(entry.content())) + + assert.equal(Uint8Arrays.equals(unixBytes, bytes), true) +} + +export async function assertUnixNodeRemoval( + opts: { dependencies: { depot: Depot.Implementation } }, + fs: FileSystem, + path: Path.Distinctive> +): void { + const { depot } = opts.dependencies + const dataRoot = await fs.calculateDataRoot() + + const rootTree = await linksFromCID(depot, dataRoot) + const unixRoot = rootTree.unix + + const pathString = Path.toPosix(Path.removePartition(path), { + absolute: true, + }) + + try { + const _entry = await UnixExporter.exporter( + `${unixRoot}${pathString}`, + depot.blockstore + ) + } catch (error) { + assert(error.toString(), 'File does not exist') + } +} diff --git a/packages/nest/test/path.test.ts b/packages/nest/test/path.test.ts index 8f89db6..9969365 100644 --- a/packages/nest/test/path.test.ts +++ b/packages/nest/test/path.test.ts @@ -1,7 +1,9 @@ import { strict as assert } from 'assert' import * as fc from 'fast-check' + +import type { DirectoryPath, FilePath } from '../src/path.js' import * as Path from '../src/path.js' -import { DirectoryPath, FilePath, RootBranch } from './index.js' +import { RootBranch } from '../src/path.js' describe('Path functions', () => { // CREATION @@ -16,11 +18,15 @@ describe('Path functions', () => { assert.throws(() => Path.directory('/')) // Type testing - const a: Path.Directory> = + const _a: Path.Directory> = Path.directory('private') - const b: Path.Directory> = + const _b: Path.Directory> = Path.directory('public', 'a') - const c: Path.Directory = Path.directory('private', 'a', 'b') + const _c: Path.Directory = Path.directory( + 'private', + 'a', + 'b' + ) }) it('creates file paths', () => { @@ -33,11 +39,11 @@ describe('Path functions', () => { assert.throws(() => Path.file('/')) // Type testing - const a: Path.File> = Path.file( + const _a: Path.File> = Path.file( 'private', 'a' ) - const b: Path.File = Path.file('private', 'a', 'b') + const _b: Path.File = Path.file('private', 'a', 'b') }) it('creates directory paths with fromKind', () => { @@ -50,13 +56,13 @@ describe('Path functions', () => { ) // Type testing - const a: Path.Directory> = Path.fromKind( + const _a: Path.Directory> = Path.fromKind( Path.Kind.Directory, 'private' ) - const b: Path.Directory> = + const _b: Path.Directory> = Path.fromKind(Path.Kind.Directory, 'public', 'a') - const c: Path.Directory = Path.fromKind( + const _c: Path.Directory = Path.fromKind( Path.Kind.Directory, 'private', 'a', @@ -72,12 +78,12 @@ describe('Path functions', () => { ) // Type testing - const a: Path.File> = Path.fromKind( + const _a: Path.File> = Path.fromKind( Path.Kind.File, 'private', 'a' ) - const b: Path.File = Path.fromKind( + const _b: Path.File = Path.fromKind( Path.Kind.File, 'private', 'a', @@ -162,28 +168,28 @@ describe('Path functions', () => { assert.deepEqual(file, { file: ['a', 'b'] }) // Type testing - const a: DirectoryPath> = + const _a: DirectoryPath> = Path.combine(Path.directory('private'), Path.directory('a')) - const aa: FilePath> = Path.combine( + const _aa: FilePath> = Path.combine( Path.directory('public'), Path.file('a') ) - const b: DirectoryPath> = Path.combine( + const _b: DirectoryPath> = Path.combine( Path.directory('private'), Path.directory() ) - const bb: FilePath> = Path.combine( + const _bb: FilePath> = Path.combine( Path.directory('public'), Path.file() ) - const c: DirectoryPath> = + const _c: DirectoryPath> = Path.combine(Path.directory('private'), Path.directory('a')) - const cc: FilePath> = Path.combine( + const _cc: FilePath> = Path.combine( Path.directory('public'), Path.file('a') ) @@ -281,37 +287,37 @@ describe('Path functions', () => { assert.deepEqual(Path.parent(Path.file('foo')), Path.root()) - assert.equal(Path.parent(Path.root()), null) + assert.equal(Path.parent(Path.root()), undefined) // Type testing - const a: DirectoryPath> = + const _a: DirectoryPath> = Path.parent({ directory: ['private', 'a', 'b'], }) - const a_: DirectoryPath = Path.parent({ + const _a_: DirectoryPath = Path.parent({ directory: ['random', 'a', 'b'], }) - const b: DirectoryPath> = Path.parent({ + const _b: DirectoryPath> = Path.parent({ directory: ['private', 'a'], }) - const b_: DirectoryPath = Path.parent({ + const _b_: DirectoryPath = Path.parent({ directory: ['random', 'a'], }) - const c: DirectoryPath = Path.parent({ + const _c: DirectoryPath = Path.parent({ directory: ['private'], }) - const c_: DirectoryPath = Path.parent({ + const _c_: DirectoryPath = Path.parent({ directory: ['random'], }) - const x: null = Path.parent({ - directory: [], - }) + // const _x: undefined = Path.parent({ + // directory: [], + // }) }) it('supports removePartition', () => { @@ -332,7 +338,7 @@ describe('Path functions', () => { ) // Type testing - const a: DirectoryPath> = + const _a: DirectoryPath> = Path.replaceTerminus( { directory: ['private', 'a'], @@ -340,7 +346,7 @@ describe('Path functions', () => { 'b' ) - const b: FilePath> = + const _b: FilePath> = Path.replaceTerminus( { file: ['private', 'a'], @@ -348,14 +354,14 @@ describe('Path functions', () => { 'b' ) - const c: DirectoryPath = Path.replaceTerminus( + const _c: DirectoryPath = Path.replaceTerminus( { directory: ['a'], }, 'b' ) - const d: FilePath = Path.replaceTerminus( + const _d: FilePath = Path.replaceTerminus( { file: ['a'], }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d5bedf..4374687 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -66,6 +66,12 @@ importers: compare-versions: specifier: ^6.1.0 version: 6.1.0 + debounce-promise: + specifier: ^3.1.2 + version: 3.1.2 + emittery: + specifier: ^1.0.1 + version: 1.0.1 interface-blockstore: specifier: ^5.2.7 version: 5.2.7 @@ -106,12 +112,18 @@ importers: assert: specifier: ^2.1.0 version: 2.1.0 + fast-check: + specifier: ^3.14.0 + version: 3.14.0 mocha: specifier: ^10.2.0 version: 10.2.0 playwright-test: specifier: ^14.0.0 version: 14.0.0 + tsx: + specifier: ^4.6.2 + version: 4.6.2 packages: @@ -367,6 +379,15 @@ packages: jsdoc-type-pratt-parser: 4.0.0 dev: true + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64@0.19.5: resolution: {integrity: sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==} engines: {node: '>=12'} @@ -385,6 +406,15 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.19.5: resolution: {integrity: sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==} engines: {node: '>=12'} @@ -403,6 +433,15 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.19.5: resolution: {integrity: sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==} engines: {node: '>=12'} @@ -421,6 +460,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.19.5: resolution: {integrity: sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==} engines: {node: '>=12'} @@ -439,6 +487,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.19.5: resolution: {integrity: sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==} engines: {node: '>=12'} @@ -457,6 +514,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.19.5: resolution: {integrity: sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==} engines: {node: '>=12'} @@ -475,6 +541,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.19.5: resolution: {integrity: sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==} engines: {node: '>=12'} @@ -493,6 +568,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.19.5: resolution: {integrity: sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==} engines: {node: '>=12'} @@ -511,6 +595,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.19.5: resolution: {integrity: sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==} engines: {node: '>=12'} @@ -529,6 +622,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.19.5: resolution: {integrity: sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==} engines: {node: '>=12'} @@ -547,6 +649,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.19.5: resolution: {integrity: sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==} engines: {node: '>=12'} @@ -565,6 +676,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.19.5: resolution: {integrity: sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==} engines: {node: '>=12'} @@ -583,6 +703,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.19.5: resolution: {integrity: sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==} engines: {node: '>=12'} @@ -601,6 +730,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64@0.19.5: resolution: {integrity: sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==} engines: {node: '>=12'} @@ -619,6 +757,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x@0.19.5: resolution: {integrity: sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==} engines: {node: '>=12'} @@ -637,6 +784,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.19.5: resolution: {integrity: sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==} engines: {node: '>=12'} @@ -655,6 +811,15 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.19.5: resolution: {integrity: sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==} engines: {node: '>=12'} @@ -673,6 +838,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.19.5: resolution: {integrity: sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==} engines: {node: '>=12'} @@ -691,6 +865,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.19.5: resolution: {integrity: sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==} engines: {node: '>=12'} @@ -709,6 +892,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.19.5: resolution: {integrity: sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==} engines: {node: '>=12'} @@ -727,6 +919,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.19.5: resolution: {integrity: sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==} engines: {node: '>=12'} @@ -745,6 +946,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.19.5: resolution: {integrity: sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==} engines: {node: '>=12'} @@ -1923,6 +2133,10 @@ packages: type-fest: 1.4.0 dev: true + /debounce-promise@3.1.2: + resolution: {integrity: sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==} + dev: false + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2075,6 +2289,11 @@ packages: resolution: {integrity: sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==} dev: true + /emittery@1.0.1: + resolution: {integrity: sha512-2ID6FdrMD9KDLldGesP6317G78K7km/kMcwItRtVFva7I/cSEOIaLpewaUb+YLXVwdAp3Ctfxh/V5zIl1sj7dQ==} + engines: {node: '>=14.16'} + dev: false + /emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} dev: true @@ -2195,6 +2414,36 @@ packages: engines: {node: '>=0.10.0'} dev: true + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + /esbuild@0.19.5: resolution: {integrity: sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==} engines: {node: '>=12'} @@ -2767,6 +3016,13 @@ packages: homedir-polyfill: 1.0.3 dev: true + /fast-check@3.14.0: + resolution: {integrity: sha512-9Z0zqASzDNjXBox/ileV/fd+4P+V/f3o4shM6QawvcdLFh8yjPG4h5BrHUZ8yzY6amKGDTAmRMyb/JZqe+dCgw==} + engines: {node: '>=8.0.0'} + dependencies: + pure-rand: 6.0.4 + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -4524,6 +4780,10 @@ packages: engines: {node: '>=6'} dev: true + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + dev: true + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -5178,6 +5438,17 @@ packages: typescript: 5.2.2 dev: true + /tsx@4.6.2: + resolution: {integrity: sha512-QPpBdJo+ZDtqZgAnq86iY/PD2KYCUPSUGIunHdGwyII99GKH+f3z3FZ8XNFLSGQIA4I365ui8wnQpl8OKLqcsg==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.18.20 + get-tsconfig: 4.7.2 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'}