From 3ed37a9404f5385e50dcd157f8fa6962e22452ab Mon Sep 17 00:00:00 2001 From: yicchi <86967274+YiCChi@users.noreply.github.com> Date: Wed, 2 Oct 2024 00:50:47 +0900 Subject: [PATCH] feat(jest): add support for vitest (#231) --- .eslintignore | 3 + README.md | 27 +- .../aws-sdk-client-mock-jest/package.json | 47 +- .../aws-sdk-client-mock-jest/src/index.ts | 1 - packages/aws-sdk-client-mock-jest/src/jest.ts | 137 +++ .../src/jestMatchers.ts | 746 ++++++---------- .../aws-sdk-client-mock-jest/src/types.ts | 242 ++++++ .../aws-sdk-client-mock-jest/src/vitest.ts | 94 ++ .../aws-sdk-client-mock-jest/test-d/types.ts | 8 +- .../test/jestGlobals.test.ts | 10 +- .../test/jestMatchers.test.ts | 8 +- .../test/matcher.vitest.ts | 790 +++++++++++++++++ .../aws-sdk-client-mock-jest/vitest.config.ts | 16 + .../vitest.serializer.ts | 18 + pnpm-lock.yaml | 819 ++++++++++++++++++ 15 files changed, 2447 insertions(+), 519 deletions(-) delete mode 100644 packages/aws-sdk-client-mock-jest/src/index.ts create mode 100644 packages/aws-sdk-client-mock-jest/src/jest.ts create mode 100644 packages/aws-sdk-client-mock-jest/src/types.ts create mode 100644 packages/aws-sdk-client-mock-jest/src/vitest.ts create mode 100644 packages/aws-sdk-client-mock-jest/test/matcher.vitest.ts create mode 100644 packages/aws-sdk-client-mock-jest/vitest.config.ts create mode 100644 packages/aws-sdk-client-mock-jest/vitest.serializer.ts diff --git a/.eslintignore b/.eslintignore index 76c9b0e..8aca18d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,3 +3,6 @@ **/test-d/** test-e2e/** **/dist/** + +**/vitest.config.ts +**/vitest.serializer.ts diff --git a/README.md b/README.md index 89fe9e2..b1d7007 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ In action: - [About AWS SDK v3](#about-aws-sdk-v3) - [Usage](#usage) - [Install](#install) + - [Versions compatibility](#versions-compatibility) - [Import](#import) - [Mock](#mock) - [DynamoDB DocumentClient](#dynamodb-documentclient) @@ -38,7 +39,9 @@ In action: - [SDK v2-style mocks](#sdk-v2-style-mocks) - [Inspect](#inspect) - [Reset and restore](#reset-and-restore) - - [Jest matchers](#jest-matchers) + - [Custom matchers](#custom-matchers) + - [Jest](#jest) + - [Vitest](#vitest) - [API Reference](#api-reference) - [AWS Lambda example](#aws-lambda-example) - [Caveats](#caveats) @@ -46,6 +49,7 @@ In action: - [AwsClientStub and strictFunctionTypes](#awsclientstub-and-strictfunctiontypes) - [Order of mock behaviors](#order-of-mock-behaviors) - [Order of type and instance mocks](#order-of-type-and-instance-mocks) + - [Using with Mocha](#using-with-mocha) ## About AWS SDK v3 @@ -363,7 +367,7 @@ s3Mock.on(UploadPartCommand).rejects(); #### S3 GetObjectCommand AWS SDK wraps the stream in the S3 `GetObjectCommand` result to provide utility methods to parse it. -To mock it, you need to install the [`@smithy/util-stream`](https://www.npmjs.com/package/@smithy/util-stream) package +To mock it, you need to install the [`@smithy/util-stream`](https://www.npmjs.com/package/@smithy/util-stream) package and call the wrapping function `sdkStreamMixin()` on the stream you provide as the command output: ```ts @@ -510,7 +514,9 @@ You can also pass custom [Sinon Sandbox](https://sinonjs.org/releases/latest/san with `mockClient(client, { sandbox: mySandbox })` to manage all mocks lifecycle at once. -### Jest matchers +### Custom matchers + +#### Jest Custom [Jest](https://jestjs.io/) matchers simplify verification that the mocked Client was called with given Commands. @@ -556,10 +562,19 @@ expect(snsMock).toHaveReceivedNthSpecificCommandWith( ); ``` -Shorter aliases exist, like `toReceiveCommandTimes()`. +Shorter aliases exist, like `toReceiveCommandTimes()`. + +#### Vitest + +Use those matchers with [Vitest](https://vitest.dev/): + +```ts +import 'aws-sdk-client-mock-jest/vitest'; +import { expect } from 'vitest'; -To use those matchers with [Vitest](https://vitest.dev/), set `test.globals` to `true` in `vite.config.js` -(see [#139](https://github.com/m-radzikowski/aws-sdk-client-mock/issues/139)). +// a PublishCommand was sent to SNS +expect(snsMock).toHaveReceivedCommand(PublishCommand); +``` To use the matchers outside of Jest, you can pull in the [expect](https://www.npmjs.com/package/expect) library separately and add it to the global scope directly, e.g.: diff --git a/packages/aws-sdk-client-mock-jest/package.json b/packages/aws-sdk-client-mock-jest/package.json index ef78a84..20b0e00 100644 --- a/packages/aws-sdk-client-mock-jest/package.json +++ b/packages/aws-sdk-client-mock-jest/package.json @@ -25,7 +25,9 @@ "jest-matchers" ], "scripts": { - "test": "jest --coverage --colors", + "test": "pnpm run jest && pnpm run vitest", + "jest": "jest --coverage --colors ", + "vitest": "vitest run", "test-types": "tsd", "build:cjs": "tsc -p tsconfig.json", "build:es": "tsc -p tsconfig.es.json", @@ -33,13 +35,36 @@ "build": "pnpm run build:cjs && pnpm run build:es", "local-publish": "pnpm publish --registry http://localhost:4873/ --no-git-checks" }, - "module": "dist/es/index.js", - "main": "dist/cjs/index.js", - "types": "dist/types/index.d.ts", + "module": "dist/es/jest.js", + "main": "dist/cjs/jest.js", + "types": "dist/types/jest.d.ts", + "exports": { + ".": { + "require": { + "types": "./dist/types/jest.d.ts", + "default": "./dist/cjs/jest.js" + }, + "import": { + "types": "./dist/types/jest.d.ts", + "default": "./dist/es/jest.js" + } + }, + "./vitest": { + "require": { + "types": "./dist/types/vitest.d.ts", + "default": "./dist/cjs/vitest.js" + }, + "import": { + "types": "./dist/types/vitest.d.ts", + "default": "./dist/es/vitest.js" + } + } + }, "files": [ "dist" ], "dependencies": { + "@vitest/expect": ">1.6.0", "expect": ">28.1.3", "tslib": "^2.1.0" }, @@ -49,12 +74,22 @@ "@smithy/types": "1.1.0", "@types/jest": "29.5.12", "@types/sinon": "^17.0.3", + "@vitest/coverage-v8": "^2.1.1", "aws-sdk-client-mock": "workspace:*", + "chalk": "^5.3.0", "expect": "29.7.0", - "jest-serializer-ansi-escapes": "3.0.0" + "jest-serializer-ansi-escapes": "3.0.0", + "pretty-ansi": "^2.0.0", + "vitest": "^2.1.1" }, "peerDependencies": { - "aws-sdk-client-mock": "workspace:*" + "aws-sdk-client-mock": "workspace:*", + "vitest": ">1.6.0" + }, + "peerDependenciesMeta": { + "vitest": { + "optional": true + } }, "jest": { "preset": "ts-jest", diff --git a/packages/aws-sdk-client-mock-jest/src/index.ts b/packages/aws-sdk-client-mock-jest/src/index.ts deleted file mode 100644 index d1ec2d0..0000000 --- a/packages/aws-sdk-client-mock-jest/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './jestMatchers'; diff --git a/packages/aws-sdk-client-mock-jest/src/jest.ts b/packages/aws-sdk-client-mock-jest/src/jest.ts new file mode 100644 index 0000000..77430d4 --- /dev/null +++ b/packages/aws-sdk-client-mock-jest/src/jest.ts @@ -0,0 +1,137 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +import type { MatcherContext } from 'expect'; +import { expect } from 'expect'; +import type { AwsSdkMockMatchers } from './jestMatchers'; +import { createBaseMatchers } from './jestMatchers'; +import type { + AnySpyCall, + AwsSdkMockAliasMatchers, + CommonMatcherUtils, + MatcherFunction, +} from './types'; + +/** + * Prettyprints command calls for message + */ +function addCalls( + ctxUtils: CommonMatcherUtils, + calls: AnySpyCall[], + ...msgs: string[] +) { + if (calls.length === 0) return msgs.join('\n'); + + return [ + ...msgs, + '', + 'Calls:', + ...calls.map( + (c, i) => + ` ${i + 1}. ${c.args[0].constructor.name}: ${ctxUtils.printReceived( + c.args[0].input + )}` + ), + ].join('\n'); +} + +const baseMatchers = createBaseMatchers({ + toHaveReceivedCommand: ({ + client, + cmd, + notPrefix, + calls, + commandCalls, + ctxUtils, + }) => + addCalls( + ctxUtils, + calls, + `Expected ${client} to ${notPrefix}receive ${ctxUtils.printExpected(cmd)}`, + `${client} received ${ctxUtils.printExpected(cmd)} ${ctxUtils.printReceived(commandCalls.length)} times` + ), + toHaveReceivedCommandTimes: + (expectedCalls) => + ({ calls, client, cmd, commandCalls, notPrefix, ctxUtils }) => + addCalls( + ctxUtils, + calls, + `Expected ${client} to ${notPrefix}receive ${ctxUtils.printExpected(cmd)} ${ctxUtils.printExpected(expectedCalls)} times`, + `${client} received ${ctxUtils.printExpected(cmd)} ${ctxUtils.printReceived(commandCalls.length)} times` + ), + + toHaveReceivedCommandWith: + (input) => + ({ client, cmd, notPrefix, data, calls, ctxUtils }) => + addCalls( + ctxUtils, + calls, + `Expected ${client} to ${notPrefix}receive ${ctxUtils.printExpected(cmd)} with ${ctxUtils.printExpected(input)}`, + `${client} received matching ${ctxUtils.printExpected(cmd)} ${ctxUtils.printReceived(data.matchCount)} times` + ), + + toHaveReceivedNthCommandWith: + (call, input) => + ({ cmd, client, data, notPrefix, ctxUtils, calls }) => + addCalls( + ctxUtils, + calls, + `Expected ${client} to ${notPrefix}receive ${call}. ${ctxUtils.printExpected(cmd)} with ${ctxUtils.printExpected(input)}`, + ...(data.received + ? [ + `${client} received ${ctxUtils.printReceived(data.received.constructor.name)} with input:`, + ctxUtils.printDiffOrStringify(input, data.received.input, 'Expected', 'Received', false), + ] + : []) + ), + toHaveReceivedNthSpecificCommandWith: + (call, input) => + ({ cmd, client, data, notPrefix, ctxUtils, calls }) => + addCalls( + ctxUtils, + calls, + `Expected ${client} to ${notPrefix}receive ${call}. ${ctxUtils.printExpected(cmd)} with ${ctxUtils.printExpected(input)}`, + ...(data.received + ? [ + `${client} received ${ctxUtils.printReceived(data.received.constructor.name)} with input:`, + ctxUtils.printDiffOrStringify(input, data.received.input, 'Expected', 'Received', false), + ] + : []) + ), + toHaveReceivedAnyCommand: ({ client, notPrefix, calls, ctxUtils }) => + addCalls( + ctxUtils, + calls, + `Expected ${client} to ${notPrefix}receive any command`, + `${client} received any command ${ctxUtils.printReceived(calls.length)} times` + ), +}, +(sample: Record) => expect.objectContaining(sample) +); + +/* typing ensures keys matching */ +const aliasMatchers: { + [P in keyof AwsSdkMockAliasMatchers]: MatcherFunction; +} = { + toReceiveCommandTimes: baseMatchers.toHaveReceivedCommandTimes, + toReceiveCommand: baseMatchers.toHaveReceivedCommand, + toReceiveCommandWith: baseMatchers.toHaveReceivedCommandWith, + toReceiveNthCommandWith: baseMatchers.toHaveReceivedNthCommandWith, + toReceiveNthSpecificCommandWith:baseMatchers.toHaveReceivedNthSpecificCommandWith, + toReceiveAnyCommand: baseMatchers.toHaveReceivedAnyCommand, +}; + +// Skip registration if jest expect does not exist +if (typeof expect !== 'undefined' && typeof expect.extend === 'function') { + expect.extend({ ...baseMatchers, ...aliasMatchers }); +} + +/** + * Types for @types/jest + */ +declare global { + namespace jest { + interface Matchers extends AwsSdkMockMatchers {} + } +} +declare module 'expect' { + interface Matchers extends AwsSdkMockMatchers {} +} diff --git a/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts b/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts index 2db6471..e9ee1b9 100644 --- a/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts +++ b/packages/aws-sdk-client-mock-jest/src/jestMatchers.ts @@ -1,198 +1,83 @@ -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-interface */ -import assert from 'assert'; -import type {MetadataBearer} from '@smithy/types'; -import {AwsCommand, AwsStub} from 'aws-sdk-client-mock'; -import type {SinonSpyCall} from 'sinon'; -import type {ExpectationResult, MatcherContext, MatcherFunction} from 'expect'; -import {expect} from 'expect'; - -interface AwsSdkJestMockBaseMatchers extends Record { - /** - * Asserts the {@link AwsStub Client Mock} received given `Command` exact number of times. - * - * @example - * ```js - * expect(snsMock).toHaveReceivedCommandTimes(PublishCommand, 2); - * ``` - * - * @param command AWS SDK Command type - * @param times Number of expected calls - */ - toHaveReceivedCommandTimes( - command: new (input: TCmdInput) => AwsCommand, - times: number, - ): R; - - /** - * Asserts the {@link AwsStub Client Mock} received given `Command` at least one time. - * - * @example - * ```js - * expect(snsMock).toHaveReceivedCommandTimes(PublishCommand); - * ``` - * - * @param command AWS SDK Command type - */ - toHaveReceivedCommand( - command: new (input: TCmdInput) => AwsCommand, - ): R; - - /** - * Asserts the {@link AwsStub Client Mock} received given `Command` at least one time with matching input. - * - * @example - * ```js - * expect(snsMock).toHaveReceivedCommandWith( - * PublishCommand, - * { - * Message: 'hello world', - * }, - * ); - * ``` - * - * @example - * With asymmetric matcher: - * ```js - * expect(snsMock).toHaveReceivedCommandWith( - * PublishCommand, - * { - * Message: expect.stringContaining('hello'), - * }, - * ); - * ``` - * - * @param command AWS SDK Command type - * @param input Partial input to match - */ - toHaveReceivedCommandWith( - command: new (input: TCmdInput) => AwsCommand, - input: Partial<{ - [Property in keyof TCmdInput]: unknown; - }>, - ): R; - - /** - * Asserts the nth call to the {@link AwsStub Client Mock} was a given `Command` with matching input. - * - * @example - * The second call to `SNSClient` was a `PublishCommand` with Message 'hello world': - * ```js - * expect(snsMock).toHaveReceivedNthCommandWith( - * 2, - * PublishCommand, - * { - * Message: 'hello world', - * }, - * ); - * ``` - * - * @param call Call number - * @param command AWS SDK Command type - * @param input Partial input to match - */ - toHaveReceivedNthCommandWith( - call: number, - command: new (input: TCmdInput) => AwsCommand, - input: Partial<{ - [Property in keyof TCmdInput]: unknown; - }>, - ): R; - - /** - * Asserts the nth `Command` of given type sent to the {@link AwsStub Client Mock} had matching input. - * - * @example - * The second `PublishCommand` sent to `SNSClient` had Message 'hello world': - * ```js - * expect(snsMock).toHaveReceivedNthSpecificCommandWith( - * 2, - * PublishCommand, - * { - * Message: 'hello world', - * }, - * ); - * ``` - * - * @param call Call number - * @param command AWS SDK Command type - * @param input Partial input to match - */ - toHaveReceivedNthSpecificCommandWith( - call: number, - command: new (input: TCmdInput) => AwsCommand, - input: Partial<{ - [Property in keyof TCmdInput]: unknown; - }>, - ): R; +import { AwsStub } from 'aws-sdk-client-mock'; +import assert from 'node:assert'; +import type { + AnyCommand, + AnySpyCall, + AwsSdkMockAliasMatchers, + AwsSdkMockBaseMatchers, + CommonMatcherContext, + CommonMatcherUtils, + ExpectationResult, + MatcherFunction, +} from './types'; + +interface MessageFunctionParams { + cmd: string; + client: string; + commandCalls: AnySpyCall[]; + calls: AnySpyCall[]; + data: CheckData; + notPrefix: string; + ctxUtils: T; +} - /** - * Asserts {@link AwsStub Client Mock} was called at least once with any `Command`. - */ - toHaveReceivedAnyCommand(): R; +interface ProcessMatchArgs { + ctx: CommonMatcherContext; + mockClient: unknown; + command?: new () => AnyCommand; + check: (params: { calls: AnySpyCall[]; commandCalls: AnySpyCall[] }) => { + pass: boolean; + data: CheckData; + }; + message: (params: MessageFunctionParams) => string; } -interface AwsSdkJestMockAliasMatchers extends Record { - /** - * @see toHaveReceivedCommandTimes - */ - toReceiveCommandTimes( - command: new (input: TCmdInput) => AwsCommand, - times: number, - ): R; +function processMatch( + args: ProcessMatchArgs, +): ExpectationResult { + const { ctx, mockClient, command, check, message } = args; - /** - * @see toHaveReceivedCommand - */ - toReceiveCommand( - command: new (input: TCmdInput) => AwsCommand, - ): R; + assert( + mockClient instanceof AwsStub, + 'The actual must be a client mock instance' + ); - /** - * @see toHaveReceivedCommandWith - */ - toReceiveCommandWith( - command: new (input: TCmdInput) => AwsCommand, - input: Partial<{ - [Property in keyof TCmdInput]: unknown; - }>, - ): R; + if (command) { + assert( + typeof command === 'function' && + typeof command.name === 'string' && + command.name.length > 0, + 'Command must be valid AWS SDK Command' + ); + } - /** - * @see toHaveReceivedNthCommandWith - */ - toReceiveNthCommandWith( - call: number, - command: new (input: TCmdInput) => AwsCommand, - input: Partial<{ - [Property in keyof TCmdInput]: unknown; - }>, - ): R; + const calls = mockClient.calls(); + const commandCalls = command ? mockClient.commandCalls(command) : []; + + const { pass, data } = check({ calls, commandCalls }); - /** - * @see toHaveReceivedNthSpecificCommandWith - */ - toReceiveNthSpecificCommandWith( - call: number, - command: new (input: TCmdInput) => AwsCommand, - input: Partial<{ - [Property in keyof TCmdInput]: unknown; - }>, - ): R; + const msg = (): string => { + const cmd = command?.name ?? 'Any Command'; + const client = mockClient.clientName(); + + return message({ + client, + cmd, + data, + calls, + commandCalls, + notPrefix: ctx.isNot ? 'not ' : '', + ctxUtils: ctx.utils, + }); + }; - /** - * @see toHaveReceivedAnyCommand - */ - toReceiveAnyCommand(): R; + return { pass, message: msg }; } +const ensureNoOtherArgs = (args: unknown[]): void => { + assert(args.length === 0, 'Too many matcher arguments'); +}; + /** * Provides {@link jest} matcher for testing {@link AwsStub} command calls * @@ -217,317 +102,192 @@ interface AwsSdkJestMockAliasMatchers extends Record { * }); * ``` */ -export interface AwsSdkJestMockMatchers extends AwsSdkJestMockBaseMatchers, AwsSdkJestMockAliasMatchers, Record { -} - -/** - * Types for @types/jest - */ -declare global { - namespace jest { - interface Matchers extends AwsSdkJestMockMatchers { - } - } +export interface AwsSdkMockMatchers + extends AwsSdkMockBaseMatchers, + AwsSdkMockAliasMatchers, + Record {} + +interface MessageImpl { + toHaveReceivedCommandTimes: (expectedCalls: number) => (args:MessageFunctionParams) => string; + toHaveReceivedCommand: (args:MessageFunctionParams) => string; + toHaveReceivedCommandWith:(input: Record,)=> (args:MessageFunctionParams) => string; + toHaveReceivedNthCommandWith: (call: number, input: Record) => (args:MessageFunctionParams) => string; + toHaveReceivedNthSpecificCommandWith: (call: number, input: Record) => (args:MessageFunctionParams) => string; + toHaveReceivedAnyCommand: (args:MessageFunctionParams) => string; } -/** - * Types for @jest/globals - */ -declare module 'expect' { - interface Matchers extends AwsSdkJestMockMatchers { +export function createBaseMatchers( + errorMsg: MessageImpl, + objectContaining: (sample: Record) => { + asymmetricMatch(other: unknown): boolean; + toString(): string; + getExpectedType?(): string; + toAsymmetricMatcher?(): string; } -} - -type AnyCommand = AwsCommand; -type AnySpyCall = SinonSpyCall<[AnyCommand]>; -type MessageFunctionParams = { - cmd: string; - client: string; - commandCalls: AnySpyCall[]; - calls: AnySpyCall[]; - data: CheckData; - notPrefix: string; -}; - -/** - * Prettyprints command calls for message - */ -const printCalls = (ctx: MatcherContext, calls: AnySpyCall[]): string[] => - calls.length > 0 - ? [ - '', - 'Calls:', - ...calls.map( - (c, i) => - ` ${i + 1}. ${c.args[0].constructor.name}: ${ctx.utils.printReceived( - c.args[0].input, - )}`, - )] - : []; - -const processMatch = ({ctx, mockClient, command, check, message}: { - ctx: MatcherContext; - mockClient: unknown; - command?: new () => AnyCommand; - check: (params: { calls: AnySpyCall[]; commandCalls: AnySpyCall[] }) => { - pass: boolean; - data: CheckData; - }; - message: (params: MessageFunctionParams) => string[]; -}): ExpectationResult => { - assert(mockClient instanceof AwsStub, 'The actual must be a client mock instance'); - command && assert( - command && - typeof command === 'function' && - typeof command.name === 'string' && - command.name.length > 0, - 'Command must be valid AWS SDK Command', - ); - - const calls = mockClient.calls(); - const commandCalls = command ? mockClient.commandCalls(command) : []; - - const {pass, data} = check({calls, commandCalls}); - - const msg = (): string => { - const cmd = ctx.utils.printExpected(command?.name || 'Any Command'); - const client = mockClient.clientName(); - - return [ - ...message({ - client, - cmd, - data, - calls, - commandCalls, - notPrefix: ctx.isNot ? 'not ' : '', - }), - ...printCalls(ctx, calls), - ].join('\n'); - }; - - return {pass, message: msg}; -}; - -const ensureNoOtherArgs = (args: unknown[]): void => { - assert(args.length === 0, 'Too many matcher arguments'); -}; - -const baseMatchers: { [P in keyof AwsSdkJestMockBaseMatchers]: MatcherFunction } = { - /** - * implementation of {@link AwsSdkJestMockMatchers.toHaveReceivedCommand} matcher - */ - toHaveReceivedCommand( - this: MatcherContext, - mockClient: unknown, - command: new () => AnyCommand, - ...other: unknown[] - ) { - ensureNoOtherArgs(other); - return processMatch({ - ctx: this, - mockClient, - command, - check: ({commandCalls}) => ({pass: commandCalls.length > 0, data: undefined}), - message: ({client, cmd, notPrefix, commandCalls}) => [ - `Expected ${client} to ${notPrefix}receive ${cmd}`, - `${client} received ${cmd} ${this.utils.printReceived(commandCalls.length)} times`, - ], - }); - }, - /** - * implementation of {@link AwsSdkJestMockMatchers.toHaveReceivedCommandTimes} matcher - */ - toHaveReceivedCommandTimes( - this: MatcherContext, - mockClient: unknown, - command: new () => AnyCommand, - expectedCalls: number, - ...other: unknown[] - ) { - ensureNoOtherArgs(other); - return processMatch({ - ctx: this, - mockClient, - command, - check: ({commandCalls}) => ({pass: commandCalls.length === expectedCalls, data: undefined}), - message: ({client, cmd, commandCalls, notPrefix}) => [ - `Expected ${client} to ${notPrefix}receive ${cmd} ${this.utils.printExpected(expectedCalls)} times`, - `${client} received ${cmd} ${this.utils.printReceived(commandCalls.length)} times`, - ], - }); - }, - /** - * implementation of {@link AwsSdkJestMockMatchers.toHaveReceivedCommandWith} matcher - */ - toHaveReceivedCommandWith( - this: MatcherContext, - mockClient: unknown, - command: new () => AnyCommand, - input: Record, - ...other: unknown[] - ) { - ensureNoOtherArgs(other); - return processMatch<{ matchCount: number }>({ - ctx: this, - mockClient, - command, - check: ({commandCalls}) => { - const matchCount = commandCalls - .map(call => call.args[0].input) // eslint-disable-line @typescript-eslint/no-unsafe-return - .map(received => this.equals(received, expect.objectContaining(input))) - .reduce((acc, val) => acc + Number(val), 0); - - return {pass: matchCount > 0, data: {matchCount}}; - }, - message: ({client, cmd, notPrefix, data}) => [ - `Expected ${client} to ${notPrefix}receive ${cmd} with ${this.utils.printExpected(input)}`, - `${client} received matching ${cmd} ${this.utils.printReceived(data.matchCount)} times`, - ], - }); - }, - /** - * implementation of {@link AwsSdkJestMockMatchers.toHaveReceivedNthCommandWith} matcher - */ - toHaveReceivedNthCommandWith( - this: MatcherContext, - mockClient: unknown, - call: number, - command: new () => AnyCommand, - input: Record, - ...other: unknown[] - ) { - ensureNoOtherArgs(other); - assert( - call && typeof call === 'number' && call > 0, - 'Call number must be a number greater than 0', - ); - - return processMatch<{ received: AnyCommand | undefined }>({ - ctx: this, - mockClient, - command, - check: ({calls}) => { - if (calls.length < call) { - return {pass: false, data: {received: undefined}}; - } - - const received = calls[call - 1].args[0]; - - let pass = false; - if (received instanceof command) { - pass = this.equals(received.input, expect.objectContaining(input)); - } - - return { - pass, - data: {received}, - }; - }, - message: ({cmd, client, data, notPrefix}) => [ - `Expected ${client} to ${notPrefix}receive ${call}. ${cmd} with ${this.utils.printExpected(input)}`, - ...(data.received - ? [ - `${client} received ${this.utils.printReceived(data.received.constructor.name)} with input:`, - this.utils.printDiffOrStringify( - input, - data.received.input, - 'Expected', - 'Received', - false, - ), - ] - : []), - ], - }); - }, - /** - * implementation of {@link AwsSdkJestMockMatchers.toHaveReceivedNthSpecificCommandWith} matcher - */ - toHaveReceivedNthSpecificCommandWith( - this: MatcherContext, - mockClient: unknown, - call: number, - command: new () => AnyCommand, - input: Record, - ...other: unknown[] - ) { - ensureNoOtherArgs(other); - assert( - call && typeof call === 'number' && call > 0, - 'Call number must be a number greater than 0', - ); - - return processMatch<{ received: AnyCommand | undefined }>({ - ctx: this, - mockClient, - command, - check: ({commandCalls}) => { - if (commandCalls.length < call) { - return {pass: false, data: {received: undefined}}; +): { [P in keyof AwsSdkMockBaseMatchers]: MatcherFunction } { + return { + toHaveReceivedCommand( + this: CommonMatcherContext, + mockClient: unknown, + command: new () => AnyCommand, + ...other: unknown[] + ) { + ensureNoOtherArgs(other); + return processMatch( + { + ctx: this, + mockClient, + command, + check: ({ commandCalls }) => ({ + pass: commandCalls.length > 0, + data: undefined, + }), + message: errorMsg.toHaveReceivedCommand, + }, + + ); + }, + toHaveReceivedCommandTimes( + this: CommonMatcherContext, + mockClient: unknown, + command: new () => AnyCommand, + expectedCalls: number, + ...other: unknown[] + ) { + ensureNoOtherArgs(other); + return processMatch( + { + ctx: this, + mockClient, + command, + check: ({ commandCalls }) => ({ + pass: commandCalls.length === expectedCalls, + data: undefined, + }), + message: errorMsg.toHaveReceivedCommandTimes(expectedCalls), + }, + ); + }, + toHaveReceivedCommandWith( + this: CommonMatcherContext, + mockClient: unknown, + command: new () => AnyCommand, + input: Record, + ...other: unknown[] + ) { + ensureNoOtherArgs(other); + return processMatch( + { + ctx: this, + mockClient, + command, + check: ({ commandCalls }) => { + const matchCount = commandCalls + .map((call) => call.args[0].input) // eslint-disable-line @typescript-eslint/no-unsafe-return + .map((received) => objectContaining(input).asymmetricMatch(received)) + .reduce((acc, val) => acc + Number(val), 0); + + return { pass: matchCount > 0, data: { matchCount } }; + }, + message: errorMsg.toHaveReceivedCommandWith(input), + }, + + ); + }, + toHaveReceivedNthCommandWith( + this: CommonMatcherContext, + mockClient: unknown, + call: number, + command: new () => AnyCommand, + input: Record, + ...other: unknown[] + ) { + ensureNoOtherArgs(other); + assert( + call && typeof call === 'number' && call > 0, + 'Call number must be a number greater than 0' + ); + + return processMatch( + { + ctx: this, + mockClient, + command, + check: ({ calls }) => { + if (calls.length < call) { + return { pass: false, data: { received: undefined } }; + } + + const received = calls[call - 1].args[0]; + + let pass = false; + if (received instanceof command) { + pass = objectContaining(input).asymmetricMatch(received.input); + } + + return { + pass, + data: { received }, + }; + }, + message: errorMsg.toHaveReceivedNthCommandWith(call, input), + }, + ); + }, + toHaveReceivedNthSpecificCommandWith( + this: CommonMatcherContext, + mockClient: unknown, + call: number, + command: new () => AnyCommand, + input: Record, + ...other: unknown[] + ) { + ensureNoOtherArgs(other); + assert( + call && typeof call === 'number' && call > 0, + 'Call number must be a number greater than 0' + ); + + return processMatch( + { + ctx: this, + mockClient, + command, + check: ({ commandCalls }) => { + if (commandCalls.length < call) { + return { pass: false, data: { received: undefined } }; + } + + const received = commandCalls[call - 1].args[0]; + + let pass = false; + if (received instanceof command) { + pass = objectContaining(input).asymmetricMatch(received.input); + } + + return { + pass, + data: { received }, + }; + }, + message: errorMsg.toHaveReceivedNthSpecificCommandWith(call, input), } - - const received = commandCalls[call - 1].args[0]; - - let pass = false; - if (received instanceof command) { - pass = this.equals(received.input, expect.objectContaining(input)); + ); + }, + toHaveReceivedAnyCommand( + this: CommonMatcherContext, + mockClient: unknown, + ...other: unknown[] + ) { + ensureNoOtherArgs(other); + return processMatch( + { + ctx: this, + mockClient, + check: ({ calls }) => ({ pass: calls.length > 0, data: undefined }), + message: errorMsg.toHaveReceivedAnyCommand, } - - return { - pass, - data: {received}, - }; - }, - message: ({cmd, client, data, notPrefix}) => [ - `Expected ${client} to ${notPrefix}receive ${call}. ${cmd} with ${this.utils.printExpected(input)}`, - ...(data.received - ? [ - `${client} received ${this.utils.printReceived(data.received.constructor.name)} with input:`, - this.utils.printDiffOrStringify( - input, - data.received.input, - 'Expected', - 'Received', - false, - ), - ] - : []), - ], - }); - }, - /** - * implementation of {@link AwsSdkJestMockMatchers.toHaveReceivedAnyCommand} matcher - */ - toHaveReceivedAnyCommand( - this: MatcherContext, - mockClient: unknown, - ...other: unknown[] - ) { - ensureNoOtherArgs(other); - return processMatch({ - ctx: this, - mockClient, - check: ({calls}) => ({pass: calls.length > 0, data: undefined}), - message: ({client, notPrefix, calls}) => [ - `Expected ${client} to ${notPrefix}receive any command`, - `${client} received any command ${this.utils.printReceived(calls.length)} times`, - ], - }); - }, -}; - -/* typing ensures keys matching */ -const aliasMatchers: { [P in keyof AwsSdkJestMockAliasMatchers]: MatcherFunction } = { - toReceiveCommandTimes: baseMatchers.toHaveReceivedCommandTimes, - toReceiveCommand: baseMatchers.toHaveReceivedCommand, - toReceiveCommandWith: baseMatchers.toHaveReceivedCommandWith, - toReceiveNthCommandWith: baseMatchers.toHaveReceivedNthCommandWith, - toReceiveNthSpecificCommandWith: baseMatchers.toHaveReceivedNthSpecificCommandWith, - toReceiveAnyCommand: baseMatchers.toHaveReceivedAnyCommand, -}; - -// Skip registration if jest expect does not exist -if (typeof expect !== 'undefined' && typeof expect.extend === 'function') { - expect.extend({...baseMatchers, ...aliasMatchers}); + ); + }, + }; } diff --git a/packages/aws-sdk-client-mock-jest/src/types.ts b/packages/aws-sdk-client-mock-jest/src/types.ts new file mode 100644 index 0000000..75f133a --- /dev/null +++ b/packages/aws-sdk-client-mock-jest/src/types.ts @@ -0,0 +1,242 @@ +import type { MetadataBearer } from '@smithy/types'; +import type { AwsCommand } from 'aws-sdk-client-mock'; +import type { SinonSpyCall } from 'sinon'; + +// type declaration for compatibility with both Jest and Vitest +// -------------------------------------------------------------- +type EqualsFunction = ( + a: unknown, + b: unknown, + customTesters?: Array, + strictCheck?: boolean +) => boolean; + +type Tester = ( + this: TesterContext, + a: any, + b: any, + customTesters: Array +) => boolean | undefined; +interface TesterContext { + equals: EqualsFunction; +} + +interface CommonMatcherUtils { + printReceived: (object: unknown) => string; + printExpected: (value: unknown) => string; +} + +interface CommonMatcherContext { + isNot?: boolean; + equals: EqualsFunction; + utils: T; +} + +interface ExpectationResult { + message: () => string; + pass: boolean; +} + +interface MatcherFunction< + T extends CommonMatcherUtils = CommonMatcherUtils, + U extends CommonMatcherContext = CommonMatcherContext, +> { + (this: U, received: any, expected: any, ...others: any[]): ExpectationResult; +} + +// -------------------------------------------------------------- + + +type AnyCommand = AwsCommand; +type AnySpyCall = SinonSpyCall<[AnyCommand]>; + +interface AwsSdkMockBaseMatchers extends Record { + /** + * Asserts the {@link AwsStub Client Mock} received given `Command` exact number of times. + * + * @example + * ```js + * expect(snsMock).toHaveReceivedCommandTimes(PublishCommand, 2); + * ``` + * + * @param command AWS SDK Command type + * @param times Number of expected calls + */ + toHaveReceivedCommandTimes( + command: new (input: TCmdInput) => AwsCommand, + times: number, + ): R; + + /** + * Asserts the {@link AwsStub Client Mock} received given `Command` at least one time. + * + * @example + * ```js + * expect(snsMock).toHaveReceivedCommandTimes(PublishCommand); + * ``` + * + * @param command AWS SDK Command type + */ + toHaveReceivedCommand( + command: new (input: TCmdInput) => AwsCommand, + ): R; + + /** + * Asserts the {@link AwsStub Client Mock} received given `Command` at least one time with matching input. + * + * @example + * ```js + * expect(snsMock).toHaveReceivedCommandWith( + * PublishCommand, + * { + * Message: 'hello world', + * }, + * ); + * ``` + * + * @example + * With asymmetric matcher: + * ```js + * expect(snsMock).toHaveReceivedCommandWith( + * PublishCommand, + * { + * Message: expect.stringContaining('hello'), + * }, + * ); + * ``` + * + * @param command AWS SDK Command type + * @param input Partial input to match + */ + toHaveReceivedCommandWith( + command: new (input: TCmdInput) => AwsCommand, + input: Partial<{ + [Property in keyof TCmdInput]: unknown; + }>, + ): R; + + /** + * Asserts the nth call to the {@link AwsStub Client Mock} was a given `Command` with matching input. + * + * @example + * The second call to `SNSClient` was a `PublishCommand` with Message 'hello world': + * ```js + * expect(snsMock).toHaveReceivedNthCommandWith( + * 2, + * PublishCommand, + * { + * Message: 'hello world', + * }, + * ); + * ``` + * + * @param call Call number + * @param command AWS SDK Command type + * @param input Partial input to match + */ + toHaveReceivedNthCommandWith( + call: number, + command: new (input: TCmdInput) => AwsCommand, + input: Partial<{ + [Property in keyof TCmdInput]: unknown; + }>, + ): R; + + /** + * Asserts the nth `Command` of given type sent to the {@link AwsStub Client Mock} had matching input. + * + * @example + * The second `PublishCommand` sent to `SNSClient` had Message 'hello world': + * ```js + * expect(snsMock).toHaveReceivedNthSpecificCommandWith( + * 2, + * PublishCommand, + * { + * Message: 'hello world', + * }, + * ); + * ``` + * + * @param call Call number + * @param command AWS SDK Command type + * @param input Partial input to match + */ + toHaveReceivedNthSpecificCommandWith( + call: number, + command: new (input: TCmdInput) => AwsCommand, + input: Partial<{ + [Property in keyof TCmdInput]: unknown; + }>, + ): R; + + /** + * Asserts {@link AwsStub Client Mock} was called at least once with any `Command`. + */ + toHaveReceivedAnyCommand(): R; +} + +interface AwsSdkMockAliasMatchers extends Record { + /** + * @see toHaveReceivedCommandTimes + */ + toReceiveCommandTimes( + command: new (input: TCmdInput) => AwsCommand, + times: number, + ): R; + + /** + * @see toHaveReceivedCommand + */ + toReceiveCommand( + command: new (input: TCmdInput) => AwsCommand, + ): R; + + /** + * @see toHaveReceivedCommandWith + */ + toReceiveCommandWith( + command: new (input: TCmdInput) => AwsCommand, + input: Partial<{ + [Property in keyof TCmdInput]: unknown; + }>, + ): R; + + /** + * @see toHaveReceivedNthCommandWith + */ + toReceiveNthCommandWith( + call: number, + command: new (input: TCmdInput) => AwsCommand, + input: Partial<{ + [Property in keyof TCmdInput]: unknown; + }>, + ): R; + + /** + * @see toHaveReceivedNthSpecificCommandWith + */ + toReceiveNthSpecificCommandWith( + call: number, + command: new (input: TCmdInput) => AwsCommand, + input: Partial<{ + [Property in keyof TCmdInput]: unknown; + }>, + ): R; + + /** + * @see toHaveReceivedAnyCommand + */ + toReceiveAnyCommand(): R; +} + + +export { AnyCommand, AnySpyCall, AwsSdkMockAliasMatchers, AwsSdkMockBaseMatchers, CommonMatcherContext, CommonMatcherUtils, ExpectationResult, MatcherFunction }; + diff --git a/packages/aws-sdk-client-mock-jest/src/vitest.ts b/packages/aws-sdk-client-mock-jest/src/vitest.ts new file mode 100644 index 0000000..dc3e77e --- /dev/null +++ b/packages/aws-sdk-client-mock-jest/src/vitest.ts @@ -0,0 +1,94 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +import type { MatcherState } from '@vitest/expect'; +import { ObjectContaining } from '@vitest/expect'; +import { Chalk } from 'chalk'; +import { expect } from 'vitest'; +import { AwsSdkMockMatchers, createBaseMatchers } from './jestMatchers'; +import { AwsSdkMockAliasMatchers, MatcherFunction } from './types'; + +export function ordinalOf(n: number) { + const j = n % 10; + const k = n % 100; + if (j === 1 && k !== 11) return `${n}st`; + if (j === 2 && k !== 12) return `${n}nd`; + if (j === 3 && k !== 13) return `${n}rd`; + return `${n}th`; +} + +const chalk = new Chalk({ level: 3 }); + +const baseMatchers = createBaseMatchers({ + toHaveReceivedCommand: ({ client, cmd, notPrefix }) => + `expected ${client} to ${notPrefix}receive ${cmd} at least once`, + toHaveReceivedCommandTimes: + (expectedCalls) => + ({ client, cmd, commandCalls, notPrefix }) => + [ + `expected ${client} to ${notPrefix}receive ${cmd} ${expectedCalls} times`, + `but got ${commandCalls.length} times`, + ].join(', '), + toHaveReceivedCommandWith: + (input) => + ({ client, cmd, notPrefix, ctxUtils, commandCalls }) => { + return [ + `expected ${client} to ${notPrefix}receive ${cmd} with arguments [${ctxUtils.stringify(input, undefined, { printBasicPrototype: false })}]`, + chalk.gray('Received:'), + ...commandCalls.map((c, i) => { + const callAnnotation = chalk.gray(` ${ordinalOf(i + 1)} call:`); + // type of input can not be string, so the return value of `diff` will always be defined + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const diff = ctxUtils.diff(input, c.args[0].input, { omitAnnotationLines: true, commonColor: chalk.gray })!; + + return `${callAnnotation}\n\n${diff}`; + }).filter((x) => x), + chalk.gray(`Number of calls: ${commandCalls.length}`), + ].join('\n\n'); + }, + toHaveReceivedNthCommandWith: + (call, input) => + ({ cmd, client, data, notPrefix, ctxUtils, commandCalls }) => { + const beNthCalled = commandCalls.length >= call; + + const summary = `expected ${client} to ${notPrefix}receive ${ordinalOf(call)} ${cmd} with arguments [${ctxUtils.stringify(input, undefined, { printBasicPrototype: false })}]`; + const msgWhenNotBeCalled = beNthCalled ? '' : `, but ${client} has only received ${cmd} ${commandCalls.length} times`; + const diff = beNthCalled ? ctxUtils.diff(input, data.received?.input, { commonColor: chalk.gray }) : ''; + + return [summary + msgWhenNotBeCalled, diff].join('\n\n'); + }, + toHaveReceivedNthSpecificCommandWith: + (call, input) => + ({ cmd, client, data, notPrefix, ctxUtils, commandCalls }) => { + const beNthCalled = commandCalls.length >= call; + + const summary = `expected ${client} to ${notPrefix}receive ${ordinalOf(call)} ${cmd} with arguments [${ctxUtils.stringify(input, undefined, { printBasicPrototype: false })}]`; + const msgWhenNotBeCalled = beNthCalled ? '' : `, but ${client} has only received ${cmd} ${commandCalls.length} times`; + const diff = beNthCalled ? ctxUtils.diff(input, data.received?.input, { commonColor: chalk.gray }) : ''; + + return [summary + msgWhenNotBeCalled, diff].join('\n\n'); + }, + toHaveReceivedAnyCommand: ({ client, notPrefix }) => + `expected ${client} to ${notPrefix}receive any command at least once`, +}, +(sample) => new ObjectContaining(sample) +); + +const aliasMatchers: { + [P in keyof AwsSdkMockAliasMatchers]: MatcherFunction; +} = { + toReceiveCommandTimes: baseMatchers.toHaveReceivedCommandTimes, + toReceiveCommand: baseMatchers.toHaveReceivedCommand, + toReceiveCommandWith: baseMatchers.toHaveReceivedCommandWith, + toReceiveNthCommandWith: baseMatchers.toHaveReceivedNthCommandWith, + toReceiveNthSpecificCommandWith:baseMatchers.toHaveReceivedNthSpecificCommandWith, + toReceiveAnyCommand: baseMatchers.toHaveReceivedAnyCommand, +}; + +expect.extend({ + ...baseMatchers, + ...aliasMatchers, +}); + +declare module 'vitest' { + interface Assertion extends AwsSdkMockMatchers {} + interface AsymmetricMatchersContaining extends AwsSdkMockMatchers {} +} diff --git a/packages/aws-sdk-client-mock-jest/test-d/types.ts b/packages/aws-sdk-client-mock-jest/test-d/types.ts index edf989f..a184113 100644 --- a/packages/aws-sdk-client-mock-jest/test-d/types.ts +++ b/packages/aws-sdk-client-mock-jest/test-d/types.ts @@ -1,7 +1,7 @@ -import {mockClient} from 'aws-sdk-client-mock'; -import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; -import {expect as globalsExpect} from '@jest/globals'; -import {expectError} from 'tsd'; +import { PublishCommand, SNSClient } from '@aws-sdk/client-sns'; +import { expect as globalsExpect } from '@jest/globals'; +import { mockClient } from 'aws-sdk-client-mock'; +import { expectError } from 'tsd'; import '../src'; expect(1).toHaveReceivedCommand(PublishCommand); diff --git a/packages/aws-sdk-client-mock-jest/test/jestGlobals.test.ts b/packages/aws-sdk-client-mock-jest/test/jestGlobals.test.ts index fb72f88..2742c07 100644 --- a/packages/aws-sdk-client-mock-jest/test/jestGlobals.test.ts +++ b/packages/aws-sdk-client-mock-jest/test/jestGlobals.test.ts @@ -1,8 +1,8 @@ -import {mockClient} from 'aws-sdk-client-mock'; -import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; -import {publishCmd1} from 'aws-sdk-client-mock/test/fixtures'; -import {expect, it} from '@jest/globals'; -import '../src'; +import { PublishCommand, SNSClient } from '@aws-sdk/client-sns'; +import { expect, it } from '@jest/globals'; +import { mockClient } from 'aws-sdk-client-mock'; +import { publishCmd1 } from 'aws-sdk-client-mock/test/fixtures'; +import '../src/jest'; const snsMock = mockClient(SNSClient); diff --git a/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts b/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts index 4021a1c..98e6196 100644 --- a/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts +++ b/packages/aws-sdk-client-mock-jest/test/jestMatchers.test.ts @@ -1,7 +1,7 @@ -import {AwsClientStub, AwsCommand, mockClient} from 'aws-sdk-client-mock'; -import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; -import {publishCmd1, publishCmd2, subscribeCmd1} from 'aws-sdk-client-mock/test/fixtures'; -import '../src'; +import { PublishCommand, SNSClient } from '@aws-sdk/client-sns'; +import { AwsClientStub, AwsCommand, mockClient } from 'aws-sdk-client-mock'; +import { publishCmd1, publishCmd2, subscribeCmd1 } from 'aws-sdk-client-mock/test/fixtures'; +import '../src/jest'; let snsMock: AwsClientStub; diff --git a/packages/aws-sdk-client-mock-jest/test/matcher.vitest.ts b/packages/aws-sdk-client-mock-jest/test/matcher.vitest.ts new file mode 100644 index 0000000..646a3e8 --- /dev/null +++ b/packages/aws-sdk-client-mock-jest/test/matcher.vitest.ts @@ -0,0 +1,790 @@ +import { PublishCommand, SNSClient } from '@aws-sdk/client-sns'; +import { AwsCommand, mockClient } from 'aws-sdk-client-mock'; +import { + publishCmd1, + publishCmd2, + subscribeCmd1, +} from 'aws-sdk-client-mock/test/fixtures'; +import { beforeEach, describe, expect, it } from 'vitest'; +import '../src/vitest'; +import { ordinalOf } from '../src/vitest'; + +const snsMock = mockClient(SNSClient); + +beforeEach(() => { + snsMock.reset(); +}); + +describe('toHaveReceivedCommand', () => { + it('passes on receiving Command', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).toHaveReceivedCommand(PublishCommand)).not.toThrow(); + }); + + it('fails on not receiving Command', () => { + expect(() => expect(snsMock).toHaveReceivedCommand(PublishCommand)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to receive PublishCommand at least once]'); + }); + + it('fails on receiving Command with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).not.toHaveReceivedCommand(PublishCommand)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to not receive PublishCommand at least once]'); + }); + + it('fails on more arguments', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(() => expect(snsMock).not.toHaveReceivedCommand(PublishCommand, {})) + .toThrowErrorMatchingInlineSnapshot('[AssertionError: Too many matcher arguments]'); + }); +}); + +describe('toHaveReceivedCommandTimes', () => { + it('passes on receiving Command twice', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => expect(snsMock).toHaveReceivedCommandTimes(PublishCommand, 2)).not.toThrow(); + }); + + it('fails on not receiving Command twice', async () => { + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).toHaveReceivedCommandTimes(PublishCommand, 2)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to receive PublishCommand 2 times, but got 1 times]'); + }); + + it('fails on receiving Command twice with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).not.toHaveReceivedCommandTimes(PublishCommand, 2)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to not receive PublishCommand 2 times, but got 2 times]'); + }); +}); + +describe('toHaveReceivedCommandWith', () => { + it('passes on receiving Command with partial match', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toHaveReceivedCommandWith(PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).not.toThrow(); + }); + + it('fails on not receiving Command', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toHaveReceivedCommandWith(PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive PublishCommand with arguments [{ + "Message": "second mock message", +}] + +Received: + + 1st call: + + Object { +- "Message": "second mock message", ++ "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + } + +Number of calls: 1" +`); + }); + + it('fails on receiving Command with partial match with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).not.toHaveReceivedCommandWith(PublishCommand, { + Message: publishCmd1.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to not receive PublishCommand with arguments [{ + "Message": "mock message", +}] + +Received: + + 1st call: + + Object { + "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + } + +Number of calls: 1" +`); + }); + + it('passes on match with asymmetric matcher', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toHaveReceivedCommandWith(PublishCommand, { + Message: expect.stringMatching(/message/), // eslint-disable-line @typescript-eslint/no-unsafe-assignment + }) + ).not.toThrow(); + }); +}); + +describe('toHaveReceivedNthCommandWith', () => { + it('passes on receiving second Command with partial match', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toHaveReceivedNthCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).not.toThrow(); + }); + + it('displays diff when client received nth command with different arguments', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toHaveReceivedNthCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}] + +- Expected ++ Received + + Object { +- "Message": "second mock message", ++ "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); + + it('fails on receiving less Commands than the nth requested', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toHaveReceivedNthCommandWith(5, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +[Error: expected SNSClient to receive 5th PublishCommand with arguments [{ + "Message": "second mock message", +}], but SNSClient has only received PublishCommand 2 times + +] +`); + }); + + it('fails on receiving second Command with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).not.toHaveReceivedNthCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to not receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}] + +- Expected ++ Received + + Object { + "Message": "second mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); + + it('passes on match with asymmetric matcher', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toHaveReceivedNthCommandWith(2, PublishCommand, { + Message: expect.stringMatching(/second/), // eslint-disable-line @typescript-eslint/no-unsafe-assignment + }) + ).not.toThrow(); + }); + + it('fails on unmatch with asymmetric matcher', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toHaveReceivedNthCommandWith(2, PublishCommand, { + Message: expect.stringMatching(/qq/), // eslint-disable-line @typescript-eslint/no-unsafe-assignment + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": StringMatching /qq/, +}] + +- Expected ++ Received + + Object { +- "Message": StringMatching /qq/, ++ "Message": "second mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); +}); + +describe('toHaveReceivedNthSpecificCommandWith', () => { + it('passes on receiving second Command with partial match', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(subscribeCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toHaveReceivedNthSpecificCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).not.toThrow(); + }); + + it('fails on receiving less Commands than the nth expected', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toHaveReceivedNthSpecificCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +[Error: expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}], but SNSClient has only received PublishCommand 1 times + +] +`); + }); + + it('displays diff when client received nth command with different arguments', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toHaveReceivedNthSpecificCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}] + +- Expected ++ Received + + Object { +- "Message": "second mock message", ++ "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); +}); + +describe('toHaveReceivedAnyCommand', () => { + it.each` + command + ${publishCmd1} + ${subscribeCmd1} + `( + 'passes on receiving any command', + async ({ command }: { command: AwsCommand }) => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(command); // eslint-disable-line @typescript-eslint/no-unsafe-argument + + expect(() => expect(snsMock).toHaveReceivedAnyCommand()).not.toThrow(); + } + ); + + it('fails on not receiving any command', () => { + expect.assertions(2); + expect(() => expect(snsMock).toHaveReceivedAnyCommand()) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to receive any command at least once]'); + }); +}); + +// alias matchers + +describe('toReceivedCommand', () => { + it('passes on receiving Command', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).toReceiveCommand(PublishCommand)).not.toThrow(); + }); + + it('fails on not receiving Command', () => { + expect(() => expect(snsMock).toReceiveCommand(PublishCommand)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to receive PublishCommand at least once]'); + }); + + it('fails on receiving Command with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).not.toReceiveCommand(PublishCommand)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to not receive PublishCommand at least once]'); + }); + + it('fails on more arguments', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(() => expect(snsMock).not.toReceiveCommand(PublishCommand, {})) + .toThrowErrorMatchingInlineSnapshot('[AssertionError: Too many matcher arguments]'); + }); +}); + +describe('toReceivedCommandTimes', () => { + it('passes on receiving Command twice', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => expect(snsMock).toReceiveCommandTimes(PublishCommand, 2)).not.toThrow(); + }); + + it('fails on not receiving Command twice', async () => { + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).toReceiveCommandTimes(PublishCommand, 2)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to receive PublishCommand 2 times, but got 1 times]'); + }); + + it('fails on receiving Command twice with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => expect(snsMock).not.toReceiveCommandTimes(PublishCommand, 2)) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to not receive PublishCommand 2 times, but got 2 times]'); + }); +}); + +describe('toReceivedCommandWith', () => { + it('passes on receiving Command with partial match', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toReceiveCommandWith(PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).not.toThrow(); + }); + + it('fails on not receiving Command', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toReceiveCommandWith(PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive PublishCommand with arguments [{ + "Message": "second mock message", +}] + +Received: + + 1st call: + + Object { +- "Message": "second mock message", ++ "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + } + +Number of calls: 1" +`); + }); + + it('fails on receiving Command with partial match with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).not.toReceiveCommandWith(PublishCommand, { + Message: publishCmd1.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to not receive PublishCommand with arguments [{ + "Message": "mock message", +}] + +Received: + + 1st call: + + Object { + "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + } + +Number of calls: 1" +`); + }); + + it('passes on match with asymmetric matcher', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toReceiveCommandWith(PublishCommand, { + Message: expect.stringMatching(/message/), // eslint-disable-line @typescript-eslint/no-unsafe-assignment + }) + ).not.toThrow(); + }); +}); + +describe('toReceivedNthCommandWith', () => { + it('passes on receiving second Command with partial match', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toReceiveNthCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).not.toThrow(); + }); + + it('displays diff when client received nth command with different arguments', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toReceiveNthCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}] + +- Expected ++ Received + + Object { +- "Message": "second mock message", ++ "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); + + it('fails on receiving less Commands than the nth requested', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toReceiveNthCommandWith(5, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +[Error: expected SNSClient to receive 5th PublishCommand with arguments [{ + "Message": "second mock message", +}], but SNSClient has only received PublishCommand 2 times + +] +`); + }); + + it('fails on receiving second Command with not', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).not.toReceiveNthCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to not receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}] + +- Expected ++ Received + + Object { + "Message": "second mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); + + it('passes on match with asymmetric matcher', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toReceiveNthCommandWith(2, PublishCommand, { + Message: expect.stringMatching(/second/), // eslint-disable-line @typescript-eslint/no-unsafe-assignment + }) + ).not.toThrow(); + }); + + it('fails on unmatch with asymmetric matcher', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toReceiveNthCommandWith(2, PublishCommand, { + Message: expect.stringMatching(/qq/), // eslint-disable-line @typescript-eslint/no-unsafe-assignment + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": StringMatching /qq/, +}] + +- Expected ++ Received + + Object { +- "Message": StringMatching /qq/, ++ "Message": "second mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); +}); + +describe('toReceivedNthSpecificCommandWith', () => { + it('passes on receiving second Command with partial match', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(subscribeCmd1); + await sns.send(publishCmd2); + + expect(() => + expect(snsMock).toReceiveNthSpecificCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).not.toThrow(); + }); + + it('fails on receiving less Commands than the nth expected', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toReceiveNthSpecificCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +[Error: expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}], but SNSClient has only received PublishCommand 1 times + +] +`); + }); + + it('displays diff when client received nth command with different arguments', async () => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(publishCmd1); + await sns.send(publishCmd1); + + expect(() => + expect(snsMock).toReceiveNthSpecificCommandWith(2, PublishCommand, { + Message: publishCmd2.input.Message, + }) + ).toThrowErrorMatchingInlineSnapshot(` +"expected SNSClient to receive 2nd PublishCommand with arguments [{ + "Message": "second mock message", +}] + +- Expected ++ Received + + Object { +- "Message": "second mock message", ++ "Message": "mock message", ++ "TopicArn": "arn:aws:sns:us-east-1:111111111111:MyTopic", + }" +`); + }); +}); + +describe('toReceivedAnyCommand', () => { + it.each` + command + ${publishCmd1} + ${subscribeCmd1} + `( + 'passes on receiving any command', + async ({ command }: { command: AwsCommand }) => { + expect.assertions(2); + + const sns = new SNSClient({}); + await sns.send(command); // eslint-disable-line @typescript-eslint/no-unsafe-argument + + expect(() => expect(snsMock).toReceiveAnyCommand()).not.toThrow(); + } + ); + + it('fails on not receiving any command', () => { + expect.assertions(2); + expect(() => expect(snsMock).toReceiveAnyCommand()) + .toThrowErrorMatchingInlineSnapshot('[Error: expected SNSClient to receive any command at least once]'); + }); +}); + +describe('ordinalOf', () => { + const cases: Array<[number, string]> = [ + [1, '1st'], + [2, '2nd'], + [3, '3rd'], + [4, '4th'], + [9, '9th'], + [10, '10th'], + [11, '11th'], + [12, '12th'], + [13, '13th'], + [21, '21st'], + [32, '32nd'], + [93, '93rd'], + [94, '94th'], + [1001, '1001st'], + [1011, '1011th'], + [1012, '1012th'], + [1013, '1013th'], + [1021, '1021st'], + [1022, '1022nd'], + [1023, '1023rd'], + ]; + it.each(cases)('should translate %d to %s', (a, b) => { + expect(ordinalOf(a)).toStrictEqual(b); + }); +}); diff --git a/packages/aws-sdk-client-mock-jest/vitest.config.ts b/packages/aws-sdk-client-mock-jest/vitest.config.ts new file mode 100644 index 0000000..62bce68 --- /dev/null +++ b/packages/aws-sdk-client-mock-jest/vitest.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['**/*.vitest.ts'], + coverage: { + enabled: true, + provider: 'v8', + include: ['src/vitest.ts'], + thresholds: { + 100: true + } + }, + snapshotSerializers: ['./vitest.serializer.ts'], + } +}) diff --git a/packages/aws-sdk-client-mock-jest/vitest.serializer.ts b/packages/aws-sdk-client-mock-jest/vitest.serializer.ts new file mode 100644 index 0000000..51c2179 --- /dev/null +++ b/packages/aws-sdk-client-mock-jest/vitest.serializer.ts @@ -0,0 +1,18 @@ +import prettyAnsi from "pretty-ansi"; +import type { SnapshotSerializer } from "vitest"; + +export default { + serialize(text, config, indentation, depth, refs, printer) { + return printer(prettyAnsi(typeof text === 'string' ? text : text.message), config, indentation, depth, refs); + }, + test(val) { + return ( + (typeof val === "string" && val.includes(`\x1B`)) || + (val !== null && + typeof val === "object" && + "message" in val && + typeof val.message === "string" && + val.message.includes(`\x1B`)) + ); + }, +} satisfies SnapshotSerializer; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9b217a..1101a32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,6 +102,9 @@ importers: packages/aws-sdk-client-mock-jest: dependencies: + '@vitest/expect': + specifier: '>1.6.0' + version: 2.1.1 expect: specifier: '>28.1.3' version: 29.7.0 @@ -124,12 +127,24 @@ importers: '@types/sinon': specifier: ^17.0.3 version: 17.0.3 + '@vitest/coverage-v8': + specifier: ^2.1.1 + version: 2.1.1(vitest@2.1.1(@types/node@20.14.0)) aws-sdk-client-mock: specifier: workspace:* version: link:../aws-sdk-client-mock + chalk: + specifier: ^5.3.0 + version: 5.3.0 jest-serializer-ansi-escapes: specifier: 3.0.0 version: 3.0.0 + pretty-ansi: + specifier: ^2.0.0 + version: 2.0.0 + vitest: + specifier: ^2.1.1 + version: 2.1.1(@types/node@20.14.0) packages: @@ -605,6 +620,144 @@ packages: resolution: {integrity: sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==} engines: {node: '>= 6'} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -731,6 +884,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -753,6 +909,86 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@rollup/rollup-android-arm-eabi@4.18.0': + resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.0': + resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.0': + resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.0': + resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.0': + resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.0': + resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.0': + resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.0': + resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.0': + resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.0': + resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.0': + resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.0': + resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.0': + resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.0': + resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} + cpu: [x64] + os: [win32] + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -1169,6 +1405,45 @@ packages: resolution: {integrity: sha512-J0X/SFiCgty5hSI9ghjj4ZG5nf6+txfVWGzuFjlR3UPP1VvpqTu+oya/45sBwZcC/uvfm1LwKCT6tVbcQYlScg==} engines: {node: '>=12'} + '@vitest/coverage-v8@2.1.1': + resolution: {integrity: sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==} + peerDependencies: + '@vitest/browser': 2.1.1 + vitest: 2.1.1 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@2.1.1': + resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} + + '@vitest/mocker@2.1.1': + resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==} + peerDependencies: + '@vitest/spy': 2.1.1 + msw: ^2.3.5 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.1': + resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} + + '@vitest/runner@2.1.1': + resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} + + '@vitest/snapshot@2.1.1': + resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} + + '@vitest/spy@2.1.1': + resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} + + '@vitest/utils@2.1.1': + resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -1280,6 +1555,10 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + async@3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} @@ -1382,6 +1661,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} @@ -1408,6 +1691,10 @@ packages: caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + chai@5.1.1: + resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + engines: {node: '>=12'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1424,6 +1711,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1679,6 +1970,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} @@ -1695,6 +1995,10 @@ packages: babel-plugin-macros: optional: true + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1809,6 +2113,11 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -1877,6 +2186,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2042,6 +2354,9 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -2353,6 +2668,10 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} @@ -2687,6 +3006,9 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + loupe@3.1.1: + resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} + lowdb@1.0.0: resolution: {integrity: sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==} engines: {node: '>=4'} @@ -2709,6 +3031,12 @@ packages: lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + magicast@0.3.4: + resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -2855,6 +3183,11 @@ packages: nanoclone@0.2.1: resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -3054,6 +3387,13 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -3108,6 +3448,10 @@ packages: resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==} engines: {node: '>=10'} + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -3282,6 +3626,11 @@ packages: engines: {node: '>=14.18'} hasBin: true + rollup@4.18.0: + resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -3346,6 +3695,9 @@ packages: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -3372,6 +3724,10 @@ packages: sonic-boom@3.8.1: resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -3413,6 +3769,9 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standard-version@9.5.0: resolution: {integrity: sha512-3zWJ/mmZQsOaO+fOlsa0+QK90pwhNd042qEcw6hKFNoLFs7peGyvPffpEBbK/DSGPbyOvli0mUIFv5A4qTjh2Q==} engines: {node: '>=10'} @@ -3422,6 +3781,9 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + steno@0.4.4: resolution: {integrity: sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w==} @@ -3509,6 +3871,10 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + text-extensions@1.9.0: resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} engines: {node: '>=0.10'} @@ -3535,6 +3901,24 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + + tinypool@1.0.1: + resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -3765,6 +4149,64 @@ packages: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} + vite-node@2.1.1: + resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.3.2: + resolution: {integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@2.1.1: + resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.1 + '@vitest/ui': 2.1.1 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} @@ -3785,6 +4227,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -4841,6 +5288,75 @@ snapshots: tunnel-agent: 0.6.0 uuid: 8.3.2 + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': dependencies: eslint: 8.57.0 @@ -5071,6 +5587,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -5096,6 +5614,54 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@rollup/rollup-android-arm-eabi@4.18.0': + optional: true + + '@rollup/rollup-android-arm64@4.18.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.18.0': + optional: true + + '@rollup/rollup-darwin-x64@4.18.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.18.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.18.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.18.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.18.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.18.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.18.0': + optional: true + '@sinclair/typebox@0.27.8': {} '@sinonjs/commons@2.0.0': @@ -5712,6 +6278,64 @@ snapshots: minimatch: 7.4.6 semver: 7.6.0 + '@vitest/coverage-v8@2.1.1(vitest@2.1.1(@types/node@20.14.0))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.3.7 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.11 + magicast: 0.3.4 + std-env: 3.7.0 + test-exclude: 7.0.1 + tinyrainbow: 1.2.0 + vitest: 2.1.1(@types/node@20.14.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@2.1.1': + dependencies: + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 + chai: 5.1.1 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.3.2(@types/node@20.14.0))': + dependencies: + '@vitest/spy': 2.1.1 + estree-walker: 3.0.3 + magic-string: 0.30.11 + optionalDependencies: + vite: 5.3.2(@types/node@20.14.0) + + '@vitest/pretty-format@2.1.1': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.1': + dependencies: + '@vitest/utils': 2.1.1 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.1': + dependencies: + '@vitest/pretty-format': 2.1.1 + magic-string: 0.30.11 + pathe: 1.1.2 + + '@vitest/spy@2.1.1': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.1': + dependencies: + '@vitest/pretty-format': 2.1.1 + loupe: 3.1.1 + tinyrainbow: 1.2.0 + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -5814,6 +6438,8 @@ snapshots: assert-plus@1.0.0: {} + assertion-error@2.0.1: {} + async@3.2.4: {} async@3.2.5: {} @@ -5948,6 +6574,8 @@ snapshots: bytes@3.1.2: {} + cac@6.7.14: {} + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 @@ -5972,6 +6600,14 @@ snapshots: caseless@0.12.0: {} + chai@5.1.1: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.1 + pathval: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -5987,6 +6623,8 @@ snapshots: char-regex@1.0.2: {} + check-error@2.1.1: {} + ci-info@3.9.0: {} cjs-module-lexer@1.3.1: {} @@ -6274,6 +6912,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.7: + dependencies: + ms: 2.1.3 + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 @@ -6283,6 +6925,8 @@ snapshots: dedent@1.5.3: {} + deep-eql@5.0.2: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -6374,6 +7018,32 @@ snapshots: es-errors@1.3.0: {} + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + escalade@3.1.2: {} escape-html@1.0.3: {} @@ -6472,6 +7142,10 @@ snapshots: estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 + esutils@2.0.3: {} etag@1.8.1: {} @@ -6710,6 +7384,8 @@ snapshots: get-caller-file@2.0.5: {} + get-func-name@2.0.2: {} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 @@ -7012,6 +7688,14 @@ snapshots: transitivePeerDependencies: - supports-color + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.3.7 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + istanbul-reports@3.1.7: dependencies: html-escaper: 2.0.2 @@ -7505,6 +8189,10 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + loupe@3.1.1: + dependencies: + get-func-name: 2.0.2 + lowdb@1.0.0: dependencies: graceful-fs: 4.2.11 @@ -7527,6 +8215,16 @@ snapshots: lunr@2.3.9: {} + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + magicast@0.3.4: + dependencies: + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 + source-map-js: 1.2.0 + make-dir@4.0.0: dependencies: semver: 7.6.2 @@ -7655,6 +8353,8 @@ snapshots: nanoclone@0.2.1: {} + nanoid@3.3.7: {} + natural-compare-lite@1.4.0: {} natural-compare@1.4.0: {} @@ -7829,6 +8529,10 @@ snapshots: path-type@4.0.0: {} + pathe@1.1.2: {} + + pathval@2.0.0: {} + performance-now@2.1.0: {} picocolors@1.0.1: {} @@ -7893,6 +8597,12 @@ snapshots: dependencies: irregular-plurals: 3.5.0 + postcss@8.4.38: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + prelude-ls@1.2.1: {} pretty-ansi@2.0.0: {} @@ -8053,6 +8763,28 @@ snapshots: dependencies: glob: 10.4.1 + rollup@4.18.0: + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.18.0 + '@rollup/rollup-android-arm64': 4.18.0 + '@rollup/rollup-darwin-arm64': 4.18.0 + '@rollup/rollup-darwin-x64': 4.18.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 + '@rollup/rollup-linux-arm-musleabihf': 4.18.0 + '@rollup/rollup-linux-arm64-gnu': 4.18.0 + '@rollup/rollup-linux-arm64-musl': 4.18.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 + '@rollup/rollup-linux-riscv64-gnu': 4.18.0 + '@rollup/rollup-linux-s390x-gnu': 4.18.0 + '@rollup/rollup-linux-x64-gnu': 4.18.0 + '@rollup/rollup-linux-x64-musl': 4.18.0 + '@rollup/rollup-win32-arm64-msvc': 4.18.0 + '@rollup/rollup-win32-ia32-msvc': 4.18.0 + '@rollup/rollup-win32-x64-msvc': 4.18.0 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -8133,6 +8865,8 @@ snapshots: get-intrinsic: 1.2.4 object-inspect: 1.13.1 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -8162,6 +8896,8 @@ snapshots: dependencies: atomic-sleep: 1.0.0 + source-map-js@1.2.0: {} + source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 @@ -8211,6 +8947,8 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 + stackback@0.0.2: {} + standard-version@9.5.0: dependencies: chalk: 2.4.2 @@ -8230,6 +8968,8 @@ snapshots: statuses@2.0.1: {} + std-env@3.7.0: {} + steno@0.4.4: dependencies: graceful-fs: 4.2.11 @@ -8312,6 +9052,12 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.1 + minimatch: 9.0.4 + text-extensions@1.9.0: {} text-extensions@2.4.0: {} @@ -8337,6 +9083,16 @@ snapshots: through@2.3.8: {} + tinybench@2.9.0: {} + + tinyexec@0.3.0: {} + + tinypool@1.0.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + tmpl@1.0.5: {} to-fast-properties@2.0.0: {} @@ -8586,6 +9342,64 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 + vite-node@2.1.1(@types/node@20.14.0): + dependencies: + cac: 6.7.14 + debug: 4.3.7 + pathe: 1.1.2 + vite: 5.3.2(@types/node@20.14.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + + vite@5.3.2(@types/node@20.14.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.38 + rollup: 4.18.0 + optionalDependencies: + '@types/node': 20.14.0 + fsevents: 2.3.3 + + vitest@2.1.1(@types/node@20.14.0): + dependencies: + '@vitest/expect': 2.1.1 + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.3.2(@types/node@20.14.0)) + '@vitest/pretty-format': 2.1.1 + '@vitest/runner': 2.1.1 + '@vitest/snapshot': 2.1.1 + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 + chai: 5.1.1 + debug: 4.3.7 + magic-string: 0.30.11 + pathe: 1.1.2 + std-env: 3.7.0 + tinybench: 2.9.0 + tinyexec: 0.3.0 + tinypool: 1.0.1 + tinyrainbow: 1.2.0 + vite: 5.3.2(@types/node@20.14.0) + vite-node: 2.1.1(@types/node@20.14.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.14.0 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - stylus + - sugarss + - supports-color + - terser + vscode-oniguruma@1.7.0: {} vscode-textmate@8.0.0: {} @@ -8605,6 +9419,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wordwrap@1.0.0: {}