diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..70baf6d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +dist +node_modules +package.json +package-lock.json +tsconfig.json +./olympus/** diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..4dbae71 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,19 @@ +module.exports = { + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + parserOptions: { + ecmaVersion: 2020, + project: ['./tsconfig.json'], + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + }, + ], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-misused-promises': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + }, +} diff --git a/.github/workflows/main-merge.yml b/.github/workflows/main-merge.yml new file mode 100644 index 0000000..8dd1153 --- /dev/null +++ b/.github/workflows/main-merge.yml @@ -0,0 +1,52 @@ +name: Merge and Publish +on: + push: + branches: [main] +jobs: + quality-check: + name: Node Quality Check + runs-on: ubuntu-latest + env: + PORT: 8000 + strategy: + matrix: + node-version: [20.x, 19.x, 18.x] + steps: + - uses: actions/checkout@v4 + - name: Enable Corepack + run: corepack enable + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "yarn" + - name: Install packages + run: yarn install + - name: Run build command + run: yarn build + - name: Run test suite + run: yarn test run + - name: Run coverage check + run: yarn coverage + publish: + name: Publish package to npm ๐Ÿš€ + runs-on: ubuntu-latest + needs: quality-check + steps: + - uses: actions/checkout@v4 + - name: Enable Corepack + run: corepack enable + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "yarn" + - name: Install packages + run: yarn install + - name: Run build command + run: yarn build + - id: publish + name: Publish to npm ๐Ÿš€ + uses: JS-DevTools/npm-publish@v3 + with: + token: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52303bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +node_modules +.pnp +.pnp.* +coverage +dist/ +.DS_Store +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* +.env* +.idea +.vscode +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +/.env diff --git a/README.md b/README.md index f18eea1..2c2870d 100644 --- a/README.md +++ b/README.md @@ -1 +1,332 @@ -## Parallel SDK \ No newline at end of file +# Parallel + +[Get familiar with Parallel's service offering and capabilities at their docs](https://docs.parallel.ai/introduction/overview) + +## Installation + +```sh +npm install @owner.com/parallel-sdk +``` + +```sh +yarn add @owner.com/parallel-sdk +``` + +```sh +pnpm add @owner.com/parallel-sdk +``` + +## Getting started + +First, initialize a new `Parallel` instance: + +```ts +const parallel = new Parallel({apiKey: 'your-api-key'}) +``` + +You can access all available Parallel endpoints via this instance: + +```ts +await parallel.createTaskRun({ + body: { + input: 'Who is the president of France?', + processor: ParallelProcessor.Base, + task_spec: { + input_schema: { + type: 'text', + description: 'A question you should answer', + }, + output_schema: { + json_schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: false, + type: 'object', + properties: {name: {type: 'string'}, age: {type: 'number'}}, + required: ['name', 'age'], + }, + type: 'json', + }, + }, + metadata: {foo: 'bar'}, + }, +}) +``` + +## Using tasks + +As an abstraction, this package provides `ParallelTaskDefinition`, which works with `Parallel` to provide a fully typed +experience for defining and resolving task runs. + +> [!NOTE] +> Parallel does not actually have a defined entity for a `Task`, only for `TaskRun`s. The concept of a task is an +> opinion that exists purely in this library to make working with `TaskRun`s easier. It also makes `Task`s extremely +> flexible, since they only exist in your runtime code. + +You can define tasks in just a few lines: + +```ts +const getBrandLogoParallelTask = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input: new ZodParallelTaskJsonIO({ + description: 'url', + schema: z.object({url: z.string()}), + }), + output: new ZodParallelTaskJsonIO({ + description: 'logo', + schema: z.object({logoUrl: z.string()}), + }), + metadata: new ZodParallelTaskMetadata(z.object({foo: z.string()})), + defaultProcessor: ParallelProcessor.Core, +}) +``` + +Now, you can start a task using the `input` schema defined in `getBrandLogoParallelTask`. + +```ts +const taskRun = await getBrandLogoParallelTask.startRun( + { + input: {url: 'https://example.com'}, + metadata: {foo: 'bar'}, + processor: ParallelProcessor.Base, // optional - default is tasks `defaultProcessor` + }, + parallel +) +``` + +Tasks are resolved asynchronously in Parallel and can take >10 mins. To check the status of a task: + +```ts +// vv check taskRun.status +const taskRun = await getBrandLogoParallelTask.getRun(taskRun.run_id, parallel) +``` + +Once a task run's status is `completed`, you can get the result: + +```ts +// vv output is typed and parsed for you +const output = await getBrandLogoParallelTask.getRunOutput( + taskRun.run_id, + parallel +) +``` + +If you want to resolve the run synchronously, you can use `pollForRunOutput` (although this is not recommended for +slower processors): + +```ts +const output = await getBrandLogoParallelTask.pollForRunOutput( + taskRun.run_id, + parallel, + {timeoutMs: 1000, intervalMs: 100, backoffExponent: 1.5} + // ^^ all this configuration is optional - if left blank, sane defaults will be set based on the run processor +) +``` + +## Using webhooks + +> [!NOTE] +> Remember that to receive webhook events, you need to use `parallel.createTaskRunWithWebhook` from the stock client or +> add the `webhook` property if consuming `task.startRun`. + +Support for webhooks is made easy via the `ParallelWebhookHandler` class. Inside an endpoint, you can use an instance of +this class to verify and parse the event payload. Verification uses the `Parallel` client's `webhookSecret` value under +the hood to check the payload is directly from Parallel. + +```ts +const parallel = new Parallel({ + apiKey: 'api-key', + webhookSecret: 'webhook-secret', +}) +const webhookHandler = new ParallelWebhookHandler() +const payload = 'request.body' +const webhookSignature = 'request.headers.webhook-signature' +const webhookId = 'request.headers.webhook-id' +const webhookTimestamp = 'request.headers.webhook-timestamp' +const verified = await webhookHandler.verifyWebhookSecret( + {payload, webhookSignature, webhookId, webhookTimestamp}, + parallel +) +if (!verified) throw new Error() +const event = webhookHandler.parseWebhookEvent(body) +``` + +### Integrating with task definitions + +The `ParallelWebhookHandler` can take 0-n `ParallelTaskWebhook`s in its constructor. Using `handleTaskWebhook` +enables you to conveniently route the `event` to the handling logic for the task it originated from. If the run was +successful, `ParallelWebhookHandler` will automatically fetch the output for you, parse it and pipe it into the task +definitions handler. + +```ts +const getBrandLogoParallelTaskWebhook = new ParallelTaskWebhook({ + taskDefinition: getBrandLogoParallelTask, + onSuccess: (output, taskRun) => console.log(output, taskRun), +}) + +const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [getBrandLogoParallelTaskWebhook], +}) + +// ... same verification and parsing logic as example above +await webhookHandler.handleTaskWebhook(event, parallel) +``` + +### Success and failure callbacks + +All task webhooks allow you to set optional success and failure handlers that get triggered in webhooks via `onSuccess` +and `onFailure`. + +```ts +const getBrandLogoParallelWebhook = new ParallelTaskWebhook({ + // other properties hidden for brevity + onSuccess: (output, taskRun) => console.log(output, taskRun), + onFailure: (taskRun) => console.error(taskRun), +}) +``` + +By default, `ParallelWebhookHandler` will run `onSuccess` when processing a task run event with a `success` status and +`onFailure` when processing a task run event with a `failed` status. + +Sometimes, you might want to count some "successful" results as failures. Task definitions support this via the +`outputIsFailure` callback. If this function is declared, `success` status outputs will be piped into this function +before running `onSuccess/onFailure`, going to the former if the result is false, and the latter if the result is true. + +```ts +const getBrandLogoParallelWebhook = new ParallelTaskWebhook({ + // other properties hidden for brevity + onSuccess: (output, taskRun) => console.log(output, taskRun), + onFailure: (taskRun) => console.error(taskRun), + // JSON Schema doesn't support URL compliance, so we'll do it here + // Will pipe to `onFailure` if parsing fails, or `onSuccess` if it succeeds + outputIsFailure: (output, taskRun) => + !z.string().url().safeParse(output.logoUrl).success, +}) +``` + +### Retries + +Task webhooks also support retries via `retries`. You can configure it in two ways. One, is simply setting a number of +retries to do before giving up. + +```ts +const getBrandLogoParallelTask = new ParallelTaskDefinition({ + // other properties hidden for brevity + defaultProcessor: ParallelProcessor.Core, +}) + +const getBrandLogoParallelWebhook = new ParallelTaskWebhook({ + // other properties hidden for brevity + onSuccess: (output, taskRun) => console.log(output, taskRun), + retries: 3, +}) +``` + +In this configuration, if the initial run fails, the webhook handler will retry the task up to two additional runs. + +You can also use the more advanced retry behavior, which allows you to configure a number of retires for different +models, with automatic upgrades: + +```ts +const getBrandLogoParallelWebhook = new ParallelTaskWebhook({ + // other properties hidden for brevity + defaultProcessor: ParallelProcessor.Base, + onSuccess: (output, taskRun) => console.log(output, taskRun), + retries: { + [ParallelProcessor.Base]: 2, + [ParallelProcessor.Core]: 1, + [ParallelProcessor.Pro]: 1, + [ParallelProcessor.Ultra]: 1, + }, +}) +``` + +In this configuration, if the initial run fails, it will retry on the base model once. If that run fails, it will +upgrade and retry on the core model, then the pro model, and then the ultra model, one time on each. If a success is +ever encountered, it will stop retrying. + +All retry state is stored in the task run's metadata, so retries are totally asynchronous. + +### Testing webhooks locally + +To test webhooks using local endpoints, extract the webhook URL to an `.env` variable and use a local tunnelling service +like [`ngrok`](https://ngrok.com/docs/getting-started/) or [`localhost.run`](https://localhost.run/docs/) to make your +server available to Parallel. + +## Mocking + +This package has been designed with easy mocking in mind. Since abstractions rely on `IParallel`, you can easily inject +a mocked, type-safe `jest`/`vi` function into your application logic in tests. + +```ts +const mockParallel = mock() +mockParallel.getTaskRunResult.mockResolvedValueOnce({ + // ... data here +}) +await taskDefinition.getRunOutput('123', mockParallel) +``` + +If you are working with instances of `ParallelTaskDefinition`, this package exports `MockParallelForTaskDefinition`, +which makes it easy to fully simulate the behaviors of the Parallel server. + +```ts +// arrange +const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: (input) => ({logoUrl: input.url + '/test'}), + successLikelihood: 1, + timeToResolveMs: 30, +}) + +// act +const taskRun = await taskDefinition.startRun( + { + input: {url: 'https://example.com'}, + processor: ParallelProcessor.Base, + metadata: {foo: 'bar'}, + }, + mockParallel +) +const result = await taskDefinition.pollForRunOutput( + taskRun.run_id, + mockParallel, + {timeoutMs: 100, intervalMs: 10, backoffExponent: 1} +) + +// assert +expect(result).toEqual({logoUrl: 'https://example.com'}) +``` + +Under the hood, the resolution to tasks called with `mockParallel` will always be available exactly `timeToResolveMs` +after `startRun` is called, and after this time, success if determined based on `successLikelihood`, returning a call to +`outputFactory` with the original input injected if true. + +To simulate webhook handling using `MockParallelForTaskDefinition`, you can pass in a `ParallelWebhookHandler` instance: + +```ts +const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], +}) +const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: (input) => ({logoUrl: input.url + '/test'}), + successLikelihood: 1, + timeToResolveMs: 30, +}) +``` + +When you do so, if calls in your application logic specify webhook handling, the webhook handler will subscribe to a +mocked event stream with a delay of `timeToResolve`. + +```ts +// act +const taskRun = await taskDefinition.startRun( + { + input: {url: 'https://example.com'}, + processor: ParallelProcessor.Base, + metadata: {foo: 'bar'}, + webhook: {url: new URL('http://localhost:3000')}, + // ^^ will call taskDefinition's webhook onSuccess callback in 30ms + }, + mockParallel +) +``` + +This event bus resolves synchronously, so you don't need to block the thread in tests. diff --git a/api/base-encoding.ts b/api/base-encoding.ts new file mode 100644 index 0000000..95848a5 --- /dev/null +++ b/api/base-encoding.ts @@ -0,0 +1,12 @@ +import { ParallelError } from './sdk-errors' + +export const normalizeBase64Padding = (input: string): string => { + // Convert URL-safe Base64 to standard Base64 + const standardBase64 = input.replace(/-/g, '+').replace(/_/g, '/') + // Add padding if needed + const remainder = standardBase64.length % 4 + if (remainder === 2) return standardBase64 + '==' + if (remainder === 3) return standardBase64 + '=' + if (remainder === 1) throw new ParallelError('Invalid base64 string') + return standardBase64 +} diff --git a/api/client.gen.ts b/api/client.gen.ts new file mode 100644 index 0000000..d0ed4db --- /dev/null +++ b/api/client.gen.ts @@ -0,0 +1,28 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { ClientOptions } from './types.gen' +import { + type Config, + type ClientOptions as DefaultClientOptions, + createClient, + createConfig, +} from './client' + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = + ( + override?: Config + ) => Config & T> + +export const client = createClient( + createConfig({ + baseUrl: 'https://api.parallel.ai', + }) +) diff --git a/api/client/client.ts b/api/client/client.ts new file mode 100644 index 0000000..156d05f --- /dev/null +++ b/api/client/client.ts @@ -0,0 +1,195 @@ +import type { Client, Config, RequestOptions } from './types' +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils' + +type ReqInit = Omit & { + body?: any + headers: ReturnType +} + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config) + + const getConfig = (): Config => ({ ..._config }) + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config) + return getConfig() + } + + const interceptors = createInterceptors< + Request, + Response, + unknown, + RequestOptions + >() + + const request: Client['request'] = async (options) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + } + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }) + } + + if (opts.requestValidator) { + await opts.requestValidator(opts) + } + + if (opts.body && opts.bodySerializer) { + opts.body = opts.bodySerializer(opts.body) + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.body === '') { + opts.headers.delete('Content-Type') + } + + const url = buildUrl(opts) + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + } + + let request = new Request(url, requestInit) + + for (const fn of interceptors.request._fns) { + if (fn) { + request = await fn(request, opts) + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch! + let response = await _fetch(request) + + for (const fn of interceptors.response._fns) { + if (fn) { + response = await fn(response, request, opts) + } + } + + const result = { + request, + response, + } + + if (response.ok) { + if ( + response.status === 204 || + response.headers.get('Content-Length') === '0' + ) { + return opts.responseStyle === 'data' + ? {} + : { + data: {}, + ...result, + } + } + + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json' + + let data: any + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'json': + case 'text': + data = await response[parseAs]() + break + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + } + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data) + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data) + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + } + } + + const textError = await response.text() + let jsonError: unknown + + try { + jsonError = JSON.parse(textError) + } catch { + // noop + } + + const error = jsonError ?? textError + let finalError = error + + for (const fn of interceptors.error._fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string + } + } + + finalError = finalError || ({} as string) + + if (opts.throwOnError) { + throw finalError + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + } + } + + return { + buildUrl, + connect: (options) => request({ ...options, method: 'CONNECT' }), + delete: (options) => request({ ...options, method: 'DELETE' }), + get: (options) => request({ ...options, method: 'GET' }), + getConfig, + head: (options) => request({ ...options, method: 'HEAD' }), + interceptors, + options: (options) => request({ ...options, method: 'OPTIONS' }), + patch: (options) => request({ ...options, method: 'PATCH' }), + post: (options) => request({ ...options, method: 'POST' }), + put: (options) => request({ ...options, method: 'PUT' }), + request, + setConfig, + trace: (options) => request({ ...options, method: 'TRACE' }), + } +} diff --git a/api/client/index.ts b/api/client/index.ts new file mode 100644 index 0000000..189ac16 --- /dev/null +++ b/api/client/index.ts @@ -0,0 +1,22 @@ +export type { Auth } from '../core/auth' +export type { QuerySerializerOptions } from '../core/bodySerializer' +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer' +export { buildClientParams } from '../core/params' +export { createClient } from './client' +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + OptionsLegacyParser, + RequestOptions, + RequestResult, + ResponseStyle, + TDataShape, +} from './types' +export { createConfig, mergeHeaders } from './utils' diff --git a/api/client/types.ts b/api/client/types.ts new file mode 100644 index 0000000..0d118f5 --- /dev/null +++ b/api/client/types.ts @@ -0,0 +1,219 @@ +import type { Auth } from '../core/auth' +import type { Client as CoreClient, Config as CoreConfig } from '../core/types' +import type { Middleware } from './utils' + +export type ResponseStyle = 'data' | 'fields' + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl'] + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: (request: Request) => ReturnType + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: + | 'arrayBuffer' + | 'auto' + | 'blob' + | 'formData' + | 'json' + | 'stream' + | 'text' + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError'] +} + +export interface RequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + responseStyle: TResponseStyle + throwOnError: ThrowOnError + }> { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown + path?: Record + query?: Record + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray + url: Url +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record + ? TData[keyof TData] + : TData + request: Request + response: Response + } + > + : Promise< + TResponseStyle extends 'data' + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined + : ( + | { + data: TData extends Record + ? TData[keyof TData] + : TData + error: undefined + } + | { + data: undefined + error: TError extends Record + ? TError[keyof TError] + : TError + } + ) & { + request: Request + response: Response + } + > + +export interface ClientOptions { + baseUrl?: string + responseStyle?: ResponseStyle + throwOnError?: boolean +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> +) => RequestResult + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick>, 'method'> +) => RequestResult + +type BuildUrlFn = < + TData extends { + body?: unknown + path?: Record + query?: Record + url: string + }, +>( + options: Pick & Options +) => string + +export type Client = CoreClient & { + interceptors: Middleware +} + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config +) => Config & T> + +export interface TDataShape { + body?: unknown + headers?: unknown + path?: unknown + query?: unknown + url: string +} + +type OmitKeys = Pick> + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + Omit + +export type OptionsLegacyParser< + TData = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = TData extends { body?: any } + ? TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'body' | 'headers' | 'url' + > & + TData + : OmitKeys, 'body' | 'url'> & + TData & + Pick, 'headers'> + : TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'headers' | 'url' + > & + TData & + Pick, 'body'> + : OmitKeys, 'url'> & TData diff --git a/api/client/utils.ts b/api/client/utils.ts new file mode 100644 index 0000000..f7e2d1d --- /dev/null +++ b/api/client/utils.ts @@ -0,0 +1,417 @@ +import { getAuthToken } from '../core/auth' +import type { + QuerySerializer, + QuerySerializerOptions, +} from '../core/bodySerializer' +import { jsonBodySerializer } from '../core/bodySerializer' +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer' +import type { Client, ClientOptions, Config, RequestOptions } from './types' + +interface PathSerializer { + path: Record + url: string +} + +const PATH_PARAM_RE = /\{[^{}]+\}/g + +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited' +type MatrixStyle = 'label' | 'matrix' | 'simple' +type ArraySeparatorStyle = ArrayStyle | MatrixStyle + +const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url + const matches = _url.match(PATH_PARAM_RE) + if (matches) { + for (const match of matches) { + let explode = false + let name = match.substring(1, match.length - 1) + let style: ArraySeparatorStyle = 'simple' + + if (name.endsWith('*')) { + explode = true + name = name.substring(0, name.length - 1) + } + + if (name.startsWith('.')) { + name = name.substring(1) + style = 'label' + } else if (name.startsWith(';')) { + name = name.substring(1) + style = 'matrix' + } + + const value = path[name] + + if (value === undefined || value === null) { + continue + } + + if (Array.isArray(value)) { + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }) + ) + continue + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }) + ) + continue + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}` + ) + continue + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string) + ) + url = url.replace(match, replaceValue) + } + } + return url +} + +export const createQuerySerializer = ({ + allowReserved, + array, + object, +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = [] + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name] + + if (value === undefined || value === null) { + continue + } + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved, + explode: true, + name, + style: 'form', + value, + ...array, + }) + if (serializedArray) search.push(serializedArray) + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...object, + }) + if (serializedObject) search.push(serializedObject) + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved, + name, + value: value as string, + }) + if (serializedPrimitive) search.push(serializedPrimitive) + } + } + } + return search.join('&') + } + return querySerializer +} + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = ( + contentType: string | null +): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream' + } + + const cleanContent = contentType.split(';')[0]?.trim() + + if (!cleanContent) { + return + } + + if ( + cleanContent.startsWith('application/json') || + cleanContent.endsWith('+json') + ) { + return 'json' + } + + if (cleanContent === 'multipart/form-data') { + return 'formData' + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => + cleanContent.startsWith(type) + ) + ) { + return 'blob' + } + + if (cleanContent.startsWith('text/')) { + return 'text' + } + + return +} + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers + }) => { + for (const auth of security) { + const token = await getAuthToken(auth, options.auth) + + if (!token) { + continue + } + + const name = auth.name ?? 'Authorization' + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {} + } + options.query[name] = token + break + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`) + break + case 'header': + default: + options.headers.set(name, token) + break + } + + return + } +} + +export const buildUrl: Client['buildUrl'] = (options) => { + const url = getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }) + return url +} + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string + path?: Record + query?: Record + querySerializer: QuerySerializer + url: string +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}` + let url = (baseUrl ?? '') + pathUrl + if (path) { + url = defaultPathSerializer({ path, url }) + } + let search = query ? querySerializer(query) : '' + if (search.startsWith('?')) { + search = search.substring(1) + } + if (search) { + url += `?${search}` + } + return url +} + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b } + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1) + } + config.headers = mergeHeaders(a.headers, b.headers) + return config +} + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers() + for (const header of headers) { + if (!header || typeof header !== 'object') { + continue + } + + const iterator = + header instanceof Headers ? header.entries() : Object.entries(header) + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key) + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string) + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string) + ) + } + } + } + return mergedHeaders +} + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options +) => Err | Promise + +type ReqInterceptor = ( + request: Req, + options: Options +) => Req | Promise + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options +) => Res | Promise + +class Interceptors { + _fns: (Interceptor | null)[] + + constructor() { + this._fns = [] + } + + clear() { + this._fns = [] + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this._fns[id] ? id : -1 + } else { + return this._fns.indexOf(id) + } + } + exists(id: number | Interceptor) { + const index = this.getInterceptorIndex(id) + return !!this._fns[index] + } + + eject(id: number | Interceptor) { + const index = this.getInterceptorIndex(id) + if (this._fns[index]) { + this._fns[index] = null + } + } + + update(id: number | Interceptor, fn: Interceptor) { + const index = this.getInterceptorIndex(id) + if (this._fns[index]) { + this._fns[index] = fn + return id + } else { + return false + } + } + + use(fn: Interceptor) { + this._fns = [...this._fns, fn] + return this._fns.length - 1 + } +} + +// `createInterceptors()` response, meant for external use as it does not +// expose internals +export interface Middleware { + error: Pick< + Interceptors>, + 'eject' | 'use' + > + request: Pick>, 'eject' | 'use'> + response: Pick< + Interceptors>, + 'eject' | 'use' + > +} + +// do not add `Middleware` as return type so we can use _fns internally +export const createInterceptors = () => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}) + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}) + +const defaultHeaders = { + 'Content-Type': 'application/json', +} + +export const createConfig = ( + override: Config & T> = {} +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}) diff --git a/api/core/auth.ts b/api/core/auth.ts new file mode 100644 index 0000000..f3729c2 --- /dev/null +++ b/api/core/auth.ts @@ -0,0 +1,39 @@ +export type AuthToken = string | undefined + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie' + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string + scheme?: 'basic' | 'bearer' + type: 'apiKey' | 'http' +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken +): Promise => { + const token = typeof callback === 'function' ? await callback(auth) : callback + + if (!token) { + return + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}` + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}` + } + + return token +} diff --git a/api/core/bodySerializer.ts b/api/core/bodySerializer.ts new file mode 100644 index 0000000..07ed85f --- /dev/null +++ b/api/core/bodySerializer.ts @@ -0,0 +1,88 @@ +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer' + +export type QuerySerializer = (query: Record) => string + +export type BodySerializer = (body: any) => any + +export interface QuerySerializerOptions { + allowReserved?: boolean + array?: SerializerOptions + object?: SerializerOptions +} + +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown +): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value) + } else { + data.append(key, JSON.stringify(value)) + } +} + +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown +): void => { + if (typeof value === 'string') { + data.append(key, value) + } else { + data.append(key, JSON.stringify(value)) + } +} + +export const formDataBodySerializer = { + bodySerializer: | Array>>( + body: T + ): FormData => { + const data = new FormData() + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)) + } else { + serializeFormDataPair(data, key, value) + } + }) + + return data + }, +} + +export const jsonBodySerializer = { + bodySerializer: (body: T): string => + JSON.stringify(body, (_key, value) => + typeof value === 'bigint' ? value.toString() : value + ), +} + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>( + body: T + ): string => { + const data = new URLSearchParams() + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)) + } else { + serializeUrlSearchParamsPair(data, key, value) + } + }) + + return data.toString() + }, +} diff --git a/api/core/params.ts b/api/core/params.ts new file mode 100644 index 0000000..07aa650 --- /dev/null +++ b/api/core/params.ts @@ -0,0 +1,149 @@ +type Slot = 'body' | 'headers' | 'path' | 'query' + +export type Field = + | { + in: Exclude + /** + * Field name. This is the name we want the user to see and use. + */ + key: string + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string + } + | { + in: Extract + /** + * Key isn't required for bodies. + */ + key?: string + map?: string + } + +export interface Fields { + allowExtra?: Partial> + args?: ReadonlyArray +} + +export type FieldsConfig = ReadonlyArray + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +} +const extraPrefixes = Object.entries(extraPrefixesMap) + +type KeyMap = Map< + string, + { + in: Slot + map?: string + } +> + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map() + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }) + } + } else if (config.args) { + buildKeyMap(config.args, map) + } + } + + return map +} + +interface Params { + body: unknown + headers: Record + path: Record + query: Record +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Object.keys(value).length) { + delete params[slot as Slot] + } + } +} + +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig +) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + } + + const map = buildKeyMap(fields) + + let config: FieldsConfig[number] | undefined + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index] + } + + if (!config) { + continue + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)! + const name = field.map || config.key + ;(params[field.in] as Record)[name] = arg + } else { + params.body = arg + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key) + + if (field) { + const name = field.map || key + ;(params[field.in] as Record)[name] = value + } else { + const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)) + + if (extra) { + const [prefix, slot] = extra + ;(params[slot] as Record)[ + key.slice(prefix.length) + ] = value + } else { + for (const [slot, allowed] of Object.entries( + config.allowExtra ?? {} + )) { + if (allowed) { + ;(params[slot as Slot] as Record)[key] = value + break + } + } + } + } + } + } + } + + stripEmptySlots(params) + + return params +} diff --git a/api/core/pathSerializer.ts b/api/core/pathSerializer.ts new file mode 100644 index 0000000..df2f61c --- /dev/null +++ b/api/core/pathSerializer.ts @@ -0,0 +1,179 @@ +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean + name: string +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean + style: T +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited' +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle +type MatrixStyle = 'label' | 'matrix' | 'simple' +export type ObjectStyle = 'form' | 'deepObject' +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.' + case 'matrix': + return ';' + case 'simple': + return ',' + default: + return '&' + } +} + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ',' + case 'pipeDelimited': + return '|' + case 'spaceDelimited': + return '%20' + default: + return ',' + } +} + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.' + case 'matrix': + return ';' + case 'simple': + return ',' + default: + return '&' + } +} + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[] +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)) + switch (style) { + case 'label': + return `.${joinedValues}` + case 'matrix': + return `;${name}=${joinedValues}` + case 'simple': + return joinedValues + default: + return `${name}=${joinedValues}` + } + } + + const separator = separatorArrayExplode(style) + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string) + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }) + }) + .join(separator) + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues +} + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return '' + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects arenโ€™t supported. Provide your own `querySerializer()` to handle these.' + ) + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}` +} + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date + valueOnly?: boolean +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}` + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = [] + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ] + }) + const joinedValues = values.join(',') + switch (style) { + case 'form': + return `${name}=${joinedValues}` + case 'label': + return `.${joinedValues}` + case 'matrix': + return `;${name}=${joinedValues}` + default: + return joinedValues + } + } + + const separator = separatorObjectExplode(style) + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }) + ) + .join(separator) + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues +} diff --git a/api/core/types.ts b/api/core/types.ts new file mode 100644 index 0000000..80ecfd8 --- /dev/null +++ b/api/core/types.ts @@ -0,0 +1,118 @@ +import type { Auth, AuthToken } from './auth' +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer' + +export interface Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, +> { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn + connect: MethodFn + delete: MethodFn + get: MethodFn + getConfig: () => Config + head: MethodFn + options: MethodFn + patch: MethodFn + post: MethodFn + put: MethodFn + request: RequestFn + setConfig: (config: Config) => Config + trace: MethodFn +} + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + > + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: + | 'CONNECT' + | 'DELETE' + | 'GET' + | 'HEAD' + | 'OPTIONS' + | 'PATCH' + | 'POST' + | 'PUT' + | 'TRACE' + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K] +} diff --git a/api/index.ts b/api/index.ts new file mode 100644 index 0000000..16b44b6 --- /dev/null +++ b/api/index.ts @@ -0,0 +1,3 @@ +// This file is auto-generated by @hey-api/openapi-ts +export * from './types.gen' +export * from './sdk.gen' diff --git a/api/schemas.gen.ts b/api/schemas.gen.ts new file mode 100644 index 0000000..6f3ecf3 --- /dev/null +++ b/api/schemas.gen.ts @@ -0,0 +1,688 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export const CitationSchema = { + properties: { + title: { + anyOf: [ + { + type: 'string', + }, + { + type: 'null', + }, + ], + title: 'Title', + description: 'Title of the citation.', + }, + url: { + type: 'string', + title: 'Url', + description: 'URL of the citation.', + }, + excerpts: { + anyOf: [ + { + items: { + type: 'string', + }, + type: 'array', + }, + { + type: 'null', + }, + ], + title: 'Excerpts', + description: + 'Excerpts from the citation supporting the output. Only certain processors provide excerpts.', + }, + }, + type: 'object', + required: ['url'], + title: 'Citation', + description: 'A citation for a task output.', +} as const + +export const ErrorSchema = { + properties: { + ref_id: { + type: 'string', + title: 'Ref Id', + description: 'Reference ID for the error.', + }, + message: { + type: 'string', + title: 'Message', + description: 'Human-readable message.', + }, + detail: { + anyOf: [ + { + type: 'object', + }, + { + type: 'null', + }, + ], + title: 'Detail', + description: 'Optional detail supporting the error.', + }, + }, + type: 'object', + required: ['ref_id', 'message'], + title: 'Error', + description: 'An error message.', +} as const + +export const ErrorResponseSchema = { + properties: { + type: { + type: 'string', + enum: ['error'], + const: 'error', + title: 'Type', + description: "Always 'error'.", + default: 'error', + }, + error: { + allOf: [ + { + $ref: '#/components/schemas/Error', + }, + ], + description: 'Error.', + }, + }, + type: 'object', + required: ['error'], + title: 'ErrorResponse', + description: 'Response object used for non-200 status codes.', +} as const + +export const HTTPValidationErrorSchema = { + properties: { + detail: { + items: { + $ref: '#/components/schemas/ValidationError', + }, + type: 'array', + title: 'Detail', + }, + }, + type: 'object', + title: 'HTTPValidationError', +} as const + +export const JsonSchemaSchema = { + properties: { + json_schema: { + type: 'object', + title: 'Json Schema', + description: + 'A JSON Schema object. Only a subset of JSON Schema is supported.', + examples: [ + { + additionalProperties: false, + properties: { + gdp: { + description: + "GDP in USD for the year, formatted like '$3.1 trillion (2023)'", + type: 'string', + }, + }, + required: ['gdp'], + type: 'object', + }, + ], + }, + type: { + type: 'string', + enum: ['json'], + const: 'json', + title: 'Type', + description: 'The type of schema being defined. Always `json`.', + default: 'json', + }, + }, + type: 'object', + required: ['json_schema'], + title: 'JsonSchema', + description: 'JSON schema for a task input or output.', +} as const + +export const TaskRunSchema = { + properties: { + run_id: { + type: 'string', + title: 'Run Id', + description: 'ID of the task run.', + }, + status: { + type: 'string', + enum: [ + 'queued', + 'action_required', + 'running', + 'completed', + 'failed', + 'cancelling', + 'cancelled', + ], + title: 'Status', + description: 'Status of the run.', + examples: [ + 'queued', + 'action_required', + 'running', + 'completed', + 'failed', + 'cancelling', + 'cancelled', + ], + }, + is_active: { + type: 'boolean', + title: 'Is Active', + description: + "Whether the run is currently active; i.e. status is one of {'running', 'queued', 'cancelling'}.", + }, + warnings: { + anyOf: [ + { + items: { + $ref: '#/components/schemas/shapley__search__tasks__api_v1__Warning', + }, + type: 'array', + }, + { + type: 'null', + }, + ], + title: 'Warnings', + description: 'Warnings for the run.', + }, + processor: { + type: 'string', + title: 'Processor', + description: 'Processor used for the run.', + }, + metadata: { + anyOf: [ + { + additionalProperties: { + anyOf: [ + { + type: 'string', + }, + { + type: 'integer', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + type: 'object', + }, + { + type: 'null', + }, + ], + title: 'Metadata', + description: 'User-provided metadata stored with the run.', + }, + created_at: { + anyOf: [ + { + type: 'string', + }, + { + type: 'null', + }, + ], + title: 'Created At', + description: + 'Timestamp of the creation of the task, as an RFC 3339 string.', + }, + modified_at: { + anyOf: [ + { + type: 'string', + }, + { + type: 'null', + }, + ], + title: 'Modified At', + description: + 'Timestamp of the last modification to the task, as an RFC 3339 string.', + }, + }, + type: 'object', + required: [ + 'run_id', + 'status', + 'is_active', + 'processor', + 'created_at', + 'modified_at', + ], + title: 'TaskRun', + description: 'Status of a task.', +} as const + +export const TaskRunInput_InputSchema = { + properties: { + task_spec: { + anyOf: [ + { + $ref: '#/components/schemas/TaskSpec', + }, + { + type: 'null', + }, + ], + description: 'Task specification.', + }, + input: { + anyOf: [ + { + type: 'string', + }, + { + type: 'object', + }, + ], + title: 'Input', + description: 'Input to the task, either text or a JSON object.', + examples: [ + 'France (2023)', + { + country: 'France', + year: 2023, + }, + ], + }, + processor: { + type: 'string', + title: 'Processor', + description: 'Processor to use for the task.', + }, + metadata: { + anyOf: [ + { + additionalProperties: { + anyOf: [ + { + type: 'string', + }, + { + type: 'integer', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + type: 'object', + }, + { + type: 'null', + }, + ], + title: 'Metadata', + description: + 'User-provided metadata stored with the run. Keys and values must be strings with a maximum length of 16 and 512 characters respectively.', + }, + }, + type: 'object', + required: ['input', 'processor'], + title: 'TaskRunInput', + description: 'Request to run a task.', +} as const + +export const TaskRunInput_OutputSchema = { + properties: { + task_spec: { + anyOf: [ + { + $ref: '#/components/schemas/TaskSpec', + }, + { + type: 'null', + }, + ], + description: 'Task specification.', + }, + input: { + anyOf: [ + { + type: 'string', + }, + { + type: 'object', + }, + ], + title: 'Input', + description: 'Input to the task, either text or a JSON object.', + examples: [ + 'France (2023)', + { + country: 'France', + year: 2023, + }, + ], + }, + processor: { + type: 'string', + title: 'Processor', + description: 'Processor to use for the task.', + }, + metadata: { + anyOf: [ + { + additionalProperties: { + anyOf: [ + { + type: 'string', + }, + { + type: 'integer', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + type: 'object', + }, + { + type: 'null', + }, + ], + title: 'Metadata', + description: + 'User-provided metadata stored with the run. Keys and values must be strings with a maximum length of 16 and 512 characters respectively.', + }, + }, + type: 'object', + required: ['input', 'processor'], + title: 'TaskRunInput', + description: 'Request to run a task.', +} as const + +export const TaskRunJsonOutputSchema = { + properties: { + content: { + type: 'object', + title: 'Content', + description: + 'Output from the task as a native JSON object, as determined by the output schema of the task spec.', + }, + basis: { + items: { + $ref: '#/components/schemas/shapley__search__tasks__api_v1__FieldBasis', + }, + type: 'array', + title: 'Basis', + description: 'Basis for each top-level field in the JSON output.', + }, + type: { + type: 'string', + enum: ['json'], + const: 'json', + title: 'Type', + description: + 'The type of output being returned, as determined by the output schema of the task spec.', + }, + }, + type: 'object', + required: ['content', 'basis', 'type'], + title: 'TaskRunJsonOutput', + description: 'Output from a task that returns text.', +} as const + +export const TaskRunResultSchema = { + properties: { + run: { + allOf: [ + { + $ref: '#/components/schemas/TaskRun', + }, + ], + description: "Task run object with status 'completed'.", + }, + output: { + anyOf: [ + { + $ref: '#/components/schemas/TaskRunTextOutput', + }, + { + $ref: '#/components/schemas/TaskRunJsonOutput', + }, + ], + title: 'Output', + description: 'Output from the task conforming to the output schema.', + }, + }, + type: 'object', + required: ['run', 'output'], + title: 'TaskRunResult', + description: 'Result of a task run.', +} as const + +export const TaskRunTextOutputSchema = { + properties: { + content: { + type: 'string', + title: 'Content', + description: 'Text output from the task.', + }, + basis: { + items: { + $ref: '#/components/schemas/shapley__search__tasks__api_v1__FieldBasis', + }, + type: 'array', + title: 'Basis', + description: + "Basis for the output. The basis has a single field 'output'.", + }, + type: { + type: 'string', + enum: ['text'], + const: 'text', + title: 'Type', + description: + 'The type of output being returned, as determined by the output schema of the task spec.', + }, + }, + type: 'object', + required: ['content', 'basis', 'type'], + title: 'TaskRunTextOutput', + description: 'Output from a task that returns text.', +} as const + +export const TaskSpecSchema = { + properties: { + output_schema: { + anyOf: [ + { + $ref: '#/components/schemas/JsonSchema', + }, + { + $ref: '#/components/schemas/TextSchema', + }, + { + type: 'string', + }, + ], + title: 'Output Schema', + description: + 'JSON schema or text fully describing the desired output from the task. Descriptions of output fields will determine the form and content of the response. A bare string is equivalent to a text schema with the same description.', + }, + input_schema: { + anyOf: [ + { + $ref: '#/components/schemas/JsonSchema', + }, + { + $ref: '#/components/schemas/TextSchema', + }, + { + type: 'string', + }, + { + type: 'null', + }, + ], + title: 'Input Schema', + description: + 'Optional JSON schema or text description of expected input to the task. A bare string is equivalent to a text schema with the same description.', + }, + }, + type: 'object', + required: ['output_schema'], + title: 'TaskSpec', + description: `Specification for a task. + +For convenience we allow bare strings as input or output schemas, which is +equivalent to a text schema with the same description.`, +} as const + +export const TextSchemaSchema = { + properties: { + description: { + type: 'string', + title: 'Description', + description: 'A text description of the desired output from the task.', + examples: [ + "GDP in USD for the year, formatted like '$3.1 trillion (2023)'", + ], + }, + type: { + type: 'string', + enum: ['text'], + const: 'text', + title: 'Type', + description: 'The type of schema being defined. Always `text`.', + default: 'text', + }, + }, + type: 'object', + required: ['description'], + title: 'TextSchema', + description: 'Text description for a task input or output.', +} as const + +export const ValidationErrorSchema = { + properties: { + loc: { + items: { + anyOf: [ + { + type: 'string', + }, + { + type: 'integer', + }, + ], + }, + type: 'array', + title: 'Location', + }, + msg: { + type: 'string', + title: 'Message', + }, + type: { + type: 'string', + title: 'Error Type', + }, + }, + type: 'object', + required: ['loc', 'msg', 'type'], + title: 'ValidationError', +} as const + +export const shapley__search__tasks__api_v1__FieldBasisSchema = { + properties: { + field: { + type: 'string', + title: 'Field', + description: 'Name of the output field.', + }, + citations: { + items: { + $ref: '#/components/schemas/Citation', + }, + type: 'array', + title: 'Citations', + description: 'List of citations supporting the output field.', + default: [], + }, + reasoning: { + type: 'string', + title: 'Reasoning', + description: 'Reasoning for the output field.', + }, + confidence: { + anyOf: [ + { + type: 'string', + }, + { + type: 'null', + }, + ], + title: 'Confidence', + description: + 'Confidence level for the output field. Only certain processors provide confidence levels.', + examples: ['low', 'medium', 'high'], + }, + }, + type: 'object', + required: ['field', 'reasoning'], + title: 'FieldBasis', + description: 'Citations and reasoning supporting one field of a task output.', +} as const + +export const shapley__search__tasks__api_v1__WarningSchema = { + properties: { + type: { + type: 'string', + title: 'Type', + description: + 'Type of warning. Note that adding new warning types is considered a backward-compatible change.', + examples: ['spec_validation', 'input_validation'], + }, + message: { + type: 'string', + title: 'Message', + description: 'Human-readable message.', + }, + detail: { + anyOf: [ + { + type: 'object', + }, + { + type: 'null', + }, + ], + title: 'Detail', + description: 'Optional detail supporting the warning.', + }, + }, + type: 'object', + required: ['type', 'message'], + title: 'Warning', + description: 'Human-readable message for a task.', +} as const diff --git a/api/sdk-errors.ts b/api/sdk-errors.ts new file mode 100644 index 0000000..aea9a40 --- /dev/null +++ b/api/sdk-errors.ts @@ -0,0 +1,65 @@ +import { WithLogger } from './sdk-logging' + +/** + * Base error class for all errors thrown by the Parallel SDK and its utilities + */ +export class ParallelError extends Error {} + +/** + * Wraps a method in a try/catch block and throws a new error with the cause if an error is thrown + * @note The method must be a class method and the class must be a class that implements WithLogger + * @param errorConstructor - The constructor to use for the error + * @returns The wrapped method + */ +export function WrapErrorsIn( + errorConstructor: new ( + message: string, + options?: { cause?: unknown } + ) => Error +) { + return function ( + target: any, + propertyKey: string, + descriptor: PropertyDescriptor + ) { + const originalMethod = descriptor.value + descriptor.value = function (this: WithLogger, ...args: any[]) { + try { + const result = originalMethod.apply(this, args) + if (result instanceof Promise) { + return result.catch((err) => { + if (err instanceof ParallelError) { + this.logger?.error('Parallel: ' + err.message, { error: err }) + throw err + } + if (err instanceof Error) { + this.logger?.error('Parallel: ' + err.message, { error: err }) + throw new errorConstructor(err.message, { cause: err }) + } + this.logger?.error('Parallel: Unexpected error occurred', { + error: err, + }) + throw new errorConstructor('Unexpected error occurred', { + cause: err, + }) + }) + } + return result + } catch (err) { + if (err instanceof ParallelError) { + this.logger?.error('Parallel: ' + err.message, { error: err }) + throw err + } + if (err instanceof Error) { + this.logger?.error('Parallel: ' + err.message, { error: err }) + throw new errorConstructor(err.message, { cause: err }) + } + this.logger?.error('Parallel: Unexpected error occurred', { + error: err, + }) + throw new errorConstructor('Unexpected error occurred', { cause: err }) + } + } + return descriptor + } +} diff --git a/api/sdk-logging.ts b/api/sdk-logging.ts new file mode 100644 index 0000000..3ca0cc9 --- /dev/null +++ b/api/sdk-logging.ts @@ -0,0 +1,11 @@ +type LogFn = (message: string, ...rest: unknown[]) => void +export type Logger = { + error: LogFn + warn: LogFn + info: LogFn + debug: LogFn +} + +export interface WithLogger { + logger?: Logger +} diff --git a/api/sdk-mocks.ts b/api/sdk-mocks.ts new file mode 100644 index 0000000..c2d9a2d --- /dev/null +++ b/api/sdk-mocks.ts @@ -0,0 +1,297 @@ +import { delay, Subject, tap } from 'rxjs' +import { randomUUID } from 'crypto' +import { ParallelError, WrapErrorsIn } from './sdk-errors' +import { ParallelWebhookEvent, ParallelWebhookHandler } from './sdk-webhook' +import { TaskRun } from './types.gen' +import { + CreateTaskRunOptions, + CreateTaskRunResult, + CreateTaskRunWithWebhookOptions, + GetTaskRunInputOptions, + GetTaskRunInputResult, + GetTaskRunOptions, + GetTaskRunResult, + GetTaskRunResultOptions, + GetTaskRunResultResult, + IParallel, +} from './sdk-parallel' +import { ParallelTaskDefinition } from './sdk-task-definition' + +/** + * Options for creating a mock parallel client for a task definition + * @param TInput - The type of the task input + * @param TOutput - The type of the task output + */ +export interface MockParallelForTaskDefinitionOptions { + /** + * If a task run is successful, this function will be called to simulate the output of the task run from the server + * @param input - The input of the task run + * @returns The output of the task run + */ + outputFactory: (input: TInput) => TOutput + /** + * The likelihood of a task run being successful between 0 and 1 + */ + successLikelihood: number + /** + * The time in milliseconds to wait before resolving a task run as successful or failed + */ + timeToResolveMs: number + /** + * If provided, a webhook event will be sent to this handler after the task run resolves as successful or failed + * @default - no webhook will be sent + */ + webhookHandler?: ParallelWebhookHandler +} + +/** + * A mock parallel client that fully simulates the behavior of a parallel server for creating, polling, and retrieving task runs + * Allows for more comprehensive testing of a ParallelTaskDefinition instance by allowing randomizing the resolution of task runs + * Can be combined with a ParallelWebhookHandler to test webhook handling + * @param TIdentifier - The type of the task identifier + * @param TInput - The type of the task input + * @param TOutput - The type of the task output + * @param TMetadata - The type of the task metadata + */ +export class MockParallelForTaskDefinition< + TIdentifier extends string, + TInput extends { [key: string]: unknown }, + TOutput extends { [key: string]: unknown }, + TMetadata extends { [key: string]: string | number | boolean }, +> implements IParallel +{ + private readonly taskRuns = new Map() + private readonly taskRunInputs = new Map() + private readonly taskRunResults = new Map() + private readonly outputFactory: (input: TInput) => TOutput + private readonly successLikelihood: number + private readonly timeToResolveMs: number + private readonly eventStream$ = new Subject() + private webhookHandler?: ParallelWebhookHandler + constructor( + private readonly taskDefinition: ParallelTaskDefinition< + TIdentifier, + TInput, + TOutput, + TMetadata + >, + options: MockParallelForTaskDefinitionOptions + ) { + this.outputFactory = options.outputFactory + this.successLikelihood = options.successLikelihood + this.timeToResolveMs = options.timeToResolveMs + this.webhookHandler = options.webhookHandler + this.eventStream$ + .pipe( + delay(this.timeToResolveMs), // give time to set result + tap(async (event) => { + if (this.webhookHandler) { + const taskRun = this.accessTaskRun(event.data.run_id) + await this.webhookHandler.handleTaskWebhook( + { ...event, data: taskRun! }, + this + ) + } + }) + ) + .subscribe() + } + + /** + * Set the webhook handler for this client + * @param handler + */ + @WrapErrorsIn(ParallelError) + public updateWebhookHandler(handler: ParallelWebhookHandler) { + this.webhookHandler = handler + } + + /** + * Create a task run with the provided input and metadata + * @param options - The options for creating a task run + * @returns The created task run + */ + @WrapErrorsIn(ParallelError) + public async createTaskRun( + options: CreateTaskRunOptions + ): Promise { + const taskRun: TaskRun = { + created_at: new Date().toISOString(), + is_active: true, + modified_at: new Date().toISOString(), + processor: options.body.processor, + run_id: randomUUID(), + status: 'running', + metadata: options.body.metadata, + } + this.taskRuns.set(taskRun.run_id, taskRun) + this.taskRunInputs.set( + taskRun.run_id, + this.taskDefinition.input.parse(options.body.input) + ) + return { + data: taskRun, + error: undefined, + request: new Request('https://parallel.ai'), + response: new Response(), + } + } + + /** + * Create a task run with the provided input and metadata and send a webhook event to the event stream (will be automatically sent to the webhook handler if provided) + * @param options - The options for creating a task run + * @returns The created task run + */ + @WrapErrorsIn(ParallelError) + public async createTaskRunWithWebhook( + options: CreateTaskRunWithWebhookOptions + ): Promise { + const taskRun = await this.createTaskRun(options) + if (taskRun.data) { + this.eventStream$.next({ + data: taskRun.data, + timestamp: new Date().toISOString(), + type: 'task_run.status', + }) + } + return taskRun + } + + @WrapErrorsIn(ParallelError) + private accessTaskRun(runId: string): TaskRun | undefined { + const taskRun = this.taskRuns.get(runId) + if (!taskRun) return undefined + if (taskRun.status === 'completed' || taskRun.status === 'failed') + return taskRun + const now = new Date() + const createdAt = new Date(taskRun.created_at!) + const resolutionTime = createdAt.getTime() + this.timeToResolveMs + const hasResolved = now.getTime() >= resolutionTime + if (!hasResolved) return taskRun + const isSuccess = Math.random() < this.successLikelihood + const newTaskRun: TaskRun = { + ...taskRun, + status: isSuccess ? 'completed' : 'failed', + modified_at: now.toISOString(), + } + if (isSuccess) { + this.taskRunResults.set( + runId, + this.outputFactory(this.taskRunInputs.get(runId)!) + ) + } + this.taskRuns.set(runId, newTaskRun) + return newTaskRun + } + + /** + * Get a task run by its ID (must have been created with this client first) + * @param options - The options for getting a task run + * @returns The task run + */ + @WrapErrorsIn(ParallelError) + public async getTaskRun( + options: GetTaskRunOptions + ): Promise { + const taskRun = this.accessTaskRun(options.path.run_id) + if (!taskRun) { + return { + data: undefined, + error: { + error: { + message: 'Task run not found', + ref_id: randomUUID(), + }, + }, + request: new Request('https://parallel.ai'), + response: new Response(), + } + } + return { + data: taskRun, + error: undefined, + request: new Request('https://parallel.ai'), + response: new Response(), + } + } + + /** + * Get the input of a task run by its ID (must have been created with this client first) + * @param options - The options for getting a task run input + * @returns The input of the task run + */ + @WrapErrorsIn(ParallelError) + public async getTaskRunInput( + options: GetTaskRunInputOptions + ): Promise { + const taskRun = this.accessTaskRun(options.path.run_id) + const taskRunInput = this.taskRunInputs.get(options.path.run_id) + if (!taskRunInput || !taskRun) { + return { + data: undefined, + error: { + error: { + message: 'Task run input not found', + ref_id: randomUUID(), + }, + }, + request: new Request('https://parallel.ai'), + response: new Response(), + } + } + return { + data: { + input: taskRunInput, + processor: taskRun.processor, + metadata: taskRun.metadata, + task_spec: { + output_schema: this.taskDefinition.output, + input_schema: this.taskDefinition.input, + }, + }, + error: undefined, + request: new Request('https://parallel.ai'), + response: new Response(), + } + } + + /** + * Get the result of a task run by its ID + * + * Results are only available if: + * - `timeToResolveMs` has passed since task creation + * - the task run completed successfully (derived from `successLikelihood`) + * + * @param options - The options for getting a task run result + * @returns The result of the task run + */ + @WrapErrorsIn(ParallelError) + public async getTaskRunResult( + options: GetTaskRunResultOptions + ): Promise { + const taskRun = this.accessTaskRun(options.path.run_id) + const taskRunResult = this.taskRunResults.get(options.path.run_id) + if (!taskRun || !taskRunResult) { + return { + data: undefined, + error: { + error: { + message: 'Task run result not found', + ref_id: randomUUID(), + }, + }, + request: new Request('https://parallel.ai'), + response: new Response(), + } + } + return { + data: { + output: { type: 'json', content: taskRunResult, basis: [] }, + run: taskRun, + }, + error: undefined, + request: new Request('https://parallel.ai'), + response: new Response(), + } + } +} diff --git a/api/sdk-parallel.ts b/api/sdk-parallel.ts new file mode 100644 index 0000000..9749888 --- /dev/null +++ b/api/sdk-parallel.ts @@ -0,0 +1,213 @@ +import { Options } from './client' +import { client } from './client.gen' +import { ParallelError, WrapErrorsIn } from './sdk-errors' +import { Logger } from './sdk-logging' +import { + tasksRunsGetV1TasksRunsRunIdGet, + tasksRunsInputGetV1TasksRunsRunIdInputGet, + tasksRunsPostV1TasksRunsPost, + tasksRunsResultGetV1TasksRunsRunIdResultGet, +} from './sdk.gen' +import { + TasksRunsGetV1TasksRunsRunIdGetData, + TasksRunsInputGetV1TasksRunsRunIdInputGetData, + TasksRunsPostV1TasksRunsPostData, + TasksRunsResultGetV1TasksRunsRunIdResultGetData, +} from './types.gen' + +/** + * Processors are the engines that execute Task Runs. The choice of Processor determines the performance profile and reasoning behavior used. Pricing is determined by which Processor you select, not by the Task Run itself. Any Task Run can be executed on any Processor. + * @see https://docs.parallel.ai/core-concepts/processors + */ +export enum ParallelProcessor { + /** + * Handles basic, one-hop information retrieval Tasks. Lowest compute cost and also typically lower latency + */ + Lite = 'lite', + /** + * Great for basic to moderateโ€‘complexity enrichments (e.g., company firmographics). Executes a simpler retrieval and reasoning to fill 1โ€‘6 columns. + */ + Base = 'base', + /** + * The workhorse processor, best on price-performance. Performs multiโ€‘hop reasoning, crossโ€‘validating facts across multiple sources. Ideal for moderate complexity tasks where trust and scale both matter. + */ + Core = 'core', + /** + * Designed for longerโ€‘running, highโ€‘complexity tasks. Reliably populates up to 20 output fields, with cross-validation, confidence and excerpts. + */ + Pro = 'pro', + /** + * The most advanced processor. Runs openโ€‘ended research with extensive retrieval and multiโ€‘stage reasoning, humanโ€‘quality analyses on some complex tasks. + */ + Ultra = 'ultra', +} + +export type CreateTaskRunOptions = Options +export type CreateTaskRunResult = ReturnType< + typeof tasksRunsPostV1TasksRunsPost +> +export type CreateTaskRunWithWebhookOptions = Options< + Omit & { + body: Options['body'] & { + webhook: { url: string } + } + } +> +export type GetTaskRunOptions = Options +export type GetTaskRunResult = ReturnType< + typeof tasksRunsGetV1TasksRunsRunIdGet +> + +export type GetTaskRunInputOptions = + Options +export type GetTaskRunInputResult = ReturnType< + typeof tasksRunsInputGetV1TasksRunsRunIdInputGet +> +export type GetTaskRunResultOptions = + Options +export type GetTaskRunResultResult = ReturnType< + typeof tasksRunsResultGetV1TasksRunsRunIdResultGet +> + +export interface IParallel { + webhookSecret?: string + createTaskRun(options: CreateTaskRunOptions): Promise + createTaskRunWithWebhook( + options: CreateTaskRunWithWebhookOptions + ): Promise + getTaskRun(options: GetTaskRunOptions): Promise + getTaskRunInput( + options: GetTaskRunInputOptions + ): Promise + getTaskRunResult( + options: GetTaskRunResultOptions + ): Promise +} + +export interface ParallelOptions { + /** + * The API key to use for the Parallel client. + */ + apiKey: string + /** + * The webhook secret to use when creating a task run with a webhook + * @default undefined + */ + webhookSecret?: string + /** + * The base URL to use for the Parallel client. + * @default https://api.parallel.ai + */ + baseUrl?: string + /** + * The logger to use for the Parallel client. + * @default - no logging will be performed + */ + logger?: Logger +} + +/** + * Standard Parallel client implementation + */ +export class Parallel implements IParallel { + private readonly apiKey: string + public readonly webhookSecret?: string + private readonly logger?: Logger + constructor(options: ParallelOptions) { + this.apiKey = options.apiKey + this.webhookSecret = options.webhookSecret + client.setConfig({ + auth: this.apiKey, + baseUrl: options.baseUrl ?? 'https://api.parallel.ai', + }) + this.logger = options.logger + } + + /** + * Create a new task run that will not trigger a webhook + * @param options - The options for the task run + * @returns The created task run + */ + @WrapErrorsIn(ParallelError) + public async createTaskRun( + options: CreateTaskRunOptions + ): Promise { + this.logger?.info('Parallel: Creating task run', { options }) + const result = await tasksRunsPostV1TasksRunsPost(options) + this.logger?.info('Parallel: Task run created', { result }) + return result + } + + /** + * Create a new task run that will trigger a webhook + * @param options - The options for the task run + * @returns The created task run + */ + @WrapErrorsIn(ParallelError) + public async createTaskRunWithWebhook( + options: CreateTaskRunWithWebhookOptions + ): Promise { + this.logger?.info('Parallel: Creating task run with webhook', { options }) + const result = await client.post({ + url: '/v1beta/tasks/runs', + headers: { + 'x-api-key': this.apiKey, + }, + body: { + ...options.body, + webhook: { + url: options.body.webhook.url, + event_types: ['task_run.status'], + secret: this.webhookSecret, + }, + }, + }) + this.logger?.info('Parallel: Task run created with webhook', { result }) + return result as unknown as CreateTaskRunResult + } + + /** + * Get a task run by its ID + * @param options - The options for the task run + * @returns The task run + */ + @WrapErrorsIn(ParallelError) + public async getTaskRun( + options: GetTaskRunOptions + ): Promise { + this.logger?.info('Parallel: Getting task run', { options }) + const result = await tasksRunsGetV1TasksRunsRunIdGet(options) + this.logger?.info('Parallel: Task run retrieved', { result }) + return result + } + + /** + * Get a task run input by its ID + * @param options - The options for the task run input + * @returns The task run input + */ + @WrapErrorsIn(ParallelError) + public async getTaskRunInput( + options: GetTaskRunInputOptions + ): Promise { + this.logger?.info('Parallel: Getting task run input', { options }) + const result = await tasksRunsInputGetV1TasksRunsRunIdInputGet(options) + this.logger?.info('Parallel: Task run input retrieved', { result }) + return result + } + + /** + * Get a task run result by its ID + * @param options - The options for the task run result + * @returns The task run result + */ + @WrapErrorsIn(ParallelError) + public async getTaskRunResult( + options: GetTaskRunResultOptions + ): Promise { + this.logger?.info('Parallel: Getting task run result', { options }) + const result = await tasksRunsResultGetV1TasksRunsRunIdResultGet(options) + this.logger?.info('Parallel: Task run result retrieved', { result }) + return result + } +} diff --git a/api/sdk-schemas.ts b/api/sdk-schemas.ts new file mode 100644 index 0000000..f4578fd --- /dev/null +++ b/api/sdk-schemas.ts @@ -0,0 +1,48 @@ +import { z } from 'zod' +import { ParallelProcessor } from './sdk-parallel' + +const preprocessInput = (input: unknown) => { + if ( + typeof input === 'object' && + input !== null && + 'webhook_url' in input && + input.webhook_url === undefined + ) { + const { webhook_url, ...rest } = input as Record + return rest + } + return input +} + +export const taskRunMetadataZodSchema = z.preprocess( + preprocessInput, + z + .object({ + task_id: z.string(), + attempt: z.number().optional(), + webhook_url: z.string().url().optional(), // Make optional + }) + .and(z.record(z.string(), z.union([z.string(), z.number(), z.boolean()]))) +) + +export const taskRunStatusWebhookEventZodSchema = z.object({ + timestamp: z.string(), + type: z.literal('task_run.status'), + data: z.object({ + run_id: z.string(), + status: z.enum([ + 'queued', + 'action_required', + 'running', + 'completed', + 'failed', + 'cancelling', + 'cancelled', + ]), + is_active: z.boolean(), + processor: z.nativeEnum(ParallelProcessor), + created_at: z.string(), + modified_at: z.string(), + metadata: taskRunMetadataZodSchema, + }), +}) diff --git a/api/sdk-task-definition.ts b/api/sdk-task-definition.ts new file mode 100644 index 0000000..6a656c4 --- /dev/null +++ b/api/sdk-task-definition.ts @@ -0,0 +1,512 @@ +import z, { ZodSchema } from 'zod' + +import { zodToJsonSchema } from 'zod-to-json-schema' +import { ParallelError, WrapErrorsIn } from './sdk-errors' +import { Logger } from './sdk-logging' +import { JsonSchema, TaskRun } from './types.gen' +import { + CreateTaskRunResult, + IParallel, + ParallelProcessor, +} from './sdk-parallel' +import { ParallelTaskRunStatusWebhookEvent } from './sdk-webhook' +import { + taskRunMetadataZodSchema, + taskRunStatusWebhookEventZodSchema, +} from './sdk-schemas' + +type UnwrapPromise = T extends Promise ? U : T + +/** + * Interface for a task JSON input/output schema compatible with task runs + * @param T - The held type of the task JSON input/output schema + */ +export interface ParallelTaskJsonIO { + /** + * The description of the task JSON input/output schema + */ + description: string + jsonSchema: JsonSchema + parse: (input: unknown) => T +} + +/** + * Options for creating a Zod-based task JSON input/output schema + * @param T - The held type of the task JSON input/output schema + */ +export interface ZodParallelTaskJsonIOOptions< + T extends { [key: string]: unknown }, +> { + /** + * The description of the task JSON input/output schema + */ + description: string + /** + * The Zod schema to use for the task JSON input/output schema + */ + schema: ZodSchema +} + +/** + * Zod-based implementation of a task JSON input/output schema + * @param description - The description of the task JSON input/output schema + * @param zodSchema - The Zod schema to use for the task JSON input/output schema + */ +export class ZodParallelTaskJsonIO + implements ParallelTaskJsonIO +{ + description: string + jsonSchema: JsonSchema + parse: (input: unknown) => T + + constructor(options: ZodParallelTaskJsonIOOptions) { + this.description = options.description + this.jsonSchema = { + type: 'json', + json_schema: zodToJsonSchema(options.schema, { + target: 'jsonSchema7', + $refStrategy: 'none', + }), + } + this.parse = (input: unknown) => { + const parsed = options.schema.safeParse(input) + if (!parsed.success) { + throw new ParallelError(`Invalid ${this.description} input`, { + cause: parsed.error, + }) + } + return parsed.data + } + } +} + +/** + * Interface for a task metadata schema + * @param T - The held type of the task metadata schema + */ +export interface ParallelTaskMetadata< + T extends { [key: string]: string | number | boolean }, +> { + parse: (input: unknown) => T +} + +/** + * Zod-based implementation of a task run metadata schema + * @param zodSchema - The Zod schema to use for the task metadata schema + */ +export class ZodParallelTaskMetadata< + T extends { [key: string]: string | number | boolean }, +> implements ParallelTaskMetadata +{ + parse: (input: unknown) => T + + constructor(options: { schema: ZodSchema }) { + this.parse = (input: unknown) => { + const parsed = options.schema.safeParse(input) + if (!parsed.success) { + throw new ParallelError(`Invalid task metadata`, { + cause: parsed.error, + }) + } + return parsed.data + } + } +} + +export type TaskRunDefaultMetadata = { + task_id: TIdentifier + attempt?: number + webhook_url?: string +} + +export type TaskRunForTask< + TIdentifier extends string, + TMetadata extends { [key: string]: string | number | boolean }, +> = Omit & { + metadata: TMetadata & + TaskRunDefaultMetadata & + TaskRun['metadata'] +} + +/** + * Options for starting a typed task run + * @param TInput - The type of the task input + * @param TMetadata - The type of the task metadata + */ +export interface StartRunOptions< + TInput extends { [key: string]: unknown }, + TMetadata extends { [key: string]: string | number | boolean }, +> { + /** + * The input for the task run + */ + input: TInput + /** + * The processor to use for the task run + * @default - the default processor for the task definition + */ + processor?: ParallelProcessor + /** + * The metadata for the task run + */ + metadata: TMetadata & { attempt?: number } + /** + * The webhook to use for the task run. If provided, a webhook event will be sent to the provided URL. + * @default - no webhook will be sent + */ + webhook?: URL +} + +interface PollingOptions { + /** + * The timeout for the polling operation + * @default - a default timeout will be used based on the processor + */ + timeoutMs?: number + /** + * The interval between polling attempts + * @default - 2 seconds + */ + intervalMs?: number + /** + * The backoff exponent for the polling operation + * @default - 1.5 + */ + backoffExponent?: number +} + +/** + * The necessary options for creating a strongly-typed task definition + * @param TIdentifier - The type of the task identifier + * @param TInput - The type of the task input + * @param TOutput - The type of the task output + * @param TMetadata - The type of the task metadata + */ +export interface ParallelTaskDefinitionOptions< + TIdentifier extends string, + TInput extends { [key: string]: unknown }, + TOutput extends { [key: string]: unknown }, + TMetadata extends { [key: string]: string | number | boolean }, +> { + /** + * The identifier of the task appended to `metadata.task_id` for tasks run via `ParallelTaskDefinition` instances + */ + identifier: TIdentifier + /** + * The input schema of the task + */ + input: ParallelTaskJsonIO + /** + * The output schema of the task + */ + output: ParallelTaskJsonIO + /** + * The metadata schema of the task + */ + metadata: ParallelTaskMetadata + /** + * The processor to use for the task run + */ + defaultProcessor: ParallelProcessor + /** + * The logger to use for the task definition + * @default - no logging will be performed + */ + logger?: Logger +} + +export class ParallelTaskDefinition< + TIdentifier extends string, + TInput extends { [key: string]: unknown }, + TOutput extends { [key: string]: unknown }, + TMetadata extends { [key: string]: string | number | boolean }, +> { + /** + * The identifier of the task + */ + identifier: TIdentifier + /** + * The input schema of the task + */ + input: ParallelTaskJsonIO + /** + * The output schema of the task + */ + output: ParallelTaskJsonIO + /** + * The metadata schema of the task + */ + metadata: ParallelTaskMetadata + /** + * The default processor to use for the task run + */ + defaultProcessor: ParallelProcessor + /** + * The logger to use for the task definition + * @default - no logging will be performed + */ + private logger?: Logger + + constructor( + definition: ParallelTaskDefinitionOptions< + TIdentifier, + TInput, + TOutput, + TMetadata + > + ) { + this.identifier = definition.identifier + this.input = definition.input + this.output = definition.output + this.metadata = { + parse: (input) => { + const metadata = taskRunMetadataZodSchema.parse(input) + return definition.metadata.parse(metadata) + }, + } + this.defaultProcessor = definition.defaultProcessor + this.logger = definition.logger + } + + private parseMetadata(input: { + metadata?: { [key: string]: string | number | boolean } | null | undefined + }): TMetadata & TaskRunDefaultMetadata & TaskRun['metadata'] { + if (!input.metadata) { + throw new ParallelError( + `Invalid run of ${this.identifier}: missing metadata`, + { + cause: input, + } + ) + } + const parsed = taskRunMetadataZodSchema.safeParse(input.metadata) + if (!parsed.success) { + throw new ParallelError( + `Invalid run of ${this.identifier}: default metadata invalid`, + { + cause: parsed.error, + } + ) + } + if (parsed.data.task_id !== this.identifier) { + throw new ParallelError( + `Invalid run of ${this.identifier}: invalid task_id`, + { + cause: parsed.data.task_id, + } + ) + } + try { + return { + ...this.metadata.parse(input.metadata), + task_id: parsed.data.task_id as TIdentifier, + attempt: parsed.data.attempt, + webhook_url: parsed.data.webhook_url as string, + } + } catch (error) { + throw new ParallelError( + `Invalid run of ${this.identifier}: invalid metadata`, + { + cause: error, + } + ) + } + } + + /** + * Start a task run with the provided input and metadata + * @param parameters - The input and metadata for the task run + * @param parallel - The parallel client to use + * @returns The created task run + */ + @WrapErrorsIn(ParallelError) + public async startRun( + parameters: StartRunOptions, + parallel: IParallel + ): Promise> { + let taskRun: UnwrapPromise + if (parameters.webhook) { + taskRun = await parallel.createTaskRunWithWebhook({ + body: { + processor: parameters.processor ?? this.defaultProcessor, + metadata: { + ...parameters.metadata, + task_id: this.identifier, + attempt: parameters.metadata.attempt ?? 1, + webhook_url: parameters.webhook.toString(), + }, + input: parameters.input, + task_spec: { + output_schema: this.output.jsonSchema, + input_schema: this.input.jsonSchema, + }, + webhook: { url: parameters.webhook.toString() }, + }, + }) + } else { + taskRun = await parallel.createTaskRun({ + body: { + processor: parameters.processor ?? this.defaultProcessor, + metadata: { + ...parameters.metadata, + task_id: this.identifier, + attempt: 1, + }, + input: parameters.input, + task_spec: { + output_schema: this.output.jsonSchema, + input_schema: this.input.jsonSchema, + }, + }, + }) + } + if (taskRun.data) { + return { + ...taskRun.data, + metadata: this.parseMetadata(taskRun.data), + } + } else { + throw new ParallelError( + `Failed to create task run for ${this.identifier}`, + { + cause: taskRun.error, + } + ) + } + } + + /** + * Get a task run by its ID + * @param runId - The ID of the task run + * @param parallel - The parallel client to use + * @returns The task run with the provided ID + */ + @WrapErrorsIn(ParallelError) + public async getRun( + runId: string, + parallel: IParallel + ): Promise> { + const taskRun = await parallel.getTaskRun({ path: { run_id: runId } }) + if (taskRun.data) { + return { ...taskRun.data, metadata: this.parseMetadata(taskRun.data) } + } + throw new ParallelError(`Failed to get task run for ${this.identifier}`, { + cause: taskRun.error, + }) + } + + /** + * Get the input of a task run by its ID + * @param runId - The ID of the task run + * @param parallel - The parallel client to use + * @returns The input of the task run with the provided ID + */ + @WrapErrorsIn(ParallelError) + public async getRunInput( + runId: string, + parallel: IParallel + ): Promise { + const taskRunInput = await parallel.getTaskRunInput({ + path: { run_id: runId }, + }) + if (taskRunInput.data) { + this.parseMetadata(taskRunInput.data) + return this.input.parse(taskRunInput.data.input) + } + throw new ParallelError( + `Failed to get task run input for ${this.identifier}`, + { + cause: taskRunInput.error, + } + ) + } + + /** + * Get the output of a task run by its ID + * @param runId - The ID of the task run + * @param parallel - The parallel client to use + * @returns The output of the task run with the provided ID + */ + @WrapErrorsIn(ParallelError) + public async getRunOutput( + runId: string, + parallel: IParallel + ): Promise { + const taskRunResult = await parallel.getTaskRunResult({ + path: { run_id: runId }, + }) + if (taskRunResult.data) { + this.parseMetadata(taskRunResult.data.run) + if (taskRunResult.data.output.type !== 'json') { + throw new ParallelError(`Invalid output type for ${this.identifier}`, { + cause: taskRunResult.data.output, + }) + } + return this.output.parse(taskRunResult.data.output.content) + } + throw new ParallelError( + `Failed to get task run result for ${this.identifier}`, + { + cause: taskRunResult.error, + } + ) + } + + /** + * Poll for the output of a task run by its ID until it is completed, fails, or times out + * @param runId - The ID of the task run + * @param parallel - The parallel client to use + * @param pollingOptions - The options for polling + * @throws - A `ParallelError` if the task run fails or times out + * @returns The output of the task run with the provided ID + */ + @WrapErrorsIn(ParallelError) + public async pollForRunOutput( + runId: string, + parallel: IParallel, + pollingOptions?: PollingOptions + ): Promise { + const taskRun = await this.getRun(runId, parallel) + + const MINUTE_IN_MS = 60 * 1_000 + const defaultTimeoutMs: Record = { + [ParallelProcessor.Lite]: MINUTE_IN_MS, + [ParallelProcessor.Base]: MINUTE_IN_MS * 2, + [ParallelProcessor.Core]: MINUTE_IN_MS * 4, + [ParallelProcessor.Pro]: MINUTE_IN_MS * 8, + [ParallelProcessor.Ultra]: MINUTE_IN_MS * 16, + } + const { timeoutMs, intervalMs, backoffExponent } = { + timeoutMs: defaultTimeoutMs[taskRun.processor as ParallelProcessor], + intervalMs: 2_000, + backoffExponent: 1.5, + ...pollingOptions, + } + this.logger?.info('Parallel: Polling for task run output', { + task_id: this.identifier, + runId, + processor: taskRun.processor, + timeoutMs, + intervalMs, + backoffExponent, + }) + const startTime = Date.now() + let backoff = 1 + while (Date.now() - startTime < timeoutMs) { + const taskRun = await this.getRun(runId, parallel) + this.parseMetadata(taskRun) + if (taskRun.status === 'completed') { + return await this.getRunOutput(runId, parallel) + } else if (taskRun.status === 'failed') { + throw new ParallelError(`Task run failed for ${this.identifier}`, { + cause: taskRun, + }) + } + backoff = backoff * backoffExponent + await new Promise((resolve) => setTimeout(resolve, intervalMs * backoff)) + } + throw new ParallelError(`Task run check failed with this timeout`, { + cause: taskRun, + }) + } +} diff --git a/api/sdk-task-webhook.ts b/api/sdk-task-webhook.ts new file mode 100644 index 0000000..31b9421 --- /dev/null +++ b/api/sdk-task-webhook.ts @@ -0,0 +1,257 @@ +import z, { ZodSchema } from 'zod' + +import { zodToJsonSchema } from 'zod-to-json-schema' +import { ParallelError, WrapErrorsIn } from './sdk-errors' +import { Logger } from './sdk-logging' +import { JsonSchema, TaskRun } from './types.gen' +import { + CreateTaskRunResult, + IParallel, + ParallelProcessor, +} from './sdk-parallel' +import { ParallelTaskRunStatusWebhookEvent } from './sdk-webhook' +import { + taskRunMetadataZodSchema, + taskRunStatusWebhookEventZodSchema, +} from './sdk-schemas' +import { ParallelTaskDefinition, TaskRunForTask } from './sdk-task-definition' + +export interface ParallelTaskWebhookOptions< + TIdentifier extends string, + TInput extends { [key: string]: unknown }, + TOutput extends { [key: string]: unknown }, + TMetadata extends { [key: string]: string | number | boolean }, +> { + /** + * Represents a task to be executed in parallel within a broader system or workflow. + * + * @type {ParallelTaskDefinition} + * @template TIdentifier - The type representing the unique identifier for the task. + * @template TInput - The type representing the input data structure required for the task. + * @template TOutput - The type representing the output data structure produced by the task. + * @template TMetadata - The type representing any additional metadata associated with the task. + */ + task: ParallelTaskDefinition + /** + * The output schema of the task + */ + onSuccess?: ( + output: TOutput, + taskRun: TaskRunForTask + ) => Promise | void + /** + * The number of times to retry the task run if it fails + * @type {number} - The number of times to retry the task run if it fails + * @type {Record} - If provided, the number of times to retry the task run if it fails for each processor, once all retires for one processor are exhausted, the next most capable processor will be used + * @default - 0 - no retries will be attempted + */ + retries?: number | Partial<{ [key in ParallelProcessor]: number }> + /** + * A handler to call when the task run status is `success` in a webhook but retries should be attempted conditionally based on the output + * @param output - The output of the task run + * @returns A boolean indicating whether retries should be attempted + * @default - no retries will be attempted + */ + outputIsFailure?: ( + output: TOutput, + taskRun: TaskRunForTask + ) => Promise | boolean + /** + * A handler to call when the task run status is `failed` in a webhook + * @param taskRun - The task run + * @default - no-operation will be performed when webhook events are received + */ + onFailure?: ( + taskRun: TaskRunForTask + ) => Promise | void + /** + * The logger to use for the task definition + * @default - no logging will be performed + */ + logger?: Logger +} + +export class ParallelTaskWebhook< + TIdentifier extends string, + TInput extends { [key: string]: unknown }, + TOutput extends { [key: string]: unknown }, + TMetadata extends { [key: string]: string | number | boolean }, +> { + /** + * The task this webhook configuration is for + */ + public task: ParallelTaskDefinition + private readonly onSuccess?: ( + output: TOutput, + taskRun: TaskRunForTask + ) => Promise | void + private readonly retries?: + | number + | Partial<{ [key in ParallelProcessor]: number }> + private readonly outputIsFailure?: ( + output: TOutput, + taskRun: TaskRunForTask + ) => Promise | boolean + private readonly onFailure?: ( + taskRun: TaskRunForTask + ) => Promise | void + /** + * The logger to use for the task definition + * @default - no logging will be performed + */ + private logger?: Logger + + constructor( + definition: ParallelTaskWebhookOptions< + TIdentifier, + TInput, + TOutput, + TMetadata + > + ) { + this.task = definition.task + this.onSuccess = definition.onSuccess + this.retries = definition.retries + this.outputIsFailure = definition.outputIsFailure + this.onFailure = definition.onFailure + this.logger = definition.logger + } + + private getNextProcessor( + attempt: number, + currentProcessor: ParallelProcessor, + retries: number | Partial<{ [key in ParallelProcessor]: number }> + ): ParallelProcessor | null { + if (typeof retries === 'number') return currentProcessor + const allowedRetriesOnCurrentProcessor = retries[currentProcessor]! + if (attempt < allowedRetriesOnCurrentProcessor) { + return currentProcessor + } + const processorsOrderedByCapability = [ + ParallelProcessor.Lite, + ParallelProcessor.Base, + ParallelProcessor.Core, + ParallelProcessor.Pro, + ParallelProcessor.Ultra, + ] + const currentProcessorIndex = + processorsOrderedByCapability.indexOf(currentProcessor) + const nextProcessor = processorsOrderedByCapability.find( + (processor, index) => + retries[processor] !== undefined && + retries[processor] > 0 && + index > currentProcessorIndex + ) + return nextProcessor! + } + + private async shouldRetryWebhookTaskRun( + taskRun: TaskRunForTask + ): Promise { + if (!this.retries || !taskRun.metadata.attempt) return false + if (typeof this.retries === 'number') { + return taskRun.metadata.attempt < this.retries + } + const currentProcessor = z + .nativeEnum(ParallelProcessor) + .parse(taskRun.processor) + const nextProcessor = this.getNextProcessor( + taskRun.metadata.attempt, + currentProcessor, + this.retries + ) + if (!nextProcessor) return false + const newProcessorOnThisAttempt = nextProcessor !== currentProcessor + if (newProcessorOnThisAttempt) return true + const allowedRetriesOnCurrentProcessor = this.retries[currentProcessor] ?? 0 + return taskRun.metadata.attempt <= allowedRetriesOnCurrentProcessor + } + + private async retryWebhookTaskRun( + taskRun: TaskRunForTask, + parallel: IParallel + ): Promise { + this.logger?.info('Parallel: Retrying task run', { + task_id: this.task.identifier, + run_id: taskRun.run_id, + }) + const input = await this.task.getRunInput(taskRun.run_id, parallel) + const currentProcessor = z + .nativeEnum(ParallelProcessor) + .parse(taskRun.processor) + const nextProcessor = this.getNextProcessor( + taskRun.metadata.attempt!, + currentProcessor, + this.retries! + ) + if (!nextProcessor) return + const newProcessorOnThisAttempt = nextProcessor !== currentProcessor + await this.task.startRun( + { + input, + processor: nextProcessor, + webhook: new URL(taskRun.metadata.webhook_url!), + metadata: { + ...taskRun.metadata, + attempt: newProcessorOnThisAttempt + ? 1 + : taskRun.metadata.attempt! + 1, + }, + }, + parallel + ) + } + + /** + * Handle a webhook event for a task run, unwraps the event, validates the metadata, and calls the task run status success webhook handler + * @param event - The webhook event + * @param parallel - The parallel client to use + */ + @WrapErrorsIn(ParallelError) + public async webhook( + event: ParallelTaskRunStatusWebhookEvent, + parallel: IParallel + ) { + const parsed = taskRunStatusWebhookEventZodSchema.safeParse(event) + if (!parsed.success) { + throw new ParallelError(`Invalid task run status webhook event`, { + cause: parsed.error, + }) + } + if (parsed.data.data.metadata.task_id !== this.task.identifier) return + this.logger?.info('Parallel: Compatible webhook event received by task', { + task_id: this.task.identifier, + event: parsed.data, + }) + const runId = parsed.data.data.run_id + if (event.type === 'task_run.status') { + const taskRun = await this.task.getRun(runId, parallel) + if (event.data.status === 'completed') { + const output = await this.task.getRunOutput(runId, parallel) + if ( + this.outputIsFailure && + (await this.outputIsFailure(output, taskRun)) + ) { + if (await this.shouldRetryWebhookTaskRun(taskRun)) { + await this.retryWebhookTaskRun(taskRun, parallel) + } else { + await this.onFailure?.(taskRun) + } + } else if (this.onSuccess) { + this.logger?.info('Parallel: Webhook event successful for task', { + task_id: this.task.identifier, + run_id: taskRun.run_id, + output, + }) + await this.onSuccess(output, taskRun) + } + } else if (event.data.status === 'failed') { + if (await this.shouldRetryWebhookTaskRun(taskRun)) { + await this.retryWebhookTaskRun(taskRun, parallel) + } else { + await this.onFailure?.(taskRun) + } + } + } + } +} diff --git a/api/sdk-webhook.ts b/api/sdk-webhook.ts new file mode 100644 index 0000000..612f91f --- /dev/null +++ b/api/sdk-webhook.ts @@ -0,0 +1,187 @@ +import { ParallelError, WrapErrorsIn } from './sdk-errors' +import crypto from 'crypto' +import { normalizeBase64Padding } from './base-encoding' +import { Logger } from './sdk-logging' +import { TaskRun } from './types.gen' +import { IParallel } from './sdk-parallel' +import { ParallelTaskDefinition } from './sdk-task-definition' +import { + taskRunMetadataZodSchema, + taskRunStatusWebhookEventZodSchema, +} from './sdk-schemas' +import { ParallelTaskWebhook } from './sdk-task-webhook' + +export interface ParallelTaskRunStatusWebhookEvent { + timestamp: string + type: 'task_run.status' + data: TaskRun +} + +export type ParallelWebhookEvent = ParallelTaskRunStatusWebhookEvent + +/** + * The parameters for verifying the webhook secret + */ +export interface VerifyWebhookSecretParameters { + /** + * The ID of the webhook, from the `webhook-id` header + */ + webhookId: string + /** + * The timestamp of the webhook, from the `webhook-timestamp` header + */ + webhookTimestamp: string + /** + * The signature of the webhook, from the `webhook-signature` header + */ + webhookSignature: string + /** + * The received JSON payload of the webhook + */ + payload: string +} + +export interface ParallelWebhookHandlerOptions { + /** + * The task definitions to handle webhook events for + */ + taskWebhooks: ParallelTaskWebhook[] + /** + * The logger to use for the webhook handler + * @default - no logging will be performed + */ + logger?: Logger +} + +/** + * A handler for webhook events for a set of task definitions. + * @param taskDefinitions - The task definitions to handle webhook events for + */ +export class ParallelWebhookHandler { + private readonly taskWebhooks: Map< + string, + ParallelTaskWebhook< + string, + Record, + Record, + Record + > + > + private readonly logger?: Logger + + constructor(options: ParallelWebhookHandlerOptions) { + this.logger = options.logger + this.taskWebhooks = new Map( + options.taskWebhooks.map((taskDefinition) => [ + taskDefinition.task.identifier, + taskDefinition, + ]) + ) + if (this.taskWebhooks.size !== options.taskWebhooks.length) { + throw new ParallelError( + 'Duplicate task definitions provided to webhook handler' + ) + } + } + + /** + * Verify the secret of a webhook event + * @param parameters - The required parameters for verifying the webhook secret + * @param parallel - The parallel client to use + * @returns `true` if the webhook secret is valid + * @throws - A `ParallelError` if the webhook secret is invalid + */ + @WrapErrorsIn(ParallelError) + public async verifyWebhookSecret( + parameters: VerifyWebhookSecretParameters, + parallel: IParallel + ): Promise { + const secret = parallel.webhookSecret + if (!secret) return true + try { + const [_version, receivedSignature] = + parameters.webhookSignature.split(',') + const toSign = `${parameters.webhookId}.${parameters.webhookTimestamp}.${parameters.payload}` + const hmac = crypto.createHmac('sha256', secret) + hmac.update(toSign) + const expectedSignature = hmac.digest('base64') + const valid = crypto.timingSafeEqual( + Buffer.from(expectedSignature), + Buffer.from(normalizeBase64Padding(receivedSignature)) + ) + if (!valid) { + this.logger?.warn('Parallel: Invalid webhook secret received', { + cause: parameters, + }) + return false + } + return true + } catch (error) { + this.logger?.warn('Parallel: Invalid webhook secret received', { + cause: error, + }) + return false + } + } + + /** + * Parse a webhook event into a strongly-typed webhook event + * @param input - The webhook event to parse + * @returns The parsed webhook event + */ + @WrapErrorsIn(ParallelError) + public parseWebhookEvent(input: unknown): ParallelWebhookEvent { + if (typeof input === 'string') { + try { + input = JSON.parse(input) + } catch { + throw new ParallelError(`Invalid task run status webhook event`, { + cause: input, + }) + } + } + const parsed = taskRunStatusWebhookEventZodSchema.safeParse(input) + if (!parsed.success) { + throw new ParallelError(`Invalid task run status webhook event`, { + cause: parsed.error, + }) + } + this.logger?.info('Parallel: Successfully parsed webhook event', { + event: parsed.data, + }) + return parsed.data + } + + /** + * Handle a webhook event. Finds the task definition for the task run and calls the webhook handler. + * @param event - The webhook event + * @param parallel - The parallel client to use + */ + @WrapErrorsIn(ParallelError) + public async handleTaskWebhook( + event: ParallelWebhookEvent, + parallel: IParallel + ): Promise { + const parsedMetadata = taskRunMetadataZodSchema.safeParse( + event.data.metadata + ) + if (!parsedMetadata.success) { + throw new ParallelError(`Invalid task run status webhook event`, { + cause: parsedMetadata.error, + }) + } + const taskWebhook = this.taskWebhooks.get(parsedMetadata.data.task_id) + if (!taskWebhook) { + this.logger?.warn('Parallel: Missing task definition for task', { + task_id: parsedMetadata.data.task_id, + event, + }) + return + } + await taskWebhook.webhook(event, parallel) + this.logger?.info('Parallel: Successfully handled webhook event for task', { + task_id: parsedMetadata.data.task_id, + event, + }) + } +} diff --git a/api/sdk.gen.ts b/api/sdk.gen.ts new file mode 100644 index 0000000..0b1c808 --- /dev/null +++ b/api/sdk.gen.ts @@ -0,0 +1,142 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Options as ClientOptions, TDataShape, Client } from './client' +import type { + TasksRunsPostV1TasksRunsPostData, + TasksRunsPostV1TasksRunsPostResponses, + TasksRunsPostV1TasksRunsPostErrors, + TasksRunsGetV1TasksRunsRunIdGetData, + TasksRunsGetV1TasksRunsRunIdGetResponses, + TasksRunsGetV1TasksRunsRunIdGetErrors, + TasksRunsInputGetV1TasksRunsRunIdInputGetData, + TasksRunsInputGetV1TasksRunsRunIdInputGetResponses, + TasksRunsInputGetV1TasksRunsRunIdInputGetErrors, + TasksRunsResultGetV1TasksRunsRunIdResultGetData, + TasksRunsResultGetV1TasksRunsRunIdResultGetResponses, + TasksRunsResultGetV1TasksRunsRunIdResultGetErrors, +} from './types.gen' +import { client as _heyApiClient } from './client.gen' + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, +> = ClientOptions & { + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record +} + +/** + * Tasks Runs Post + * Initiates a single task run. + */ +export const tasksRunsPostV1TasksRunsPost = < + ThrowOnError extends boolean = false, +>( + options: Options +) => { + return (options.client ?? _heyApiClient).post< + TasksRunsPostV1TasksRunsPostResponses, + TasksRunsPostV1TasksRunsPostErrors, + ThrowOnError + >({ + security: [ + { + name: 'x-api-key', + type: 'apiKey', + }, + ], + url: '/v1/tasks/runs', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers, + }, + }) +} + +/** + * Tasks Runs Get + * Retrieves a run by run_id. + */ +export const tasksRunsGetV1TasksRunsRunIdGet = < + ThrowOnError extends boolean = false, +>( + options: Options +) => { + return (options.client ?? _heyApiClient).get< + TasksRunsGetV1TasksRunsRunIdGetResponses, + TasksRunsGetV1TasksRunsRunIdGetErrors, + ThrowOnError + >({ + security: [ + { + name: 'x-api-key', + type: 'apiKey', + }, + ], + url: '/v1/tasks/runs/{run_id}', + ...options, + }) +} + +/** + * Tasks Runs Input Get + * Retrieves the input of a run by run_id. + */ +export const tasksRunsInputGetV1TasksRunsRunIdInputGet = < + ThrowOnError extends boolean = false, +>( + options: Options +) => { + return (options.client ?? _heyApiClient).get< + TasksRunsInputGetV1TasksRunsRunIdInputGetResponses, + TasksRunsInputGetV1TasksRunsRunIdInputGetErrors, + ThrowOnError + >({ + security: [ + { + name: 'x-api-key', + type: 'apiKey', + }, + ], + url: '/v1/tasks/runs/{run_id}/input', + ...options, + }) +} + +/** + * Tasks Runs Result Get + * Retrieves a run by run_id, blocking until the run is completed. + */ +export const tasksRunsResultGetV1TasksRunsRunIdResultGet = < + ThrowOnError extends boolean = false, +>( + options: Options< + TasksRunsResultGetV1TasksRunsRunIdResultGetData, + ThrowOnError + > +) => { + return (options.client ?? _heyApiClient).get< + TasksRunsResultGetV1TasksRunsRunIdResultGetResponses, + TasksRunsResultGetV1TasksRunsRunIdResultGetErrors, + ThrowOnError + >({ + security: [ + { + name: 'x-api-key', + type: 'apiKey', + }, + ], + url: '/v1/tasks/runs/{run_id}/result', + ...options, + }) +} diff --git a/api/sdk.ts b/api/sdk.ts new file mode 100644 index 0000000..5b91b80 --- /dev/null +++ b/api/sdk.ts @@ -0,0 +1,8 @@ +export * from './sdk-parallel' +export * from './sdk-task-definition' +export * from './sdk-task-webhook' +export * from './sdk-webhook' +export * from './sdk-logging' +export * from './sdk-mocks' +export { ParallelError } from './sdk-errors' +export { TaskRunInputOutput, TaskRunResult, TaskRun } from './types.gen' diff --git a/api/test/base-encoding.spec.ts b/api/test/base-encoding.spec.ts new file mode 100644 index 0000000..ebbbe24 --- /dev/null +++ b/api/test/base-encoding.spec.ts @@ -0,0 +1,22 @@ +import { normalizeBase64Padding } from '../base-encoding' +import { ParallelError } from '../sdk-errors' +import { describe, expect, it } from 'vitest' + +describe(normalizeBase64Padding.name, () => { + it('should normalize base64 padding', () => { + expect(normalizeBase64Padding('test')).toBe('test') + expect(normalizeBase64Padding('test==')).toBe('test====') + expect(normalizeBase64Padding('test===')).toBe('test====') + }) + + it('should throw an error if the input is not a valid base64 string', () => { + expect(() => normalizeBase64Padding('t')).toThrow(ParallelError) + }) + + it('should be url safe', () => { + expect(normalizeBase64Padding('testing-')).toBe('testing+') + expect(normalizeBase64Padding('testing_')).toBe('testing/') + expect(normalizeBase64Padding('testing+')).toBe('testing+') + expect(normalizeBase64Padding('testing/')).toBe('testing/') + }) +}) diff --git a/api/test/sdk-errors.spec.ts b/api/test/sdk-errors.spec.ts new file mode 100644 index 0000000..f6e54c0 --- /dev/null +++ b/api/test/sdk-errors.spec.ts @@ -0,0 +1,74 @@ +import { describe, it, expect } from 'vitest' +import { mock } from 'vitest-mock-extended' +import { ParallelError, WrapErrorsIn } from '../sdk-errors' +import { Logger } from '../sdk-logging' + +const mockLogger = mock() + +describe(WrapErrorsIn.name, () => { + it('should wrap errors in the error constructor provided', () => { + // arrange + class TestService { + logger = mockLogger + + @WrapErrorsIn(ParallelError) + throwsParallelError() { + throw new Error('test parallel error') + } + + @WrapErrorsIn(ParallelError) + throwsError() { + throw 'test error' + } + + @WrapErrorsIn(ParallelError) + resolves() { + return 'test success' + } + } + + // act + const managedParallelErrorResult = () => + new TestService().throwsParallelError() + const managedErrorResult = () => new TestService().throwsError() + const result = new TestService().resolves() + + // assert + expect(managedParallelErrorResult).toThrow(ParallelError) + expect(managedErrorResult).toThrow(ParallelError) + expect(result).toBe('test success') + }) + + it('should wrap errors in promises', async () => { + // arrange + class TestService { + logger = mockLogger + + @WrapErrorsIn(ParallelError) + async throwsParallelError() { + return Promise.reject(new Error('test parallel error')) + } + + @WrapErrorsIn(ParallelError) + async throwsError() { + throw 'test error' + } + + @WrapErrorsIn(ParallelError) + async resolves() { + return Promise.resolve('test success') + } + } + + // act + const managedParallelErrorResult = () => + new TestService().throwsParallelError() + const managedErrorResult = () => new TestService().throwsError() + const result = await new TestService().resolves() + + // assert + await expect(managedParallelErrorResult).rejects.toThrow(ParallelError) + await expect(managedErrorResult).rejects.toThrow(ParallelError) + expect(result).toBe('test success') + }) +}) diff --git a/api/test/sdk-mocks.spec.ts b/api/test/sdk-mocks.spec.ts new file mode 100644 index 0000000..d303c0a --- /dev/null +++ b/api/test/sdk-mocks.spec.ts @@ -0,0 +1,221 @@ +import { describe, it, expect, vi } from 'vitest' +import { MockParallelForTaskDefinition } from '../sdk-mocks' +import { ParallelWebhookHandler } from '../sdk-webhook' +import { z } from 'zod' +import { + ParallelTaskDefinition, + ZodParallelTaskJsonIO, + ZodParallelTaskMetadata, +} from '../sdk-task-definition' +import { ParallelProcessor } from '../sdk-parallel' +import { ParallelTaskWebhook } from '../sdk-task-webhook' + +describe(MockParallelForTaskDefinition.name, () => { + const webhookHandlerFn = vi.fn() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input: new ZodParallelTaskJsonIO({ + description: 'brand-url', + schema: z.object({ url: z.string() }), + }), + output: new ZodParallelTaskJsonIO({ + description: 'brand-logo', + schema: z.object({ logoUrl: z.string() }), + }), + defaultProcessor: ParallelProcessor.Base, + metadata: new ZodParallelTaskMetadata({ + schema: z.object({ foo: z.string() }), + }), + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + onSuccess: webhookHandlerFn, + }) + + it('should allow you to update the handler', () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 0, + }) + mockParallel.updateWebhookHandler( + new ParallelWebhookHandler({ taskWebhooks: [taskWebhook] }) + ) + }) + + it('should return a task run not found error if the task run does not exist', async () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 0, + }) + + // act + const result = await mockParallel.getTaskRun({ path: { run_id: '123' } }) + + // assert + expect(result).toEqual( + expect.objectContaining({ + data: undefined, + error: { + error: { + message: 'Task run not found', + ref_id: expect.any(String), + }, + }, + request: expect.any(Request), + response: expect.any(Response), + }) + ) + }) + + it('should trigger the webhook handler if provided and requested', async () => { + // arrange + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], + }) + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 20, + webhookHandler, + }) + + // act + const taskRun = await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + webhook: new URL('https://example.com/webhook'), + }, + mockParallel + ) + await taskDefinition.pollForRunOutput(taskRun.run_id, mockParallel, { + backoffExponent: 1, + intervalMs: 10, + timeoutMs: 100, + }) + + // assert + expect(webhookHandlerFn).toHaveBeenCalledWith( + { + logoUrl: 'https://example.com/test', + }, + { + created_at: expect.any(String), + is_active: true, + metadata: { + attempt: 1, + foo: 'bar', + task_id: 'get-brand-logo', + webhook_url: 'https://example.com/webhook', + }, + modified_at: expect.any(String), + processor: 'base', + run_id: expect.any(String), + status: 'completed', + } + ) + }) + + it('should enable fetching the task run input', async () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 0, + }) + + // act + const taskRun = await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + const result = await mockParallel.getTaskRunInput({ + path: { run_id: taskRun.run_id }, + }) + + // assert + expect(result).toEqual( + expect.objectContaining({ + data: { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar', attempt: 1, task_id: 'get-brand-logo' }, + task_spec: { + output_schema: taskDefinition.output, + input_schema: taskDefinition.input, + }, + }, + error: undefined, + request: expect.any(Request), + response: expect.any(Response), + }) + ) + }) + + it('should return error if the task run input is not found', async () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 0, + }) + + // act + const result = await mockParallel.getTaskRunInput({ + path: { run_id: '123' }, + }) + + // assert + expect(result).toEqual( + expect.objectContaining({ + data: undefined, + error: expect.objectContaining({ + error: expect.objectContaining({ + message: 'Task run input not found', + ref_id: expect.any(String), + }), + }), + request: expect.any(Request), + response: expect.any(Response), + }) + ) + }) + + it('should return error if the task run result is not found', async () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 0, + }) + + // act + const result = await mockParallel.getTaskRunResult({ + path: { run_id: '123' }, + }) + + // assert + expect(result).toEqual( + expect.objectContaining({ + data: undefined, + error: expect.objectContaining({ + error: expect.objectContaining({ + message: 'Task run result not found', + ref_id: expect.any(String), + }), + }), + request: expect.any(Request), + response: expect.any(Response), + }) + ) + }) +}) diff --git a/api/test/sdk-parallel.spec.ts b/api/test/sdk-parallel.spec.ts new file mode 100644 index 0000000..7f36a89 --- /dev/null +++ b/api/test/sdk-parallel.spec.ts @@ -0,0 +1,119 @@ +import { + describe, + it, + expect, + beforeAll, + afterEach, + afterAll, + vi, +} from 'vitest' +import { mock } from 'vitest-mock-extended' +import { setupServer } from 'msw/node' +import { http, HttpResponse } from 'msw' +import { client } from '../client.gen' +import { Logger } from '../sdk-logging' +import { Parallel } from '../sdk-parallel' + +const mockLogger = mock() + +describe(Parallel.name, () => { + const server = setupServer() + + beforeAll(() => { + server.listen() + }) + + afterEach(() => { + server.resetHandlers() + }) + + afterAll(() => { + server.close() + }) + + it('should set a default base url if none is provided', () => { + // arrange + new Parallel({ apiKey: 'test-api-key' }) + + // assert + expect(client.getConfig().baseUrl).toBe('https://api.parallel.ai') + }) + + it('should set the api key, base url, and webhook secret', async () => { + const webhookUrl = 'http://localhost:3010/webhook' + const serverHandler = vi.fn() + const webhookHandler = http.post( + 'http://localhost:3000/v1beta/tasks/runs', + async ({ request }) => { + const body = await request.json() + serverHandler() + expect(body).toBeDefined() + expect((body as any).webhook.url).toBe(webhookUrl) + expect(request.headers.get('x-api-key')).toBe('test-api-key') + return HttpResponse.json({}) + } + ) + server.use(webhookHandler) + const parallel = new Parallel({ + apiKey: 'test-api-key', + baseUrl: 'http://localhost:3000', + webhookSecret: 'test-webhook-secret', + logger: mockLogger, + }) + + // act + await parallel.createTaskRunWithWebhook({ + body: { + processor: 'lite', + webhook: { url: webhookUrl }, + input: { test: 'test' }, + }, + }) + + // assert + expect(serverHandler).toHaveBeenCalled() + }) + + it('should handle each call to the api', async () => { + // arrange + const serverHandler = vi.fn() + const handlers = [ + http.post('http://localhost:3000/v1/tasks/runs', () => { + serverHandler() + return HttpResponse.json({}) + }), + http.get('http://localhost:3000/v1/tasks/runs/123', () => { + serverHandler() + return HttpResponse.json({}) + }), + http.get('http://localhost:3000/v1/tasks/runs/123/input', () => { + serverHandler() + return HttpResponse.json({}) + }), + http.get('http://localhost:3000/v1/tasks/runs/123/result', () => { + serverHandler() + return HttpResponse.json({}) + }), + ] as const + server.use(...handlers) + const parallel = new Parallel({ + apiKey: 'test-api-key', + baseUrl: 'http://localhost:3000', + logger: mockLogger, + }) + + // act + await parallel.createTaskRun({ + body: { + processor: 'lite', + input: { test: 'test' }, + }, + }) + await parallel.getTaskRun({ path: { run_id: '123' } }) + await parallel.getTaskRunInput({ path: { run_id: '123' } }) + await parallel.getTaskRunResult({ path: { run_id: '123' } }) + + // assert + expect(serverHandler).toHaveBeenCalledTimes(4) + }) +}) diff --git a/api/test/sdk-task-definition.spec.ts b/api/test/sdk-task-definition.spec.ts new file mode 100644 index 0000000..acf3132 --- /dev/null +++ b/api/test/sdk-task-definition.spec.ts @@ -0,0 +1,624 @@ +import { describe, it, expect } from 'vitest' +import { mock } from 'vitest-mock-extended' +import { MockParallelForTaskDefinition, ParallelError } from '../sdk' +import { z } from 'zod' +import { Logger } from '../sdk-logging' +import { IParallel, ParallelProcessor } from '../sdk-parallel' +import { + ParallelTaskDefinition, + ZodParallelTaskJsonIO, + ZodParallelTaskMetadata, +} from '../sdk-task-definition' + +const mockLogger = mock() + +describe(ZodParallelTaskJsonIO.name, () => { + it('should create a json schema from a zod schema', () => { + // arrange + const zodSchema = z.object({ + name: z.string(), + age: z.number(), + }) + + // act + const parallelTaskJsonIO = new ZodParallelTaskJsonIO({ + description: 'test', + schema: zodSchema, + }) + + // assert + expect(parallelTaskJsonIO.jsonSchema).toBeDefined() + expect(parallelTaskJsonIO.jsonSchema.type).toBe('json') + expect(parallelTaskJsonIO.jsonSchema.json_schema).toEqual({ + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: false, + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'number' }, + }, + required: ['name', 'age'], + }) + }) + + it('should provide a parse function that parses the input provided to it', () => { + // arrange + const zodSchema = z.object({ + name: z.string(), + age: z.number(), + }) + const parallelTaskJsonIO = new ZodParallelTaskJsonIO({ + description: 'test', + schema: zodSchema, + }) + + // act + const successResult = parallelTaskJsonIO.parse({ name: 'John', age: 30 }) + const managedFailureResult = () => + parallelTaskJsonIO.parse({ name: 'John' }) + + // assert + expect(successResult).toEqual({ name: 'John', age: 30 }) + expect(managedFailureResult).toThrow(ParallelError) + }) +}) + +describe(ZodParallelTaskMetadata.name, () => { + it('should create a parse function that parses the input provided to it', () => { + // arrange + const zodSchema = z.object({ + name: z.string(), + age: z.number(), + }) + const parallelTaskMetadata = new ZodParallelTaskMetadata({ + schema: zodSchema, + }) + + // act + const successResult = parallelTaskMetadata.parse({ name: 'John', age: 30 }) + const managedFailureResult = () => + parallelTaskMetadata.parse({ name: 'John' }) + + // assert + expect(successResult).toEqual({ name: 'John', age: 30 }) + expect(managedFailureResult).toThrow(ParallelError) + }) +}) + +describe(ParallelTaskDefinition.name, () => { + const input = new ZodParallelTaskJsonIO({ + description: 'url', + schema: z.object({ url: z.string() }), + }) + const output = new ZodParallelTaskJsonIO({ + description: 'logo', + schema: z.object({ logoUrl: z.string() }), + }) + const metadata = new ZodParallelTaskMetadata({ + schema: z.object({ foo: z.string() }), + }) + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + + it('should provide shape data publicly', () => { + // assert + expect(taskDefinition.identifier).toBe('get-brand-logo') + expect(taskDefinition.input).toEqual(input) + expect(taskDefinition.output).toEqual(output) + }) + + describe('startRun', () => { + it('starting a run should call right endpoint based on if webhook provided', async () => { + // arrange + const mockParallel = mock() + mockParallel.createTaskRunWithWebhook.mockResolvedValue({ + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + } as any) + mockParallel.createTaskRun.mockResolvedValue({ + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + } as any) + + // act + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + webhook: new URL('https://example.com'), + metadata: { foo: 'bar' }, + }, + mockParallel + ) + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + + // assert + expect(mockParallel.createTaskRunWithWebhook).toHaveBeenCalledOnce() + expect(mockParallel.createTaskRun).toHaveBeenCalledOnce() + }) + + it('should apply the task_id and attempt to the metadata', async () => { + // arrange + const mockParallel = mock() + mockParallel.createTaskRun.mockResolvedValue({ + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + } as any) + + // act + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + + // assert + expect(mockParallel.createTaskRun).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + metadata: { + task_id: 'get-brand-logo', + attempt: 1, + foo: 'bar', + }, + }), + }) + ) + }) + + it('should throw a ParallelError if data is not returned', async () => { + // arrange + const mockParallel = mock() + mockParallel.createTaskRun.mockResolvedValue({ + data: undefined, + } as any) + + // act + const managedFailureResult = () => + taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + + it('should throw a ParallelError if an error is thrown', async () => { + // arrange + const mockParallel = mock() + mockParallel.createTaskRunWithWebhook.mockRejectedValue(new Error('test')) + + // act + const managedFailureResult = () => + taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + }) + + describe('getRun', () => { + it('should validate the run metadata', async () => { + // arrange + const mockParallel = mock() + + // error if metadata is missing + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: { run_id: '123' }, + } as any) + const noMetadata = () => taskDefinition.getRun('123', mockParallel) + await expect(noMetadata).rejects.toThrow(ParallelError) + + // error if metadata is not an object + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: { run_id: '123', metadata: 9 }, + } as any) + const notAnObject = () => taskDefinition.getRun('123', mockParallel) + await expect(notAnObject).rejects.toThrow(ParallelError) + + // error if metadata is invalid + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: { run_id: '123', metadata: { foo: 'bar' } }, + } as any) + const invalidMetadata = () => taskDefinition.getRun('123', mockParallel) + await expect(invalidMetadata).rejects.toThrow(ParallelError) + + // error if task_id is invalid + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: { + run_id: '123', + metadata: { task_id: 'not-this-task', foo: 'bar' }, + }, + } as any) + const invalidTaskId = () => taskDefinition.getRun('123', mockParallel) + await expect(invalidTaskId).rejects.toThrow(ParallelError) + + // error if metadata is invalid + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: { run_id: '123', metadata: { task_id: 'get-brand-logo' } }, + } as any) + const invalidAttempt = () => taskDefinition.getRun('123', mockParallel) + await expect(invalidAttempt).rejects.toThrow(ParallelError) + + // returns if valid metadata attached + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + } as any) + const validMetadata = await taskDefinition.getRun('123', mockParallel) + expect(validMetadata).toEqual({ + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }) + }) + + it('should throw a ParallelError if data is not returned', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRun.mockResolvedValue({ + data: undefined, + } as any) + + // act + const managedFailureResult = () => + taskDefinition.getRun('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + + it('should throw a ParallelError if an error is thrown', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRun.mockRejectedValue(new Error('test')) + + // act + const managedFailureResult = () => + taskDefinition.getRun('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + }) + + describe('getRunInput', () => { + it('should validate the run metadata and input', async () => { + // arrange + const mockParallel = mock() + + // error if metadata is missing + mockParallel.getTaskRunInput.mockResolvedValueOnce({ + data: { run_id: '123' }, + } as any) + const noMetadata = () => taskDefinition.getRunInput('123', mockParallel) + await expect(noMetadata).rejects.toThrow(ParallelError) + + // error if metadata is invalid + mockParallel.getTaskRunInput.mockResolvedValueOnce({ + data: { run_id: '123', metadata: { foo: 'bar' } }, + } as any) + const invalidMetadata = () => + taskDefinition.getRunInput('123', mockParallel) + await expect(invalidMetadata).rejects.toThrow(ParallelError) + + // error if task_id is invalid + mockParallel.getTaskRunInput.mockResolvedValueOnce({ + data: { run_id: '123', metadata: { task_id: 'not-this-task' } }, + } as any) + const invalidTaskId = () => + taskDefinition.getRunInput('123', mockParallel) + await expect(invalidTaskId).rejects.toThrow(ParallelError) + + // error if metadata is invalid + mockParallel.getTaskRunInput.mockResolvedValueOnce({ + data: { run_id: '123', metadata: { task_id: 'get-brand-logo' } }, + } as any) + const invalidAttempt = () => + taskDefinition.getRunInput('123', mockParallel) + await expect(invalidAttempt).rejects.toThrow(ParallelError) + + // error if input is invalid + mockParallel.getTaskRunInput.mockResolvedValueOnce({ + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + input: { foo: 'bar' }, + }, + } as any) + const invalidInput = () => taskDefinition.getRunInput('123', mockParallel) + await expect(invalidInput).rejects.toThrow(ParallelError) + + // returns if valid metadata attached and input is valid + mockParallel.getTaskRunInput.mockResolvedValueOnce({ + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + input: { url: 'https://example.com' }, + }, + } as any) + const validMetadata = await taskDefinition.getRunInput( + '123', + mockParallel + ) + expect(validMetadata).toEqual({ url: 'https://example.com' }) + }) + + it('should throw a ParallelError if data is not returned', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRunInput.mockResolvedValue({ + data: undefined, + } as any) + + // act + const managedFailureResult = () => + taskDefinition.getRunInput('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + + it('should throw a ParallelError if an error is thrown', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRunInput.mockRejectedValue(new Error('test')) + + // act + const managedFailureResult = () => + taskDefinition.getRunInput('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + }) + + describe('getRunOutput', () => { + it('should validate the run metadata and output', async () => { + // arrange + const mockParallel = mock() + // error if metadata is missing + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { run: { run_id: '123' } }, + } as any) + const noMetadata = () => taskDefinition.getRunOutput('123', mockParallel) + await expect(noMetadata).rejects.toThrow(ParallelError) + + // error if metadata is invalid + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { run: { run_id: '123', metadata: { foo: 'bar' } } }, + } as any) + const invalidMetadata = () => + taskDefinition.getRunOutput('123', mockParallel) + await expect(invalidMetadata).rejects.toThrow(ParallelError) + + // error if task_id is invalid + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { + run: { run_id: '123', metadata: { task_id: 'not-this-task' } }, + }, + } as any) + const invalidTaskId = () => + taskDefinition.getRunOutput('123', mockParallel) + await expect(invalidTaskId).rejects.toThrow(ParallelError) + + // error if metadata is invalid + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { + run: { run_id: '123', metadata: { task_id: 'get-brand-logo' } }, + }, + } as any) + const invalidAttempt = () => + taskDefinition.getRunOutput('123', mockParallel) + await expect(invalidAttempt).rejects.toThrow(ParallelError) + + // returns if valid metadata attached + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { + run: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + output: { + type: 'json', + content: { logoUrl: 'https://example.com' }, + }, + }, + } as any) + const validMetadata = await taskDefinition.getRunOutput( + '123', + mockParallel + ) + expect(validMetadata).toEqual({ logoUrl: 'https://example.com' }) + }) + + it('should throw a ParallelError if output is not json', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { + run: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + output: { type: 'text', content: 'not json' }, + }, + } as any) + + // act + const managedFailureResult = () => + taskDefinition.getRunOutput('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + + it('should throw a ParallelError if data is not returned', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRunResult.mockResolvedValue({ + data: undefined, + } as any) + + // act + const managedFailureResult = () => + taskDefinition.getRunOutput('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + + it('should throw a ParallelError if output does not match the output schema', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { + run: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + output: { type: 'json', content: { foo: 'not-a-url' } }, + }, + } as any) + + // act + const managedFailureResult = () => + taskDefinition.getRunOutput('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + + it('should throw a ParallelError if an error is thrown', async () => { + // arrange + const mockParallel = mock() + mockParallel.getTaskRunResult.mockRejectedValue(new Error('test')) + + // act + const managedFailureResult = () => + taskDefinition.getRunOutput('123', mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + }) + + describe('pollForRunResult', () => { + it('should poll for the run result', async () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com' }), + successLikelihood: 1, + timeToResolveMs: 30, + }) + + // act + const taskRun = await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + webhook: new URL('https://example.com/webhook'), + }, + mockParallel + ) + const result = await taskDefinition.pollForRunOutput( + taskRun.run_id, + mockParallel, + { timeoutMs: 100, intervalMs: 10, backoffExponent: 1 } + ) + + // assert + expect(result).toEqual({ logoUrl: 'https://example.com' }) + }) + + it('should throw a ParallelError if the run fails', async () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com' }), + successLikelihood: 0, + timeToResolveMs: 30, + }) + + // act + const taskRun = await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + const managedFailureResult = () => + taskDefinition.pollForRunOutput(taskRun.run_id, mockParallel, { + timeoutMs: 100, + intervalMs: 10, + backoffExponent: 1, + }) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + + it('should throw a ParallelError if the run times out', async () => { + // arrange + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com' }), + successLikelihood: 1, + timeToResolveMs: 30, + }) + + // act + const taskRun = await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + const managedFailureResult = () => + taskDefinition.pollForRunOutput(taskRun.run_id, mockParallel, { + timeoutMs: 15, + intervalMs: 5, + backoffExponent: 1, + }) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + }) + }) +}) diff --git a/api/test/sdk-task-webhook.spec.ts b/api/test/sdk-task-webhook.spec.ts new file mode 100644 index 0000000..4caa092 --- /dev/null +++ b/api/test/sdk-task-webhook.spec.ts @@ -0,0 +1,549 @@ +import { describe, it, expect, afterEach, vi } from 'vitest' +import { mock } from 'vitest-mock-extended' +import { + MockParallelForTaskDefinition, + ParallelError, + ParallelTaskRunStatusWebhookEvent, + ParallelWebhookHandler, + TaskRun, +} from '../sdk' +import { z } from 'zod' +import { Logger } from '../sdk-logging' +import { IParallel, ParallelProcessor } from '../sdk-parallel' +import { + ParallelTaskDefinition, + ZodParallelTaskJsonIO, + ZodParallelTaskMetadata, +} from '../sdk-task-definition' +import { ParallelTaskWebhook } from '../sdk-task-webhook' + +const mockLogger = mock() + +describe(ParallelTaskWebhook.name, () => { + const input = new ZodParallelTaskJsonIO({ + description: 'url', + schema: z.object({ url: z.string() }), + }) + const output = new ZodParallelTaskJsonIO({ + description: 'logo', + schema: z.object({ logoUrl: z.string() }), + }) + const metadata = new ZodParallelTaskMetadata({ + schema: z.object({ foo: z.string() }), + }) + const onSuccess = vi.fn() + const onFailure = vi.fn() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + onSuccess, + onFailure, + }) + + describe('webhookOptions', () => { + it('should void if no webhook.onSuccess is provided', async () => { + // arrange + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ task: taskDefinition }) + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 0, + }) + + const taskRun = await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + await taskWebhook.webhook( + { + data: { + run_id: taskRun.run_id, + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + timestamp: new Date().toISOString(), + type: 'task_run.status', + }, + mockParallel + ) + }) + it("shouldn't retry if no retries specified", async () => { + const onSuccess = vi.fn() + const onFailure = vi.fn() + const retryForOutput = vi.fn() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + outputIsFailure: retryForOutput, + onSuccess, + onFailure, + }) + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], + logger: mockLogger, + }) + retryForOutput.mockResolvedValue(true) + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 20, + webhookHandler, + }) + + // act + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + webhook: new URL('https://example.com/webhook'), + }, + mockParallel + ) + + // assert + await new Promise((resolve) => setTimeout(resolve, 100)) + expect(retryForOutput).toHaveBeenCalledTimes(1) + expect(onSuccess).not.toHaveBeenCalled() + expect(onFailure).toHaveBeenCalledTimes(1) + }) + + it("shouldn't retry if zero retries specified", async () => { + const onSuccess = vi.fn() + const onFailure = vi.fn() + const retryForOutput = vi.fn() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + outputIsFailure: retryForOutput, + onSuccess, + onFailure, + retries: 0, + }) + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], + logger: mockLogger, + }) + retryForOutput.mockResolvedValue(true) + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 20, + webhookHandler, + }) + + // act + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + webhook: new URL('https://example.com/webhook'), + }, + mockParallel + ) + + // assert + await new Promise((resolve) => setTimeout(resolve, 100)) + expect(retryForOutput).toHaveBeenCalledTimes(1) + expect(onSuccess).not.toHaveBeenCalled() + expect(onFailure).toHaveBeenCalledTimes(1) + }) + + it('should retry the task run if the output fails for x retries', async () => { + const onSuccess = vi.fn() + const retryForOutput = vi.fn() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + onSuccess, + outputIsFailure: retryForOutput, + retries: 3, + }) + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], + logger: mockLogger, + }) + retryForOutput.mockResolvedValue(true) + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 20, + webhookHandler, + }) + + // act + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + webhook: new URL('https://example.com/webhook'), + }, + mockParallel + ) + + // assert + await new Promise((resolve) => setTimeout(resolve, 100)) + expect(retryForOutput).toHaveBeenCalledTimes(3) + }) + + it('should retry for each processor', async () => { + const onSuccess = vi.fn() + const retryForOutput = vi.fn() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + onSuccess, + outputIsFailure: retryForOutput, + retries: { + [ParallelProcessor.Base]: 3, + [ParallelProcessor.Core]: 2, + [ParallelProcessor.Pro]: 1, + [ParallelProcessor.Ultra]: 1, + }, + }) + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], + logger: mockLogger, + }) + retryForOutput.mockResolvedValueOnce(true) + retryForOutput.mockResolvedValueOnce(true) + retryForOutput.mockResolvedValueOnce(true) + retryForOutput.mockResolvedValueOnce(true) + retryForOutput.mockResolvedValueOnce(true) + retryForOutput.mockResolvedValueOnce(true) + retryForOutput.mockResolvedValueOnce(false) + const mockResponse = { logoUrl: 'https://example.com/test' } + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => mockResponse, + successLikelihood: 1, + timeToResolveMs: 20, + webhookHandler, + }) + + // act + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + metadata: { foo: 'bar' }, + webhook: new URL('https://example.com/webhook'), + }, + mockParallel + ) + + // assert + await new Promise((resolve) => setTimeout(resolve, 200)) + expect(retryForOutput).toHaveBeenCalledTimes(7) + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(retryForOutput.mock.calls).toEqual([ + [ + mockResponse, + expect.objectContaining({ + processor: ParallelProcessor.Base, + status: 'completed', + }), + ], + [ + mockResponse, + expect.objectContaining({ + processor: ParallelProcessor.Base, + status: 'completed', + }), + ], + [ + mockResponse, + expect.objectContaining({ + processor: ParallelProcessor.Base, + status: 'completed', + }), + ], + [ + mockResponse, + expect.objectContaining({ + processor: ParallelProcessor.Core, + status: 'completed', + }), + ], + [ + mockResponse, + expect.objectContaining({ + processor: ParallelProcessor.Core, + status: 'completed', + }), + ], + [ + mockResponse, + expect.objectContaining({ + processor: ParallelProcessor.Pro, + status: 'completed', + }), + ], + [ + mockResponse, + expect.objectContaining({ + processor: ParallelProcessor.Ultra, + status: 'completed', + }), + ], + ]) + }) + + it('should retry the task run if the output fails for x retries', async () => { + const onFailure = vi.fn() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input, + output, + metadata, + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + onFailure, + retries: 3, + }) + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], + logger: mockLogger, + }) + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 0, + timeToResolveMs: 20, + webhookHandler, + }) + + // act + await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + webhook: new URL('https://example.com/webhook'), + }, + mockParallel + ) + + // assert + await new Promise((resolve) => setTimeout(resolve, 100)) + expect(onFailure).toHaveBeenCalledTimes(1) + }) + }) + + describe('webhook', () => { + afterEach(() => { + onSuccess.mockReset() + onFailure.mockReset() + }) + + it('should call the webhook handler with the correct output', async () => { + // arrange + const mockParallel = mock() + const taskRun: TaskRun = { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + } + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: taskRun, + request: {} as any, + response: {} as any, + }) + mockParallel.getTaskRunResult.mockResolvedValueOnce({ + data: { + run: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + }, + output: { type: 'json', content: { logoUrl: 'https://example.com' } }, + }, + } as any) + const mockEvent: ParallelTaskRunStatusWebhookEvent = { + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + } + + // act + await taskWebhook.webhook(mockEvent, mockParallel) + + // assert + expect(mockParallel.getTaskRunResult).toHaveBeenCalledWith( + expect.objectContaining({ path: { run_id: '123' } }) + ) + expect(onSuccess).toHaveBeenCalledWith( + { + logoUrl: 'https://example.com', + }, + expect.objectContaining({ + created_at: expect.any(String), + is_active: true, + metadata: { + attempt: 1, + foo: 'bar', + task_id: 'get-brand-logo', + }, + modified_at: expect.any(String), + processor: 'base', + run_id: expect.any(String), + status: 'completed', + }) + ) + expect(onFailure).not.toHaveBeenCalled() + }) + + it('should call the onFailure handler if the task run fails', async () => { + // arrange + const mockParallel = mock() + const taskRun: TaskRun = { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'failed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + } + mockParallel.getTaskRun.mockResolvedValueOnce({ + data: taskRun, + request: {} as any, + response: {} as any, + }) + const mockEvent: ParallelTaskRunStatusWebhookEvent = { + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: taskRun, + } + + // act + await taskWebhook.webhook(mockEvent, mockParallel) + + // assert + expect(onFailure).toHaveBeenCalledWith( + expect.objectContaining({ + created_at: expect.any(String), + is_active: true, + metadata: { + attempt: 1, + foo: 'bar', + task_id: 'get-brand-logo', + webhook_url: undefined, + }, + modified_at: expect.any(String), + processor: 'base', + run_id: expect.any(String), + status: 'failed', + }) + ) + expect(onSuccess).not.toHaveBeenCalled() + }) + + it('should not call the webhook handler if the task is not the correct task', async () => { + // arrange + const mockParallel = mock() + const mockEvent: ParallelTaskRunStatusWebhookEvent = { + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: { + run_id: '123', + metadata: { task_id: 'not-this-task', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + } + + // act + await taskWebhook.webhook(mockEvent, mockParallel) + + // assert + expect(onSuccess).not.toHaveBeenCalled() + expect(onFailure).not.toHaveBeenCalled() + }) + }) + + it('should throw a ParallelError if the event is invalid', async () => { + // arrange + const mockParallel = mock() + const mockEvent = { + type: 'task_run.status', + timestamp: new Date().toISOString(), + } as any + + // act + const managedFailureResult = () => + taskWebhook.webhook(mockEvent, mockParallel) + + // assert + await expect(managedFailureResult).rejects.toThrow(ParallelError) + expect(onSuccess).not.toHaveBeenCalled() + expect(onFailure).not.toHaveBeenCalled() + }) +}) diff --git a/api/test/sdk-webhook.spec.ts b/api/test/sdk-webhook.spec.ts new file mode 100644 index 0000000..6a45b6e --- /dev/null +++ b/api/test/sdk-webhook.spec.ts @@ -0,0 +1,357 @@ +import { describe, it, expect, vi } from 'vitest' +import { mock } from 'vitest-mock-extended' +import { + MockParallelForTaskDefinition, + ParallelError, + ParallelWebhookHandler, +} from '../sdk' +import { z } from 'zod' +import { Logger } from '../sdk-logging' +import { IParallel, Parallel, ParallelProcessor } from '../sdk-parallel' +import { + ParallelTaskDefinition, + ZodParallelTaskJsonIO, + ZodParallelTaskMetadata, +} from '../sdk-task-definition' +import { ParallelTaskWebhook } from '../sdk-task-webhook' + +const mockLogger = mock() + +describe(ParallelWebhookHandler.name, () => { + it('should throw a ParallelError if duplicate identifiers are provided', () => { + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input: new ZodParallelTaskJsonIO({ + description: 'url', + schema: z.object({ url: z.string() }), + }), + output: new ZodParallelTaskJsonIO({ + description: 'logo', + schema: z.object({ logoUrl: z.string() }), + }), + metadata: new ZodParallelTaskMetadata({ + schema: z.object({ foo: z.string() }), + }), + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + + const taskWebhook1 = new ParallelTaskWebhook({ task: taskDefinition }) + const taskWebhook2 = new ParallelTaskWebhook({ task: taskDefinition }) + + // act + const managedFailureResult = () => + new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook1, taskWebhook2], + logger: mockLogger, + }) + + // assert + expect(managedFailureResult).toThrow(ParallelError) + }) + + it('should handle the webhook event', async () => { + // arrange + const webhookHandlerFn = + vi.fn<(output: { logoUrl: string }) => Promise | void>() + const taskDefinition = new ParallelTaskDefinition({ + identifier: 'get-brand-logo', + input: new ZodParallelTaskJsonIO({ + description: 'url', + schema: z.object({ url: z.string() }), + }), + output: new ZodParallelTaskJsonIO({ + description: 'logo', + schema: z.object({ logoUrl: z.string() }), + }), + metadata: new ZodParallelTaskMetadata({ + schema: z.object({ foo: z.string() }), + }), + defaultProcessor: ParallelProcessor.Base, + logger: mockLogger, + }) + const taskWebhook = new ParallelTaskWebhook({ + task: taskDefinition, + onSuccess: webhookHandlerFn, + }) + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [taskWebhook], + logger: mockLogger, + }) + const mockParallel = new MockParallelForTaskDefinition(taskDefinition, { + outputFactory: () => ({ logoUrl: 'https://example.com/test' }), + successLikelihood: 1, + timeToResolveMs: 30, + }) + + const taskRun = await taskDefinition.startRun( + { + input: { url: 'https://example.com' }, + processor: ParallelProcessor.Base, + metadata: { foo: 'bar' }, + }, + mockParallel + ) + await new Promise((resolve) => setTimeout(resolve, 100)) + await webhookHandler.handleTaskWebhook( + { + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: { + run_id: taskRun.run_id, + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + }, + mockParallel + ) + const managedFailureResult = () => + webhookHandler.handleTaskWebhook( + { + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: { + run_id: '123', + metadata: { + task_id: 'not-a-registered-task', + attempt: 1, + foo: 'bar', + }, + status: 'running', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + }, + mockParallel + ) + expect(webhookHandlerFn).toHaveBeenCalledWith( + { + logoUrl: 'https://example.com/test', + }, + expect.objectContaining({}) + ) + await expect(managedFailureResult()).resolves.toBeUndefined() + }) + + it('should parse the webhook event', () => { + // arrange + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [], + logger: mockLogger, + }) + + // act + const successResult = webhookHandler.parseWebhookEvent({ + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + }) + const managedFailureResult = () => webhookHandler.parseWebhookEvent({}) + + // assert + expect(successResult).toEqual({ + type: 'task_run.status', + timestamp: expect.any(String), + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: expect.any(String), + modified_at: expect.any(String), + }, + }) + expect(managedFailureResult).toThrow(ParallelError) + }) + + it('should also handle json strings when parsing the webhook event', () => { + // arrange + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [], + logger: mockLogger, + }) + + // act + const successResult = webhookHandler.parseWebhookEvent( + JSON.stringify({ + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + }) + ) + const managedFailureResult = () => + webhookHandler.parseWebhookEvent('{ "foo": "malformed"') + + // assert + expect(successResult).toEqual({ + type: 'task_run.status', + timestamp: expect.any(String), + data: { + run_id: '123', + metadata: { task_id: 'get-brand-logo', attempt: 1, foo: 'bar' }, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: expect.any(String), + modified_at: expect.any(String), + }, + }) + expect(managedFailureResult).toThrow(ParallelError) + }) + + it('should throw a ParallelError if the event metadata is invalid', async () => { + // arrange + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [], + logger: mockLogger, + }) + const mockParallel = mock() + + // act + const managedFailureResult = () => + webhookHandler.handleTaskWebhook( + { + type: 'task_run.status', + timestamp: new Date().toISOString(), + data: { + run_id: '123', + metadata: {}, + status: 'completed', + is_active: true, + processor: ParallelProcessor.Base, + created_at: new Date().toISOString(), + modified_at: new Date().toISOString(), + }, + }, + mockParallel + ) + + // assert + await expect(managedFailureResult).rejects.toThrow( + 'Invalid task run status webhook event' + ) + }) + + it('should verify the webhook secret if a webhook secret is provided', async () => { + // arrange + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [], + logger: mockLogger, + }) + + const secret = '5b1e2d8b-8ea4-470d-94ec-5335283401a8' + const parallel = new Parallel({ apiKey: secret, webhookSecret: secret }) + + // act + const successResult = await webhookHandler.verifyWebhookSecret( + { + payload: + '{"type":"task_run.status","timestamp":"2025-08-01T23:23:40.445706+00:00","data":{"created_at":"2025-08-01T23:22:56.783274Z","taskgroup_id":null,"metadata":{"task_id":"brand-logo","webhook_url":"https://4a6b4586206987.lhr.life/webhooks/parallel","attempt":1.0},"warnings":null,"run_id":"trun_9f802d7fc9a743e994432c2e2c8a553b","is_active":false,"processor":"base","error":null,"status":"completed","modified_at":"2025-08-01T23:23:40.269668Z"}}', + webhookSignature: 'v1,FI9JWvsXRSmxHHqVS4tvdufOergwijRngyEHjv6jxbY', + webhookId: 'whevent_a316c67f0e6843f1b61aff86237c843f', + webhookTimestamp: '1754090621', + }, + parallel + ) + + // assert + expect(successResult).toBe(true) + }) + + it('should return false if the webhook secret is invalid', async () => { + // arrange + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [], + logger: mockLogger, + }) + + const secret = '5b1e2d8b-8ea4-470d-94ec-5335283401a8' + const parallel = new Parallel({ apiKey: secret, webhookSecret: secret }) + + // act + const failureResult = await webhookHandler.verifyWebhookSecret( + { + payload: + '{"type":"task_run.status","timestamp":"20201T23:23:40.445706+00:00","data":{"created_at":"2025-08-01T23:22:56.783274Z","taskgroup_id":null,"metadata":{"task_id":"brand-logo","webhook_url":"https://4a6b4586206987.lhr.life/webhooks/parallel","attempt":1.0},"warnings":null,"run_id":"trun_9f802d7fc9a743e994432c2e2c8a553b","is_active":false,"processor":"base","error":null,"status":"completed","modified_at":"2025-08-01T23:23:40.269668Z"}}', + webhookSignature: 'v1,FI9JWvsXRSmxHHqVS4tvdufOergwijRngyEHjv6jxbY', + webhookId: 'whevent_a316c67f0e6843f1b61aff86237c843f', + webhookTimestamp: '1754090621', + }, + parallel + ) + + // assert + expect(failureResult).toBe(false) + }) + + it('should return true if no webhook secret in client', async () => { + // arrange + const parallel = new Parallel({ apiKey: '123' }) + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [], + logger: mockLogger, + }) + + // act + const successResult = await webhookHandler.verifyWebhookSecret( + { + payload: '123', + webhookSignature: '123', + webhookId: '123', + webhookTimestamp: '123', + }, + parallel + ) + + // assert + expect(successResult).toBe(true) + }) + + it('should return false if an error occurs', async () => { + // arrange + const webhookHandler = new ParallelWebhookHandler({ + taskWebhooks: [], + logger: mockLogger, + }) + + const secret = '5b1e2d8b-8ea4-470d-94ec-5335283401a8' + const parallel = new Parallel({ apiKey: secret, webhookSecret: secret }) + + // act + const failureResult = await webhookHandler.verifyWebhookSecret( + { + webhookSignature: null as unknown as string, + webhookId: 'wh_123', + webhookTimestamp: '1234567890', + payload: '{"test": "data"}', + }, + parallel + ) + + // assert + expect(failureResult).toBe(false) + }) +}) diff --git a/api/types.gen.ts b/api/types.gen.ts new file mode 100644 index 0000000..5920450 --- /dev/null +++ b/api/types.gen.ts @@ -0,0 +1,544 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * Citation + * A citation for a task output. + */ +export type Citation = { + /** + * Title + * Title of the citation. + */ + title?: string | null + /** + * Url + * URL of the citation. + */ + url: string + /** + * Excerpts + * Excerpts from the citation supporting the output. Only certain processors provide excerpts. + */ + excerpts?: Array | null +} + +/** + * Error + * An error message. + */ +export type _Error = { + /** + * Ref Id + * Reference ID for the error. + */ + ref_id: string + /** + * Message + * Human-readable message. + */ + message: string + /** + * Detail + * Optional detail supporting the error. + */ + detail?: { + [key: string]: unknown + } | null +} + +/** + * ErrorResponse + * Response object used for non-200 status codes. + */ +export type ErrorResponse = { + /** + * Type + * Always 'error'. + */ + type?: 'error' + /** + * Error. + */ + error: _Error +} + +/** + * HTTPValidationError + */ +export type HttpValidationError = { + /** + * Detail + */ + detail?: Array +} + +/** + * JsonSchema + * JSON schema for a task input or output. + */ +export type JsonSchema = { + /** + * Json Schema + * A JSON Schema object. Only a subset of JSON Schema is supported. + */ + json_schema: { + [key: string]: unknown + } + /** + * Type + * The type of schema being defined. Always `json`. + */ + type?: 'json' +} + +/** + * TaskRun + * Status of a task. + */ +export type TaskRun = { + /** + * Run Id + * ID of the task run. + */ + run_id: string + /** + * Status + * Status of the run. + */ + status: + | 'queued' + | 'action_required' + | 'running' + | 'completed' + | 'failed' + | 'cancelling' + | 'cancelled' + /** + * Is Active + * Whether the run is currently active; i.e. status is one of {'running', 'queued', 'cancelling'}. + */ + is_active: boolean + /** + * Warnings + * Warnings for the run. + */ + warnings?: Array | null + /** + * Processor + * Processor used for the run. + */ + processor: string + /** + * Metadata + * User-provided metadata stored with the run. + */ + metadata?: { + [key: string]: string | number | number | boolean + } | null + /** + * Created At + * Timestamp of the creation of the task, as an RFC 3339 string. + */ + created_at: string | null + /** + * Modified At + * Timestamp of the last modification to the task, as an RFC 3339 string. + */ + modified_at: string | null +} + +/** + * TaskRunInput + * Request to run a task. + */ +export type TaskRunInputInput = { + /** + * Task specification. + */ + task_spec?: TaskSpec | null + /** + * Input + * Input to the task, either text or a JSON object. + */ + input: + | string + | { + [key: string]: unknown + } + /** + * Processor + * Processor to use for the task. + */ + processor: string + /** + * Metadata + * User-provided metadata stored with the run. Keys and values must be strings with a maximum length of 16 and 512 characters respectively. + */ + metadata?: { + [key: string]: string | number | number | boolean + } | null +} + +/** + * TaskRunInput + * Request to run a task. + */ +export type TaskRunInputOutput = { + /** + * Task specification. + */ + task_spec?: TaskSpec | null + /** + * Input + * Input to the task, either text or a JSON object. + */ + input: + | string + | { + [key: string]: unknown + } + /** + * Processor + * Processor to use for the task. + */ + processor: string + /** + * Metadata + * User-provided metadata stored with the run. Keys and values must be strings with a maximum length of 16 and 512 characters respectively. + */ + metadata?: { + [key: string]: string | number | number | boolean + } | null +} + +/** + * TaskRunJsonOutput + * Output from a task that returns text. + */ +export type TaskRunJsonOutput = { + /** + * Content + * Output from the task as a native JSON object, as determined by the output schema of the task spec. + */ + content: { + [key: string]: unknown + } + /** + * Basis + * Basis for each top-level field in the JSON output. + */ + basis: Array + /** + * Type + * The type of output being returned, as determined by the output schema of the task spec. + */ + type: 'json' +} + +/** + * TaskRunResult + * Result of a task run. + */ +export type TaskRunResult = { + /** + * Task run object with status 'completed'. + */ + run: TaskRun + /** + * Output + * Output from the task conforming to the output schema. + */ + output: TaskRunTextOutput | TaskRunJsonOutput +} + +/** + * TaskRunTextOutput + * Output from a task that returns text. + */ +export type TaskRunTextOutput = { + /** + * Content + * Text output from the task. + */ + content: string + /** + * Basis + * Basis for the output. The basis has a single field 'output'. + */ + basis: Array + /** + * Type + * The type of output being returned, as determined by the output schema of the task spec. + */ + type: 'text' +} + +/** + * TaskSpec + * Specification for a task. + * + * For convenience we allow bare strings as input or output schemas, which is + * equivalent to a text schema with the same description. + */ +export type TaskSpec = { + /** + * Output Schema + * JSON schema or text fully describing the desired output from the task. Descriptions of output fields will determine the form and content of the response. A bare string is equivalent to a text schema with the same description. + */ + output_schema: JsonSchema | TextSchema | string + /** + * Input Schema + * Optional JSON schema or text description of expected input to the task. A bare string is equivalent to a text schema with the same description. + */ + input_schema?: JsonSchema | TextSchema | string | null +} + +/** + * TextSchema + * Text description for a task input or output. + */ +export type TextSchema = { + /** + * Description + * A text description of the desired output from the task. + */ + description: string + /** + * Type + * The type of schema being defined. Always `text`. + */ + type?: 'text' +} + +/** + * ValidationError + */ +export type ValidationError = { + /** + * Location + */ + loc: Array + /** + * Message + */ + msg: string + /** + * Error Type + */ + type: string +} + +/** + * FieldBasis + * Citations and reasoning supporting one field of a task output. + */ +export type ShapleySearchTasksApiV1FieldBasis = { + /** + * Field + * Name of the output field. + */ + field: string + /** + * Citations + * List of citations supporting the output field. + */ + citations?: Array + /** + * Reasoning + * Reasoning for the output field. + */ + reasoning: string + /** + * Confidence + * Confidence level for the output field. Only certain processors provide confidence levels. + */ + confidence?: string | null +} + +/** + * Warning + * Human-readable message for a task. + */ +export type ShapleySearchTasksApiV1Warning = { + /** + * Type + * Type of warning. Note that adding new warning types is considered a backward-compatible change. + */ + type: string + /** + * Message + * Human-readable message. + */ + message: string + /** + * Detail + * Optional detail supporting the warning. + */ + detail?: { + [key: string]: unknown + } | null +} + +export type TasksRunsPostV1TasksRunsPostData = { + body: TaskRunInputInput + path?: never + query?: never + url: '/v1/tasks/runs' +} + +export type TasksRunsPostV1TasksRunsPostErrors = { + /** + * Insufficient credit in account + */ + 402: ErrorResponse + /** + * Invalid processor in request + */ + 403: ErrorResponse + /** + * Request validation error + */ + 422: ErrorResponse + /** + * Quota temporarily exceeded + */ + 429: ErrorResponse +} + +export type TasksRunsPostV1TasksRunsPostError = + TasksRunsPostV1TasksRunsPostErrors[keyof TasksRunsPostV1TasksRunsPostErrors] + +export type TasksRunsPostV1TasksRunsPostResponses = { + /** + * Successful Response + */ + 200: TaskRun + /** + * Accepted + */ + 202: TaskRun +} + +export type TasksRunsPostV1TasksRunsPostResponse = + TasksRunsPostV1TasksRunsPostResponses[keyof TasksRunsPostV1TasksRunsPostResponses] + +export type TasksRunsGetV1TasksRunsRunIdGetData = { + body?: never + path: { + /** + * Run Id + */ + run_id: string + } + query?: never + url: '/v1/tasks/runs/{run_id}' +} + +export type TasksRunsGetV1TasksRunsRunIdGetErrors = { + /** + * Run not found + */ + 404: ErrorResponse + /** + * Validation Error + */ + 422: HttpValidationError +} + +export type TasksRunsGetV1TasksRunsRunIdGetError = + TasksRunsGetV1TasksRunsRunIdGetErrors[keyof TasksRunsGetV1TasksRunsRunIdGetErrors] + +export type TasksRunsGetV1TasksRunsRunIdGetResponses = { + /** + * Successful Response + */ + 200: TaskRun +} + +export type TasksRunsGetV1TasksRunsRunIdGetResponse = + TasksRunsGetV1TasksRunsRunIdGetResponses[keyof TasksRunsGetV1TasksRunsRunIdGetResponses] + +export type TasksRunsInputGetV1TasksRunsRunIdInputGetData = { + body?: never + path: { + /** + * Run Id + */ + run_id: string + } + query?: never + url: '/v1/tasks/runs/{run_id}/input' +} + +export type TasksRunsInputGetV1TasksRunsRunIdInputGetErrors = { + /** + * Not Found + */ + 404: ErrorResponse + /** + * Validation Error + */ + 422: HttpValidationError +} + +export type TasksRunsInputGetV1TasksRunsRunIdInputGetError = + TasksRunsInputGetV1TasksRunsRunIdInputGetErrors[keyof TasksRunsInputGetV1TasksRunsRunIdInputGetErrors] + +export type TasksRunsInputGetV1TasksRunsRunIdInputGetResponses = { + /** + * Successful Response + */ + 200: TaskRunInputOutput +} + +export type TasksRunsInputGetV1TasksRunsRunIdInputGetResponse = + TasksRunsInputGetV1TasksRunsRunIdInputGetResponses[keyof TasksRunsInputGetV1TasksRunsRunIdInputGetResponses] + +export type TasksRunsResultGetV1TasksRunsRunIdResultGetData = { + body?: never + path: { + /** + * Run Id + */ + run_id: string + } + query?: { + /** + * Timeout + */ + timeout?: number + } + url: '/v1/tasks/runs/{run_id}/result' +} + +export type TasksRunsResultGetV1TasksRunsRunIdResultGetErrors = { + /** + * Run failed or run id not found + */ + 404: ErrorResponse + /** + * Run still active + */ + 408: ErrorResponse + /** + * Validation Error + */ + 422: HttpValidationError +} + +export type TasksRunsResultGetV1TasksRunsRunIdResultGetError = + TasksRunsResultGetV1TasksRunsRunIdResultGetErrors[keyof TasksRunsResultGetV1TasksRunsRunIdResultGetErrors] + +export type TasksRunsResultGetV1TasksRunsRunIdResultGetResponses = { + /** + * Successful Response + */ + 200: TaskRunResult +} + +export type TasksRunsResultGetV1TasksRunsRunIdResultGetResponse = + TasksRunsResultGetV1TasksRunsRunIdResultGetResponses[keyof TasksRunsResultGetV1TasksRunsRunIdResultGetResponses] + +export type ClientOptions = { + baseUrl: 'https://api.parallel.ai' | (string & {}) +} diff --git a/openapi-ts.config.ts b/openapi-ts.config.ts new file mode 100644 index 0000000..be9059b --- /dev/null +++ b/openapi-ts.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from '@hey-api/openapi-ts' + +export default defineConfig({ + input: 'openapi.yml', + output: { format: 'prettier', lint: 'eslint', path: './api' }, + plugins: [ + '@hey-api/schemas', + { name: '@hey-api/typescript', enums: 'javascript' }, + { name: '@hey-api/sdk', asClass: false }, + ], +}) diff --git a/openapi.yml b/openapi.yml new file mode 100644 index 0000000..a0d7677 --- /dev/null +++ b/openapi.yml @@ -0,0 +1,652 @@ +openapi: 3.1.0 +info: + title: FastAPI + version: 0.1.0 + contact: + name: Parallel API + url: https://parallel.ai + email: support@parallel.ai + description: Parallel API +paths: + /v1/tasks/runs: + post: + tags: + - v1 + summary: Tasks Runs Post + description: Initiates a single task run. + operationId: tasks_runs_post_v1_tasks_runs_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TaskRunInput-Input' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TaskRun' + '202': + description: Accepted + content: + application/json: + schema: + $ref: '#/components/schemas/TaskRun' + '402': + description: Insufficient credit in account + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Invalid processor in request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Request validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '429': + description: Quota temporarily exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /v1/tasks/runs/{run_id}: + get: + tags: + - v1 + summary: Tasks Runs Get + description: Retrieves a run by run_id. + operationId: tasks_runs_get_v1_tasks_runs__run_id__get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TaskRun' + '404': + description: Run not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/tasks/runs/{run_id}/input: + get: + tags: + - v1 + summary: Tasks Runs Input Get + description: Retrieves the input of a run by run_id. + operationId: tasks_runs_input_get_v1_tasks_runs__run_id__input_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TaskRunInput-Output' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /v1/tasks/runs/{run_id}/result: + get: + tags: + - v1 + summary: Tasks Runs Result Get + description: Retrieves a run by run_id, blocking until the run is completed. + operationId: tasks_runs_result_get_v1_tasks_runs__run_id__result_get + parameters: + - name: run_id + in: path + required: true + schema: + type: string + title: Run Id + - name: timeout + in: query + required: false + schema: + type: integer + default: 600 + title: Timeout + x-stainless-naming: + python: + method_argument: api_timeout + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TaskRunResult' + '404': + description: Run failed or run id not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '408': + description: Run still active + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' +components: + schemas: + Citation: + properties: + title: + anyOf: + - type: string + - type: 'null' + title: Title + description: Title of the citation. + url: + type: string + title: Url + description: URL of the citation. + excerpts: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Excerpts + description: Excerpts from the citation supporting the output. Only certain processors provide excerpts. + type: object + required: + - url + title: Citation + description: A citation for a task output. + Error: + properties: + ref_id: + type: string + title: Ref Id + description: Reference ID for the error. + message: + type: string + title: Message + description: Human-readable message. + detail: + anyOf: + - type: object + - type: 'null' + title: Detail + description: Optional detail supporting the error. + type: object + required: + - ref_id + - message + title: Error + description: An error message. + ErrorResponse: + properties: + type: + type: string + enum: + - error + const: error + title: Type + description: Always 'error'. + default: error + error: + allOf: + - $ref: '#/components/schemas/Error' + description: Error. + type: object + required: + - error + title: ErrorResponse + description: Response object used for non-200 status codes. + HTTPValidationError: + properties: + detail: + items: + $ref: '#/components/schemas/ValidationError' + type: array + title: Detail + type: object + title: HTTPValidationError + JsonSchema: + properties: + json_schema: + type: object + title: Json Schema + description: A JSON Schema object. Only a subset of JSON Schema is supported. + examples: + - additionalProperties: false + properties: + gdp: + description: GDP in USD for the year, formatted like '$3.1 trillion (2023)' + type: string + required: + - gdp + type: object + type: + type: string + enum: + - json + const: json + title: Type + description: The type of schema being defined. Always `json`. + default: json + type: object + required: + - json_schema + title: JsonSchema + description: JSON schema for a task input or output. + TaskRun: + properties: + run_id: + type: string + title: Run Id + description: ID of the task run. + status: + type: string + enum: + - queued + - action_required + - running + - completed + - failed + - cancelling + - cancelled + title: Status + description: Status of the run. + examples: + - queued + - action_required + - running + - completed + - failed + - cancelling + - cancelled + is_active: + type: boolean + title: Is Active + description: Whether the run is currently active; i.e. status is one of {'running', 'queued', 'cancelling'}. + warnings: + anyOf: + - items: + $ref: '#/components/schemas/shapley__search__tasks__api_v1__Warning' + type: array + - type: 'null' + title: Warnings + description: Warnings for the run. + processor: + type: string + title: Processor + description: Processor used for the run. + metadata: + anyOf: + - additionalProperties: + anyOf: + - type: string + - type: integer + - type: number + - type: boolean + type: object + - type: 'null' + title: Metadata + description: User-provided metadata stored with the run. + created_at: + anyOf: + - type: string + - type: 'null' + title: Created At + description: Timestamp of the creation of the task, as an RFC 3339 string. + modified_at: + anyOf: + - type: string + - type: 'null' + title: Modified At + description: Timestamp of the last modification to the task, as an RFC 3339 string. + type: object + required: + - run_id + - status + - is_active + - processor + - created_at + - modified_at + title: TaskRun + description: Status of a task. + TaskRunInput-Input: + properties: + task_spec: + anyOf: + - $ref: '#/components/schemas/TaskSpec' + - type: 'null' + description: Task specification. + input: + anyOf: + - type: string + - type: object + title: Input + description: Input to the task, either text or a JSON object. + examples: + - France (2023) + - country: France + year: 2023 + processor: + type: string + title: Processor + description: Processor to use for the task. + metadata: + anyOf: + - additionalProperties: + anyOf: + - type: string + - type: integer + - type: number + - type: boolean + type: object + - type: 'null' + title: Metadata + description: >- + User-provided metadata stored with the run. Keys and values must be strings with a maximum length + of 16 and 512 characters respectively. + type: object + required: + - input + - processor + title: TaskRunInput + description: Request to run a task. + TaskRunInput-Output: + properties: + task_spec: + anyOf: + - $ref: '#/components/schemas/TaskSpec' + - type: 'null' + description: Task specification. + input: + anyOf: + - type: string + - type: object + title: Input + description: Input to the task, either text or a JSON object. + examples: + - France (2023) + - country: France + year: 2023 + processor: + type: string + title: Processor + description: Processor to use for the task. + metadata: + anyOf: + - additionalProperties: + anyOf: + - type: string + - type: integer + - type: number + - type: boolean + type: object + - type: 'null' + title: Metadata + description: >- + User-provided metadata stored with the run. Keys and values must be strings with a maximum length + of 16 and 512 characters respectively. + type: object + required: + - input + - processor + title: TaskRunInput + description: Request to run a task. + TaskRunJsonOutput: + properties: + content: + type: object + title: Content + description: Output from the task as a native JSON object, as determined by the output schema of the task spec. + basis: + items: + $ref: '#/components/schemas/shapley__search__tasks__api_v1__FieldBasis' + type: array + title: Basis + description: Basis for each top-level field in the JSON output. + type: + type: string + enum: + - json + const: json + title: Type + description: The type of output being returned, as determined by the output schema of the task spec. + type: object + required: + - content + - basis + - type + title: TaskRunJsonOutput + description: Output from a task that returns text. + TaskRunResult: + properties: + run: + allOf: + - $ref: '#/components/schemas/TaskRun' + description: Task run object with status 'completed'. + output: + anyOf: + - $ref: '#/components/schemas/TaskRunTextOutput' + - $ref: '#/components/schemas/TaskRunJsonOutput' + title: Output + description: Output from the task conforming to the output schema. + type: object + required: + - run + - output + title: TaskRunResult + description: Result of a task run. + TaskRunTextOutput: + properties: + content: + type: string + title: Content + description: Text output from the task. + basis: + items: + $ref: '#/components/schemas/shapley__search__tasks__api_v1__FieldBasis' + type: array + title: Basis + description: Basis for the output. The basis has a single field 'output'. + type: + type: string + enum: + - text + const: text + title: Type + description: The type of output being returned, as determined by the output schema of the task spec. + type: object + required: + - content + - basis + - type + title: TaskRunTextOutput + description: Output from a task that returns text. + TaskSpec: + properties: + output_schema: + anyOf: + - $ref: '#/components/schemas/JsonSchema' + - $ref: '#/components/schemas/TextSchema' + - type: string + title: Output Schema + description: >- + JSON schema or text fully describing the desired output from the task. Descriptions of output + fields will determine the form and content of the response. A bare string is equivalent to a text + schema with the same description. + input_schema: + anyOf: + - $ref: '#/components/schemas/JsonSchema' + - $ref: '#/components/schemas/TextSchema' + - type: string + - type: 'null' + title: Input Schema + description: >- + Optional JSON schema or text description of expected input to the task. A bare string is + equivalent to a text schema with the same description. + type: object + required: + - output_schema + title: TaskSpec + description: |- + Specification for a task. + + For convenience we allow bare strings as input or output schemas, which is + equivalent to a text schema with the same description. + TextSchema: + properties: + description: + type: string + title: Description + description: A text description of the desired output from the task. + examples: + - GDP in USD for the year, formatted like '$3.1 trillion (2023)' + type: + type: string + enum: + - text + const: text + title: Type + description: The type of schema being defined. Always `text`. + default: text + type: object + required: + - description + title: TextSchema + description: Text description for a task input or output. + ValidationError: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Location + msg: + type: string + title: Message + type: + type: string + title: Error Type + type: object + required: + - loc + - msg + - type + title: ValidationError + shapley__search__tasks__api_v1__FieldBasis: + properties: + field: + type: string + title: Field + description: Name of the output field. + citations: + items: + $ref: '#/components/schemas/Citation' + type: array + title: Citations + description: List of citations supporting the output field. + default: [] + reasoning: + type: string + title: Reasoning + description: Reasoning for the output field. + confidence: + anyOf: + - type: string + - type: 'null' + title: Confidence + description: Confidence level for the output field. Only certain processors provide confidence levels. + examples: + - low + - medium + - high + type: object + required: + - field + - reasoning + title: FieldBasis + description: Citations and reasoning supporting one field of a task output. + shapley__search__tasks__api_v1__Warning: + properties: + type: + type: string + title: Type + description: Type of warning. Note that adding new warning types is considered a backward-compatible change. + examples: + - spec_validation + - input_validation + message: + type: string + title: Message + description: Human-readable message. + detail: + anyOf: + - type: object + - type: 'null' + title: Detail + description: Optional detail supporting the warning. + type: object + required: + - type + - message + title: Warning + description: Human-readable message for a task. + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: x-api-key +tags: + - name: v1 + description: v1 +servers: + - url: https://api.parallel.ai + description: Parallel API +security: + - ApiKeyAuth: [] diff --git a/package.json b/package.json index 654af53..748bb99 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "@owner.com/parallel-sdk", - "version": "0.0.0", - "main": "index.js", - "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, + "version": "0.0.1", + "description": "Client to interact with Parallel", + "keywords": [ + "parallel", + "paralle-ai", + "sdk" + ], "repository": { "type": "git", "url": "git+https://github.com/Owner/parallel-sdk.git" @@ -16,5 +17,47 @@ "url": "https://github.com/Owner/parallel-sdk/issues" }, "homepage": "https://github.com/Owner/parallel-sdk#readme", - "description": "" + "type": "commonjs", + "main": "./dist/sdk.js", + "exports": "./dist/sdk.js", + "types": "./dist/sdk.d.ts", + "files": [ + "/dist" + ], + "scripts": { + "build": "tsc --build", + "openapi-ts": "openapi-ts", + "test": "vitest", + "coverage": "vitest run --coverage" + }, + "devDependencies": { + "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0", + "@hey-api/openapi-ts": "0.80.0", + "@tsconfig/node22": "^22.0.0", + "@tsconfig/recommended": "^1.0.8", + "@types/node": "~22", + "@typescript-eslint/eslint-plugin": "^8.32.0", + "@typescript-eslint/parser": "^8.32.0", + "@vitest/coverage-v8": "3.2.4", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-import-resolver-typescript": "^3.7.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.6.1", + "eslint-plugin-n": "^17.15.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^7.2.1", + "eslint-plugin-sort-keys-fix": "^1.1.2", + "msw": "^2.10.4", + "typescript": "^5.6.2", + "vitest": "^3.1.1", + "vitest-mock-extended": "^3.1.0" + }, + "dependencies": { + "rxjs": "^7.8.2", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.6" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ede792e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node", + "extends": "@tsconfig/node22", + "compilerOptions": { + "allowJs": true, + "composite": true, + "declaration": true, + "declarationMap": true, + "incremental": true, + "target": "ESNext", + "module": "NodeNext", + "moduleResolution": "nodenext", + "noErrorTruncation": true, + "outDir": "${configDir}/dist", + "resolveJsonModule": true, + "sourceMap": true, + "tsBuildInfoFile": "${configDir}/dist/tsconfig.tsbuildinfo", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "rootDir": "api" + }, + "include": ["api/**/*"] +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..f8507fc --- /dev/null +++ b/turbo.json @@ -0,0 +1,9 @@ +{ + "extends": ["//"], + "tasks": { + "build": { + "inputs": ["$TURBO_DEFAULT$"], + "outputs": ["dist/**"] + } + } +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..703f193 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + coverage: { + provider: 'v8', + thresholds: { + branches: 95, + functions: 95, + lines: 95, + statements: 95, + }, + reportOnFailure: true, + include: [ + 'api/sdk-webhook.ts', + 'api/sdk-task-definition.ts', + 'api/sdk-parallel.ts', + 'api/sdk-mocks.ts', + 'api/sdk-errors.ts', + 'api/base-encoding.ts', + ], + }, + }, +}) diff --git a/yarn.lock b/yarn.lock index fb57ccd..cd9dae6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,3 +2,3916 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + +"@babel/parser@^7.25.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.4.tgz#da25d4643532890932cc03f7705fe19637e03fa8" + integrity sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg== + dependencies: + "@babel/types" "^7.28.4" + +"@babel/types@^7.25.4", "@babel/types@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.4.tgz#0a4e618f4c60a7cd6c11cb2d48060e4dbe38ac3a" + integrity sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@bcoe/v8-coverage@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa" + integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + +"@bundled-es-modules/cookie@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz#b41376af6a06b3e32a15241d927b840a9b4de507" + integrity sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw== + dependencies: + cookie "^0.7.2" + +"@bundled-es-modules/statuses@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz#761d10f44e51a94902c4da48675b71a76cc98872" + integrity sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg== + dependencies: + statuses "^2.0.1" + +"@emnapi/core@^1.4.3": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.5.0.tgz#85cd84537ec989cebb2343606a1ee663ce4edaf0" + integrity sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg== + dependencies: + "@emnapi/wasi-threads" "1.1.0" + tslib "^2.4.0" + +"@emnapi/runtime@^1.4.3": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.5.0.tgz#9aebfcb9b17195dce3ab53c86787a6b7d058db73" + integrity sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ== + dependencies: + tslib "^2.4.0" + +"@emnapi/wasi-threads@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" + integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== + dependencies: + tslib "^2.4.0" + +"@es-joy/jsdoccomment@~0.50.2": + version "0.50.2" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.50.2.tgz#707768f0cb62abe0703d51aa9086986d230a5d5c" + integrity sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA== + dependencies: + "@types/estree" "^1.0.6" + "@typescript-eslint/types" "^8.11.0" + comment-parser "1.4.1" + esquery "^1.6.0" + jsdoc-type-pratt-parser "~4.1.0" + +"@esbuild/aix-ppc64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz#bef96351f16520055c947aba28802eede3c9e9a9" + integrity sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA== + +"@esbuild/android-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz#d2e70be7d51a529425422091e0dcb90374c1546c" + integrity sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg== + +"@esbuild/android-arm@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.9.tgz#d2a753fe2a4c73b79437d0ba1480e2d760097419" + integrity sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ== + +"@esbuild/android-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.9.tgz#5278836e3c7ae75761626962f902a0d55352e683" + integrity sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw== + +"@esbuild/darwin-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz#f1513eaf9ec8fa15dcaf4c341b0f005d3e8b47ae" + integrity sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg== + +"@esbuild/darwin-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz#e27dbc3b507b3a1cea3b9280a04b8b6b725f82be" + integrity sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ== + +"@esbuild/freebsd-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz#364e3e5b7a1fd45d92be08c6cc5d890ca75908ca" + integrity sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q== + +"@esbuild/freebsd-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz#7c869b45faeb3df668e19ace07335a0711ec56ab" + integrity sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg== + +"@esbuild/linux-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz#48d42861758c940b61abea43ba9a29b186d6cb8b" + integrity sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw== + +"@esbuild/linux-arm@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz#6ce4b9cabf148274101701d112b89dc67cc52f37" + integrity sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw== + +"@esbuild/linux-ia32@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz#207e54899b79cac9c26c323fc1caa32e3143f1c4" + integrity sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A== + +"@esbuild/linux-loong64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz#0ba48a127159a8f6abb5827f21198b999ffd1fc0" + integrity sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ== + +"@esbuild/linux-mips64el@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz#a4d4cc693d185f66a6afde94f772b38ce5d64eb5" + integrity sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA== + +"@esbuild/linux-ppc64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz#0f5805c1c6d6435a1dafdc043cb07a19050357db" + integrity sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w== + +"@esbuild/linux-riscv64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz#6776edece0f8fca79f3386398b5183ff2a827547" + integrity sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg== + +"@esbuild/linux-s390x@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz#3f6f29ef036938447c2218d309dc875225861830" + integrity sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA== + +"@esbuild/linux-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz#831fe0b0e1a80a8b8391224ea2377d5520e1527f" + integrity sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg== + +"@esbuild/netbsd-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz#06f99d7eebe035fbbe43de01c9d7e98d2a0aa548" + integrity sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q== + +"@esbuild/netbsd-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz#db99858e6bed6e73911f92a88e4edd3a8c429a52" + integrity sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g== + +"@esbuild/openbsd-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz#afb886c867e36f9d86bb21e878e1185f5d5a0935" + integrity sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ== + +"@esbuild/openbsd-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz#30855c9f8381fac6a0ef5b5f31ac6e7108a66ecf" + integrity sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA== + +"@esbuild/openharmony-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz#2f2144af31e67adc2a8e3705c20c2bd97bd88314" + integrity sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg== + +"@esbuild/sunos-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz#69b99a9b5bd226c9eb9c6a73f990fddd497d732e" + integrity sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw== + +"@esbuild/win32-arm64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz#d789330a712af916c88325f4ffe465f885719c6b" + integrity sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ== + +"@esbuild/win32-ia32@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz#52fc735406bd49688253e74e4e837ac2ba0789e3" + integrity sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww== + +"@esbuild/win32-x64@0.25.9": + version "0.25.9" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz#585624dc829cfb6e7c0aa6c3ca7d7e6daa87e34f" + integrity sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ== + +"@eslint-community/eslint-plugin-eslint-comments@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.5.0.tgz#4ffa576583bd99dfbaf74c893635e2c76acba048" + integrity sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg== + dependencies: + escape-string-regexp "^4.0.0" + ignore "^5.2.4" + +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.5.0", "@eslint-community/eslint-utils@^4.7.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz#0e3b5e45566d1bce1ec47d8aae2fc2ad77ad0894" + integrity sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.6.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + +"@hey-api/json-schema-ref-parser@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.6.tgz#3c415fc265846aff8455039bb90e4283e0cc30c0" + integrity sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.15" + js-yaml "^4.1.0" + lodash "^4.17.21" + +"@hey-api/openapi-ts@0.80.0": + version "0.80.0" + resolved "https://registry.yarnpkg.com/@hey-api/openapi-ts/-/openapi-ts-0.80.0.tgz#057dff90a903cfbc83c5dcd365f106db55fc2f11" + integrity sha512-sX0TFKCvwMyh10C1mmqYR2TBaHla//72kocuPpRM5ya38LqRaqkMW9A0hjcrZTrzFtjYtz2Pdr3in+JrsM3TLA== + dependencies: + "@hey-api/json-schema-ref-parser" "1.0.6" + ansi-colors "4.1.3" + c12 "2.0.1" + color-support "1.1.3" + commander "13.0.0" + handlebars "4.7.8" + open "10.1.2" + semver "7.7.2" + +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@inquirer/confirm@^5.0.0": + version "5.1.16" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.16.tgz#4f99603e5c8a1b471b819343f708c75e8abd2b88" + integrity sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag== + dependencies: + "@inquirer/core" "^10.2.0" + "@inquirer/type" "^3.0.8" + +"@inquirer/core@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.2.0.tgz#19ff527dbe0956891d825e320ecbc890bd6a1550" + integrity sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA== + dependencies: + "@inquirer/figures" "^1.0.13" + "@inquirer/type" "^3.0.8" + ansi-escapes "^4.3.2" + cli-width "^4.1.0" + mute-stream "^2.0.0" + signal-exit "^4.1.0" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/figures@^1.0.13": + version "1.0.13" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.13.tgz#ad0afd62baab1c23175115a9b62f511b6a751e45" + integrity sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw== + +"@inquirer/type@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.8.tgz#efc293ba0ed91e90e6267f1aacc1c70d20b8b4e8" + integrity sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.30": + version "0.3.30" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" + integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@mswjs/interceptors@^0.39.1": + version "0.39.6" + resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.39.6.tgz#44094a578f20da4749d1a0eaf3cdb7973604004b" + integrity sha512-bndDP83naYYkfayr/qhBHMhk0YGwS1iv6vaEGcr0SQbO0IZtbOPqjKjds/WcG+bJA+1T5vCx6kprKOzn5Bg+Vw== + dependencies: + "@open-draft/deferred-promise" "^2.2.0" + "@open-draft/logger" "^0.3.0" + "@open-draft/until" "^2.0.0" + is-node-process "^1.2.0" + outvariant "^1.4.3" + strict-event-emitter "^0.5.1" + +"@napi-rs/wasm-runtime@^0.2.11": + version "0.2.12" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz#3e78a8b96e6c33a6c517e1894efbd5385a7cb6f2" + integrity sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ== + dependencies: + "@emnapi/core" "^1.4.3" + "@emnapi/runtime" "^1.4.3" + "@tybys/wasm-util" "^0.10.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@nolyfill/is-core-module@1.0.39": + version "1.0.39" + resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" + integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== + +"@open-draft/deferred-promise@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz#4a822d10f6f0e316be4d67b4d4f8c9a124b073bd" + integrity sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA== + +"@open-draft/logger@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@open-draft/logger/-/logger-0.3.0.tgz#2b3ab1242b360aa0adb28b85f5d7da1c133a0954" + integrity sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ== + dependencies: + is-node-process "^1.2.0" + outvariant "^1.4.0" + +"@open-draft/until@^2.0.0", "@open-draft/until@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda" + integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@rollup/rollup-android-arm-eabi@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz#7d41dc45adcfcb272504ebcea9c8a5b2c659e963" + integrity sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag== + +"@rollup/rollup-android-arm64@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz#6c708fae2c9755e994c42d56c34a94cb77020650" + integrity sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw== + +"@rollup/rollup-darwin-arm64@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz#85ccf92ab114e434c83037a175923a525635cbb4" + integrity sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw== + +"@rollup/rollup-darwin-x64@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz#0af089f3d658d05573208dabb3a392b44d7f4630" + integrity sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw== + +"@rollup/rollup-freebsd-arm64@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz#46c22a16d18180e99686647543335567221caa9c" + integrity sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA== + +"@rollup/rollup-freebsd-x64@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz#819ffef2f81891c266456952962a13110c8e28b5" + integrity sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz#7fe283c14793e607e653a3214b09f8973f08262a" + integrity sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg== + +"@rollup/rollup-linux-arm-musleabihf@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz#066e92eb22ea30560414ec800a6d119ba0b435ac" + integrity sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw== + +"@rollup/rollup-linux-arm64-gnu@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz#480d518ea99a8d97b2a174c46cd55164f138cc37" + integrity sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw== + +"@rollup/rollup-linux-arm64-musl@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz#ed7db3b8999b60dd20009ddf71c95f3af49423c8" + integrity sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w== + +"@rollup/rollup-linux-loongarch64-gnu@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz#16a6927a35f5dbc505ff874a4e1459610c0c6f46" + integrity sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q== + +"@rollup/rollup-linux-ppc64-gnu@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz#a006700469be0041846c45b494c35754e6a04eea" + integrity sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q== + +"@rollup/rollup-linux-riscv64-gnu@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz#0fcc45b2ec8a0e54218ca48849ea6d596f53649c" + integrity sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ== + +"@rollup/rollup-linux-riscv64-musl@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz#d6e617eec9fe6f5859ee13fad435a16c42b469f2" + integrity sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg== + +"@rollup/rollup-linux-s390x-gnu@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz#b147760d63c6f35b4b18e6a25a2a760dd3ea0c05" + integrity sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg== + +"@rollup/rollup-linux-x64-gnu@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz#fc0be1da374f85e7e85dccaf1ff12d7cfc9fbe3d" + integrity sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA== + +"@rollup/rollup-linux-x64-musl@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz#54c79932e0f9a3c992b034c82325be3bcde0d067" + integrity sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg== + +"@rollup/rollup-openharmony-arm64@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz#fc48e74d413623ac02c1d521bec3e5e784488fdc" + integrity sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA== + +"@rollup/rollup-win32-arm64-msvc@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz#8ce3d1181644406362cf1e62c90e88ab083e02bb" + integrity sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ== + +"@rollup/rollup-win32-ia32-msvc@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz#dd2dfc896eac4b2689d55f01c6d51c249263f805" + integrity sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A== + +"@rollup/rollup-win32-x64-msvc@4.50.1": + version "4.50.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz#13f758c97b9fbbac56b6928547a3ff384e7cfb3e" + integrity sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA== + +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== + +"@tsconfig/node22@^22.0.0": + version "22.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node22/-/node22-22.0.2.tgz#1e04e2c5cc946dac787d69bb502462a851ae51b6" + integrity sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA== + +"@tsconfig/recommended@^1.0.8": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.10.tgz#5ed23fcf8cca7d78a9e3a6e4828cd96cf783994c" + integrity sha512-cGvydvg03lONp5Z9yaplW493Vw9/um7k588mvDkm+VFPF2PZUVPx0uswq4PFpeEySsLbQRETrDRhzh4Dmxaslw== + +"@tybys/wasm-util@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz#2fd3cd754b94b378734ce17058d0507c45c88369" + integrity sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ== + dependencies: + tslib "^2.4.0" + +"@types/chai@^5.2.2": + version "5.2.2" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.2.tgz#6f14cea18180ffc4416bc0fd12be05fdd73bdd6b" + integrity sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg== + dependencies: + "@types/deep-eql" "*" + +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + +"@types/deep-eql@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + +"@types/estree@1.0.8", "@types/estree@^1.0.0", "@types/estree@^1.0.6": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/node@~22": + version "22.18.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.1.tgz#cc85ee6999b2a2928739281d2f56ff410a140c52" + integrity sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw== + dependencies: + undici-types "~6.21.0" + +"@types/statuses@^2.0.4": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.6.tgz#66748315cc9a96d63403baa8671b2c124f8633aa" + integrity sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA== + +"@typescript-eslint/eslint-plugin@^8.32.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz#2172d0496c42eee8c7294b6661681100953fa88f" + integrity sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.42.0" + "@typescript-eslint/type-utils" "8.42.0" + "@typescript-eslint/utils" "8.42.0" + "@typescript-eslint/visitor-keys" "8.42.0" + graphemer "^1.4.0" + ignore "^7.0.0" + natural-compare "^1.4.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/parser@^8.32.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.42.0.tgz#20ea66f4867981fb5bb62cbe1454250fc4a440ab" + integrity sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg== + dependencies: + "@typescript-eslint/scope-manager" "8.42.0" + "@typescript-eslint/types" "8.42.0" + "@typescript-eslint/typescript-estree" "8.42.0" + "@typescript-eslint/visitor-keys" "8.42.0" + debug "^4.3.4" + +"@typescript-eslint/project-service@8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.42.0.tgz#636eb3418b6c42c98554dce884943708bf41a583" + integrity sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.42.0" + "@typescript-eslint/types" "^8.42.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz#36016757bc85b46ea42bae47b61f9421eddedde3" + integrity sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw== + dependencies: + "@typescript-eslint/types" "8.42.0" + "@typescript-eslint/visitor-keys" "8.42.0" + +"@typescript-eslint/tsconfig-utils@8.42.0", "@typescript-eslint/tsconfig-utils@^8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz#21a3e74396fd7443ff930bc41b27789ba7e9236e" + integrity sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ== + +"@typescript-eslint/type-utils@8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz#d6733e7a9fbdf5af60c09c6038dffde13f4e4253" + integrity sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ== + dependencies: + "@typescript-eslint/types" "8.42.0" + "@typescript-eslint/typescript-estree" "8.42.0" + "@typescript-eslint/utils" "8.42.0" + debug "^4.3.4" + ts-api-utils "^2.1.0" + +"@typescript-eslint/types@8.42.0", "@typescript-eslint/types@^8.11.0", "@typescript-eslint/types@^8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.42.0.tgz#ae15c09cebda20473772902033328e87372db008" + integrity sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw== + +"@typescript-eslint/typescript-estree@8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz#593c3af87d4462252c0d7239d1720b84a1b56864" + integrity sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ== + dependencies: + "@typescript-eslint/project-service" "8.42.0" + "@typescript-eslint/tsconfig-utils" "8.42.0" + "@typescript-eslint/types" "8.42.0" + "@typescript-eslint/visitor-keys" "8.42.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/utils@8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.42.0.tgz#95f8e0c697ff2f7da5f72e16135011f878d815c0" + integrity sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g== + dependencies: + "@eslint-community/eslint-utils" "^4.7.0" + "@typescript-eslint/scope-manager" "8.42.0" + "@typescript-eslint/types" "8.42.0" + "@typescript-eslint/typescript-estree" "8.42.0" + +"@typescript-eslint/visitor-keys@8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz#87c6caaa1ac307bc73a87c1fc469f88f0162f27e" + integrity sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ== + dependencies: + "@typescript-eslint/types" "8.42.0" + eslint-visitor-keys "^4.2.1" + +"@ungap/structured-clone@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +"@unrs/resolver-binding-android-arm-eabi@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz#9f5b04503088e6a354295e8ea8fe3cb99e43af81" + integrity sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw== + +"@unrs/resolver-binding-android-arm64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz#7414885431bd7178b989aedc4d25cccb3865bc9f" + integrity sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g== + +"@unrs/resolver-binding-darwin-arm64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz#b4a8556f42171fb9c9f7bac8235045e82aa0cbdf" + integrity sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g== + +"@unrs/resolver-binding-darwin-x64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz#fd4d81257b13f4d1a083890a6a17c00de571f0dc" + integrity sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ== + +"@unrs/resolver-binding-freebsd-x64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz#d2513084d0f37c407757e22f32bd924a78cfd99b" + integrity sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw== + +"@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz#844d2605d057488d77fab09705f2866b86164e0a" + integrity sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw== + +"@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz#204892995cefb6bd1d017d52d097193bc61ddad3" + integrity sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw== + +"@unrs/resolver-binding-linux-arm64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz#023eb0c3aac46066a10be7a3f362e7b34f3bdf9d" + integrity sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ== + +"@unrs/resolver-binding-linux-arm64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz#9e6f9abb06424e3140a60ac996139786f5d99be0" + integrity sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w== + +"@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz#b111417f17c9d1b02efbec8e08398f0c5527bb44" + integrity sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA== + +"@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz#92ffbf02748af3e99873945c9a8a5ead01d508a9" + integrity sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ== + +"@unrs/resolver-binding-linux-riscv64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz#0bec6f1258fc390e6b305e9ff44256cb207de165" + integrity sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew== + +"@unrs/resolver-binding-linux-s390x-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz#577843a084c5952f5906770633ccfb89dac9bc94" + integrity sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg== + +"@unrs/resolver-binding-linux-x64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz#36fb318eebdd690f6da32ac5e0499a76fa881935" + integrity sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w== + +"@unrs/resolver-binding-linux-x64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz#bfb9af75f783f98f6a22c4244214efe4df1853d6" + integrity sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA== + +"@unrs/resolver-binding-wasm32-wasi@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz#752c359dd875684b27429500d88226d7cc72f71d" + integrity sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ== + dependencies: + "@napi-rs/wasm-runtime" "^0.2.11" + +"@unrs/resolver-binding-win32-arm64-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz#ce5735e600e4c2fbb409cd051b3b7da4a399af35" + integrity sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw== + +"@unrs/resolver-binding-win32-ia32-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz#72fc57bc7c64ec5c3de0d64ee0d1810317bc60a6" + integrity sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ== + +"@unrs/resolver-binding-win32-x64-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777" + integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== + +"@vitest/coverage-v8@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz#a2d8d040288c1956a1c7d0a0e2cdcfc7a3319f13" + integrity sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ== + dependencies: + "@ampproject/remapping" "^2.3.0" + "@bcoe/v8-coverage" "^1.0.2" + ast-v8-to-istanbul "^0.3.3" + debug "^4.4.1" + 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.17" + magicast "^0.3.5" + std-env "^3.9.0" + test-exclude "^7.0.1" + tinyrainbow "^2.0.0" + +"@vitest/expect@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433" + integrity sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + tinyrainbow "^2.0.0" + +"@vitest/mocker@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.4.tgz#4471c4efbd62db0d4fa203e65cc6b058a85cabd3" + integrity sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ== + dependencies: + "@vitest/spy" "3.2.4" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4" + integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766" + integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ== + dependencies: + "@vitest/utils" "3.2.4" + pathe "^2.0.3" + strip-literal "^3.0.0" + +"@vitest/snapshot@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c" + integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ== + dependencies: + "@vitest/pretty-format" "3.2.4" + magic-string "^0.30.17" + pathe "^2.0.3" + +"@vitest/spy@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.4.tgz#cc18f26f40f3f028da6620046881f4e4518c2599" + integrity sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw== + dependencies: + tinyspy "^4.0.3" + +"@vitest/utils@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.2.4.tgz#c0813bc42d99527fb8c5b138c7a88516bca46fea" + integrity sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA== + dependencies: + "@vitest/pretty-format" "3.2.4" + loupe "^3.1.4" + tinyrainbow "^2.0.0" + +acorn-jsx@^5.2.0, acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.15.0, acorn@^8.9.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.0.tgz#2f302e7550431b1b7762705fffb52cf1ffa20447" + integrity sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + +array-includes@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" + integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.4" + define-properties "^1.2.1" + es-abstract "^1.24.0" + es-object-atoms "^1.1.1" + get-intrinsic "^1.3.0" + is-string "^1.1.1" + math-intrinsics "^1.1.0" + +array.prototype.findlastindex@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" + integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.4" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-shim-unscopables "^1.1.0" + +array.prototype.flat@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +array.prototype.flatmap@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + +ast-v8-to-istanbul@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.5.tgz#9fba217c272dd8c2615603da5de3e1a460b4b9af" + integrity sha512-9SdXjNheSiE8bALAQCQQuT6fgQaoxJh7IRYrRGZ8/9nv8WhJeC1aXAwN8TbaOssGOukUvyvnkgD9+Yuykvl1aA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.30" + estree-walker "^3.0.3" + js-tokens "^9.0.1" + +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +c12@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/c12/-/c12-2.0.1.tgz#5702d280b31a08abba39833494c9b1202f0f5aec" + integrity sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A== + dependencies: + chokidar "^4.0.1" + confbox "^0.1.7" + defu "^6.1.4" + dotenv "^16.4.5" + giget "^1.2.3" + jiti "^2.3.0" + mlly "^1.7.1" + ohash "^1.1.4" + pathe "^1.1.2" + perfect-debounce "^1.0.0" + pkg-types "^1.2.0" + rc9 "^2.1.2" + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chai@^5.2.0: + version "5.3.3" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.3.3.tgz#dd3da955e270916a4bd3f625f4b919996ada7e06" + integrity sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== + +chokidar@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +citty@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/citty/-/citty-0.1.6.tgz#0f7904da1ed4625e1a9ea7e0fa780981aab7c5e4" + integrity sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ== + dependencies: + consola "^3.2.3" + +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +commander@13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-13.0.0.tgz#1b161f60ee3ceb8074583a0f95359a4f8701845c" + integrity sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ== + +comment-parser@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.1.tgz#bdafead37961ac079be11eb7ec65c4d021eaf9cc" + integrity sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confbox@^0.1.7, confbox@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== + +consola@^3.2.3, consola@^3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== + +cookie@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cross-spawn@^7.0.2, cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0, debug@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +defu@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + +destr@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.5.tgz#7d112ff1b925fb8d2079fac5bdb4a90973b51fdb" + integrity sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA== + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dotenv@^16.4.5: + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== + +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +enhanced-resolve@^5.17.1: + version "5.18.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz#9b5f4c5c076b8787c78fe540392ce76a88855b44" + integrity sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0: + version "1.24.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" + integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-negative-zero "^2.0.3" + is-regex "^1.2.1" + is-set "^2.0.3" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.1" + math-intrinsics "^1.1.0" + object-inspect "^1.13.4" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.4" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + stop-iteration-iterator "^1.1.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.19" + +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" + integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== + dependencies: + hasown "^2.0.2" + +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + +esbuild@^0.25.0: + version "0.25.9" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.9.tgz#15ab8e39ae6cdc64c24ff8a2c0aef5b3fd9fa976" + integrity sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.9" + "@esbuild/android-arm" "0.25.9" + "@esbuild/android-arm64" "0.25.9" + "@esbuild/android-x64" "0.25.9" + "@esbuild/darwin-arm64" "0.25.9" + "@esbuild/darwin-x64" "0.25.9" + "@esbuild/freebsd-arm64" "0.25.9" + "@esbuild/freebsd-x64" "0.25.9" + "@esbuild/linux-arm" "0.25.9" + "@esbuild/linux-arm64" "0.25.9" + "@esbuild/linux-ia32" "0.25.9" + "@esbuild/linux-loong64" "0.25.9" + "@esbuild/linux-mips64el" "0.25.9" + "@esbuild/linux-ppc64" "0.25.9" + "@esbuild/linux-riscv64" "0.25.9" + "@esbuild/linux-s390x" "0.25.9" + "@esbuild/linux-x64" "0.25.9" + "@esbuild/netbsd-arm64" "0.25.9" + "@esbuild/netbsd-x64" "0.25.9" + "@esbuild/openbsd-arm64" "0.25.9" + "@esbuild/openbsd-x64" "0.25.9" + "@esbuild/openharmony-arm64" "0.25.9" + "@esbuild/sunos-x64" "0.25.9" + "@esbuild/win32-arm64" "0.25.9" + "@esbuild/win32-ia32" "0.25.9" + "@esbuild/win32-x64" "0.25.9" + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-compat-utils@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz#7fc92b776d185a70c4070d03fd26fde3d59652e4" + integrity sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q== + dependencies: + semver "^7.5.4" + +eslint-config-prettier@^9.1.0: + version "9.1.2" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz#90deb4fa0259592df774b600dbd1d2249a78ce91" + integrity sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ== + +eslint-import-resolver-alias@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz#297062890e31e4d6651eb5eba9534e1f6e68fc97" + integrity sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w== + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-import-resolver-typescript@^3.7.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz#23dac32efa86a88e2b8232eb244ac499ad636db2" + integrity sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ== + dependencies: + "@nolyfill/is-core-module" "1.0.39" + debug "^4.4.0" + get-tsconfig "^4.10.0" + is-bun-module "^2.0.0" + stable-hash "^0.0.5" + tinyglobby "^0.2.13" + unrs-resolver "^1.6.2" + +eslint-module-utils@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" + integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== + dependencies: + debug "^3.2.7" + +eslint-plugin-es-x@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" + integrity sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.11.0" + eslint-compat-utils "^0.5.1" + +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-import@^2.31.0: + version "2.32.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980" + integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== + dependencies: + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.9" + array.prototype.findlastindex "^1.2.6" + array.prototype.flat "^1.3.3" + array.prototype.flatmap "^1.3.3" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.12.1" + hasown "^2.0.2" + is-core-module "^2.16.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.1" + semver "^6.3.1" + string.prototype.trimend "^1.0.9" + tsconfig-paths "^3.15.0" + +eslint-plugin-jsdoc@^50.6.1: + version "50.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.8.0.tgz#a8d192ccca26df368a2fbaff17c9dddefacd773f" + integrity sha512-UyGb5755LMFWPrZTEqqvTJ3urLz1iqj+bYOHFNag+sw3NvaMWP9K2z+uIn37XfNALmQLQyrBlJ5mkiVPL7ADEg== + dependencies: + "@es-joy/jsdoccomment" "~0.50.2" + are-docs-informative "^0.0.2" + comment-parser "1.4.1" + debug "^4.4.1" + escape-string-regexp "^4.0.0" + espree "^10.3.0" + esquery "^1.6.0" + parse-imports-exports "^0.2.4" + semver "^7.7.2" + spdx-expression-parse "^4.0.0" + +eslint-plugin-n@^17.15.1: + version "17.21.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.21.3.tgz#a07592c28390ac742bf52acae89048c997a7b91c" + integrity sha512-MtxYjDZhMQgsWRm/4xYLL0i2EhusWT7itDxlJ80l1NND2AL2Vi5Mvneqv/ikG9+zpran0VsVRXTEHrpLmUZRNw== + dependencies: + "@eslint-community/eslint-utils" "^4.5.0" + enhanced-resolve "^5.17.1" + eslint-plugin-es-x "^7.8.0" + get-tsconfig "^4.8.1" + globals "^15.11.0" + globrex "^0.1.2" + ignore "^5.3.2" + semver "^7.6.3" + ts-declaration-location "^1.0.6" + +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz#a0652195700aea40b926dc3c74b38e373377bfb0" + integrity sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + +eslint-plugin-sort-keys-fix@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-sort-keys-fix/-/eslint-plugin-sort-keys-fix-1.1.2.tgz#00c8b5791612ec32162b8d7a0563e9c6eb27ec59" + integrity sha512-DNPHFGCA0/hZIsfODbeLZqaGY/+q3vgtshF85r+YWDNCQ2apd9PNs/zL6ttKm0nD1IFwvxyg3YOTI7FHl4unrw== + dependencies: + espree "^6.1.2" + esutils "^2.0.2" + natural-compare "^1.4.0" + requireindex "~1.2.0" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint@^8.57.0: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^10.3.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2, esquery@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +expect-type@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.2.tgz#c030a329fb61184126c8447585bc75a7ec6fbff3" + integrity sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +for-each@^0.3.3, for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + +get-tsconfig@^4.10.0, get-tsconfig@^4.8.1: + version "4.10.1" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.10.1.tgz#d34c1c01f47d65a606c37aa7a177bc3e56ab4b2e" + integrity sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ== + dependencies: + resolve-pkg-maps "^1.0.0" + +giget@^1.2.3: + version "1.2.5" + resolved "https://registry.yarnpkg.com/giget/-/giget-1.2.5.tgz#0bd4909356a0da75cc1f2b33538f93adec0d202f" + integrity sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug== + dependencies: + citty "^0.1.6" + consola "^3.4.0" + defu "^6.1.4" + node-fetch-native "^1.6.6" + nypm "^0.5.4" + pathe "^2.0.3" + tar "^6.2.1" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^10.4.1: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globals@^15.11.0: + version "15.15.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" + integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== + +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +graphql@^16.8.1: + version "16.11.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" + integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== + +handlebars@4.7.8: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-bigints@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +headers-polyfill@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-4.0.3.tgz#922a0155de30ecc1f785bcf04be77844ca95ad07" + integrity sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +ignore@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + +is-async-function@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + dependencies: + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-bun-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd" + integrity sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ== + dependencies: + semver "^7.7.1" + +is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.16.0, is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1, is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-function@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + dependencies: + call-bound "^1.0.3" + get-proto "^1.0.0" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-node-process@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.2.0.tgz#ea02a1b90ddb3934a19aea414e88edef7e11d134" + integrity sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw== + +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2, is-weakref@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + +is-weakset@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + dependencies: + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + dependencies: + "@jridgewell/trace-mapping" "^0.3.23" + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + +istanbul-reports@^3.1.7: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" + integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jiti@^2.3.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.5.1.tgz#bd099c1c2be1c59bbea4e5adcd127363446759d0" + integrity sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w== + +js-tokens@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" + integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsdoc-type-pratt-parser@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz#ff6b4a3f339c34a6c188cbf50a16087858d22113" + integrity sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loupe@^3.1.0, loupe@^3.1.4: + version "3.2.1" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.2.1.tgz#0095cf56dc5b7a9a7c08ff5b1a8796ec8ad17e76" + integrity sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ== + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +magic-string@^0.30.17: + version "0.30.18" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.18.tgz#905bfbbc6aa5692703a93db26a9edcaa0007d2bb" + integrity sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" + source-map-js "^1.2.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mlly@^1.7.1, mlly@^1.7.4: + version "1.8.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.8.0.tgz#e074612b938af8eba1eaf43299cbc89cb72d824e" + integrity sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== + dependencies: + acorn "^8.15.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + ufo "^1.6.1" + +ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +msw@^2.10.4: + version "2.11.1" + resolved "https://registry.yarnpkg.com/msw/-/msw-2.11.1.tgz#77f7c5c60ffd08e4bc351cca4608418db15e5ac2" + integrity sha512-dGSRx0AJmQVQfpGXTsAAq4JFdwdhOBdJ6sJS/jnN0ac3s0NZB6daacHF1z5Pefx+IejmvuiLWw260RlyQOf3sQ== + dependencies: + "@bundled-es-modules/cookie" "^2.0.1" + "@bundled-es-modules/statuses" "^1.0.1" + "@inquirer/confirm" "^5.0.0" + "@mswjs/interceptors" "^0.39.1" + "@open-draft/deferred-promise" "^2.2.0" + "@open-draft/until" "^2.1.0" + "@types/cookie" "^0.6.0" + "@types/statuses" "^2.0.4" + graphql "^16.8.1" + headers-polyfill "^4.0.2" + is-node-process "^1.2.0" + outvariant "^1.4.3" + path-to-regexp "^6.3.0" + picocolors "^1.1.1" + strict-event-emitter "^0.5.1" + tough-cookie "^6.0.0" + type-fest "^4.26.1" + yargs "^17.7.2" + +mute-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" + integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== + +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +napi-postinstall@^0.3.0: + version "0.3.3" + resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.3.tgz#93d045c6b576803ead126711d3093995198c6eb9" + integrity sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-fetch-native@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz#9d09ca63066cc48423211ed4caf5d70075d76a71" + integrity sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== + +nypm@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/nypm/-/nypm-0.5.4.tgz#a5ab0d8d37f96342328479f88ef58699f29b3051" + integrity sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA== + dependencies: + citty "^0.1.6" + consola "^3.4.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + tinyexec "^0.3.2" + ufo "^1.5.4" + +object-inspect@^1.13.3, object-inspect@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + +object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.values@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +ohash@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.6.tgz#9ff7b0271d7076290794537d68ec2b40a60d133e" + integrity sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@10.1.2: + version "10.1.2" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.2.tgz#d5df40984755c9a9c3c93df8156a12467e882925" + integrity sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +outvariant@^1.4.0, outvariant@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.3.tgz#221c1bfc093e8fec7075497e7799fdbf43d14873" + integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA== + +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-imports-exports@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz#e3fb3b5e264cfb55c25b5dfcbe7f410f8dc4e7af" + integrity sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ== + dependencies: + parse-statements "1.0.11" + +parse-statements@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/parse-statements/-/parse-statements-1.0.11.tgz#8787c5d383ae5746568571614be72b0689584344" + integrity sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== + +pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pathe@^2.0.1, pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pathval@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d" + integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ== + +perfect-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +pkg-types@^1.2.0, pkg-types@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== + dependencies: + confbox "^0.1.8" + mlly "^1.7.4" + pathe "^2.0.1" + +possible-typed-array-names@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== + +postcss@^8.5.6: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +rc9@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/rc9/-/rc9-2.1.2.tgz#6282ff638a50caa0a91a31d76af4a0b9cbd1080d" + integrity sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg== + dependencies: + defu "^6.1.4" + destr "^2.0.3" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" + +regexp.prototype.flags@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + +regexpp@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +requireindex@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve@^1.10.1, resolve@^1.22.4: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup@^4.43.0: + version "4.50.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.50.1.tgz#6f0717c34aacc65cc727eeaaaccc2afc4e4485fd" + integrity sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.50.1" + "@rollup/rollup-android-arm64" "4.50.1" + "@rollup/rollup-darwin-arm64" "4.50.1" + "@rollup/rollup-darwin-x64" "4.50.1" + "@rollup/rollup-freebsd-arm64" "4.50.1" + "@rollup/rollup-freebsd-x64" "4.50.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.50.1" + "@rollup/rollup-linux-arm-musleabihf" "4.50.1" + "@rollup/rollup-linux-arm64-gnu" "4.50.1" + "@rollup/rollup-linux-arm64-musl" "4.50.1" + "@rollup/rollup-linux-loongarch64-gnu" "4.50.1" + "@rollup/rollup-linux-ppc64-gnu" "4.50.1" + "@rollup/rollup-linux-riscv64-gnu" "4.50.1" + "@rollup/rollup-linux-riscv64-musl" "4.50.1" + "@rollup/rollup-linux-s390x-gnu" "4.50.1" + "@rollup/rollup-linux-x64-gnu" "4.50.1" + "@rollup/rollup-linux-x64-musl" "4.50.1" + "@rollup/rollup-openharmony-arm64" "4.50.1" + "@rollup/rollup-win32-arm64-msvc" "4.50.1" + "@rollup/rollup-win32-ia32-msvc" "4.50.1" + "@rollup/rollup-win32-x64-msvc" "4.50.1" + fsevents "~2.3.2" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^7.8.2: + version "7.8.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== + dependencies: + tslib "^2.1.0" + +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + +semver@7.7.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1, semver@^7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +semver@^6.1.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +set-function-length@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +source-map-js@^1.2.0, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz#a23af9f3132115465dac215c099303e4ceac5794" + integrity sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.22" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" + integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== + +stable-hash@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.5.tgz#94e8837aaeac5b4d0f631d2972adef2924b40269" + integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA== + +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +statuses@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +std-env@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1" + integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw== + +stop-iteration-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" + +strict-event-emitter@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz#1602ece81c51574ca39c6815e09f1a3e8550bd93" + integrity sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + +string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-literal@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.0.0.tgz#ce9c452a91a0af2876ed1ae4e583539a353df3fc" + integrity sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA== + dependencies: + js-tokens "^9.0.1" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.2.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.3.tgz#4b67b635b2d97578a06a2713d2f04800c237e99b" + integrity sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg== + +tar@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +test-exclude@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" + integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^10.4.1" + minimatch "^9.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinyglobby@^0.2.13, tinyglobby@^0.2.14: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + +tinypool@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591" + integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== + +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.3.tgz#d1d0f0602f4c15f1aae083a34d6d0df3363b1b52" + integrity sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A== + +tldts-core@^7.0.13: + version "7.0.13" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.13.tgz#01e30579c88f299485cebcada440a7a2d51c96f1" + integrity sha512-Td0LeWLgXJGsikI4mO82fRexgPCEyTcwWiXJERF/GBHX3Dm+HQq/wx4HnYowCbiwQ8d+ENLZc+ktbZw8H+0oEA== + +tldts@^7.0.5: + version "7.0.13" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.13.tgz#3f7151b0266fd613930b4dbecd28f95616267fb3" + integrity sha512-z/SgnxiICGb7Gli0z7ci9BZdjy1tQORUbdmzEUA7NbIJKWhdONn78Ji8gV0PAGfHPyEd+I+W2rMzhLjWkv2Olg== + dependencies: + tldts-core "^7.0.13" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tough-cookie@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.0.tgz#11e418b7864a2c0d874702bc8ce0f011261940e5" + integrity sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w== + dependencies: + tldts "^7.0.5" + +ts-api-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + +ts-declaration-location@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz#d4068fe9975828b3b453b3ab112b4711d8267688" + integrity sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA== + dependencies: + picomatch "^4.0.2" + +ts-essentials@>=10.0.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-10.1.1.tgz#4e1d29b7c9b33c1a2744482376634c4fafba5210" + integrity sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw== + +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.1.0, tslib@^2.4.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^4.26.1: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + +typescript@^5.6.2: + version "5.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" + integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== + +ufo@^1.5.4, ufo@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.1.tgz#ac2db1d54614d1b22c1d603e3aef44a85d8f146b" + integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== + +uglify-js@^3.1.4: + version "3.19.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== + +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +unrs-resolver@^1.6.2: + version "1.11.1" + resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9" + integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== + dependencies: + napi-postinstall "^0.3.0" + optionalDependencies: + "@unrs/resolver-binding-android-arm-eabi" "1.11.1" + "@unrs/resolver-binding-android-arm64" "1.11.1" + "@unrs/resolver-binding-darwin-arm64" "1.11.1" + "@unrs/resolver-binding-darwin-x64" "1.11.1" + "@unrs/resolver-binding-freebsd-x64" "1.11.1" + "@unrs/resolver-binding-linux-arm-gnueabihf" "1.11.1" + "@unrs/resolver-binding-linux-arm-musleabihf" "1.11.1" + "@unrs/resolver-binding-linux-arm64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-arm64-musl" "1.11.1" + "@unrs/resolver-binding-linux-ppc64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-riscv64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-riscv64-musl" "1.11.1" + "@unrs/resolver-binding-linux-s390x-gnu" "1.11.1" + "@unrs/resolver-binding-linux-x64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-x64-musl" "1.11.1" + "@unrs/resolver-binding-wasm32-wasi" "1.11.1" + "@unrs/resolver-binding-win32-arm64-msvc" "1.11.1" + "@unrs/resolver-binding-win32-ia32-msvc" "1.11.1" + "@unrs/resolver-binding-win32-x64-msvc" "1.11.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +vite-node@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07" + integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg== + dependencies: + cac "^6.7.14" + debug "^4.4.1" + es-module-lexer "^1.7.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + +"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0": + version "7.1.4" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.4.tgz#354944affb55e1aff0157406b74e0d0a3232df9a" + integrity sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw== + dependencies: + esbuild "^0.25.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.14" + optionalDependencies: + fsevents "~2.3.3" + +vitest-mock-extended@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/vitest-mock-extended/-/vitest-mock-extended-3.1.0.tgz#d4e23f4bbc18d07c4ab928b861e74414c942c974" + integrity sha512-vCM0VkuocOUBwwqwV7JB7YStw07pqeKvEIrZnR8l3PtwYi6rAAJAyJACeC1UYNfbQWi85nz7EdiXWBFI5hll2g== + dependencies: + ts-essentials ">=10.0.0" + +vitest@^3.1.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea" + integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/expect" "3.2.4" + "@vitest/mocker" "3.2.4" + "@vitest/pretty-format" "^3.2.4" + "@vitest/runner" "3.2.4" + "@vitest/snapshot" "3.2.4" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + debug "^4.4.1" + expect-type "^1.2.1" + magic-string "^0.30.17" + pathe "^2.0.3" + picomatch "^4.0.2" + std-env "^3.9.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.14" + tinypool "^1.1.1" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node "3.2.4" + why-is-node-running "^2.3.0" + +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" + +which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yoctocolors-cjs@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" + integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== + +zod-to-json-schema@^3.24.6: + version "3.24.6" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz#5920f020c4d2647edfbb954fa036082b92c9e12d" + integrity sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg== + +zod@^3.23.8: + version "3.25.76" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==