diff --git a/packages/openapi-ts/package.json b/packages/openapi-ts/package.json index 6673a7c18..55a521af4 100644 --- a/packages/openapi-ts/package.json +++ b/packages/openapi-ts/package.json @@ -112,6 +112,7 @@ "express": "4.21.0", "fastify": "5.2.0", "glob": "10.4.3", + "msw": "2.7.0", "node-fetch": "3.3.2", "nuxt": "3.15.1", "prettier": "3.4.2", diff --git a/packages/openapi-ts/src/ir/operation.ts b/packages/openapi-ts/src/ir/operation.ts index 17d52a769..84f14a272 100644 --- a/packages/openapi-ts/src/ir/operation.ts +++ b/packages/openapi-ts/src/ir/operation.ts @@ -97,6 +97,7 @@ interface OperationResponsesMap { responses?: IR.SchemaObject; } +// TODO: handle multiple content types export const operationResponsesMap = ( operation: IR.OperationObject, ): OperationResponsesMap => { diff --git a/packages/openapi-ts/src/openApi/shared/types/schema.d.ts b/packages/openapi-ts/src/openApi/shared/types/schema.d.ts index 5a9883043..3b4fbe168 100644 --- a/packages/openapi-ts/src/openApi/shared/types/schema.d.ts +++ b/packages/openapi-ts/src/openApi/shared/types/schema.d.ts @@ -20,6 +20,11 @@ export type SchemaWithRequired< [P in K]-?: S[P]; }; +export interface SchemaWithType['type']> + extends Omit { + type: Extract['type'], T>; +} + export type SchemaType< S extends { type?: unknown; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts index 074b5e71a..8b0a46b1e 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts @@ -5,6 +5,7 @@ import { compiler } from '../../../compiler'; import { operationResponsesMap } from '../../../ir/operation'; import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; +import type { SchemaWithType } from '../../../openApi/shared/types/schema'; import { escapeComment } from '../../../utils/escape'; import { irRef, isRefOpenApiComponent } from '../../../utils/ref'; import { numberRegExp } from '../../../utils/regexp'; @@ -15,11 +16,6 @@ import type { Plugin } from '../../types'; import { typesId } from './ref'; import type { Config } from './types'; -interface SchemaWithType['type']> - extends Omit { - type: Extract['type'], T>; -} - const parseSchemaJsDoc = ({ schema }: { schema: IR.SchemaObject }) => { const comments = [ schema.description && escapeComment(schema.description), diff --git a/packages/openapi-ts/src/plugins/index.ts b/packages/openapi-ts/src/plugins/index.ts index 63985a69b..db42b6565 100644 --- a/packages/openapi-ts/src/plugins/index.ts +++ b/packages/openapi-ts/src/plugins/index.ts @@ -35,6 +35,7 @@ import { defaultConfig as tanStackVueQuery, } from './@tanstack/vue-query'; import { type Config as Fastify, defaultConfig as fastify } from './fastify'; +import { type Config as Msw, defaultConfig as msw } from './msw'; import type { DefaultPluginConfigs, Plugin } from './types'; import { type Config as Zod, defaultConfig as zod } from './zod'; @@ -52,6 +53,7 @@ export type UserPlugins = | Plugin.UserConfig | Plugin.UserConfig | Plugin.UserConfig + | Plugin.UserConfig | Plugin.UserConfig; /** @@ -68,6 +70,7 @@ export type ClientPlugins = | Plugin.Config | Plugin.Config | Plugin.Config + | Plugin.Config | Plugin.Config; export const defaultPluginConfigs: DefaultPluginConfigs = { @@ -81,5 +84,6 @@ export const defaultPluginConfigs: DefaultPluginConfigs = { '@tanstack/svelte-query': tanStackSvelteQuery, '@tanstack/vue-query': tanStackVueQuery, fastify, + msw, zod, }; diff --git a/packages/openapi-ts/src/plugins/msw/config.ts b/packages/openapi-ts/src/plugins/msw/config.ts new file mode 100644 index 000000000..cc5320a81 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/config.ts @@ -0,0 +1,18 @@ +import type { Plugin } from '../types'; +import { handler } from './plugin'; +import type { Config } from './types'; + +export const defaultConfig: Plugin.Config = { + _handler: handler, + _handlerLegacy: () => {}, + name: 'msw', + output: 'msw', +}; + +/** + * Type helper for MSW plugin, returns {@link Plugin.Config} object + */ +export const defineConfig: Plugin.DefineConfig = (config) => ({ + ...defaultConfig, + ...config, +}); diff --git a/packages/openapi-ts/src/plugins/msw/index.ts b/packages/openapi-ts/src/plugins/msw/index.ts new file mode 100644 index 000000000..3a85a1be8 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/index.ts @@ -0,0 +1,2 @@ +export { defaultConfig, defineConfig } from './config'; +export type { Config } from './types'; diff --git a/packages/openapi-ts/src/plugins/msw/interfaces.ts b/packages/openapi-ts/src/plugins/msw/interfaces.ts new file mode 100644 index 000000000..317a818c2 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/interfaces.ts @@ -0,0 +1,126 @@ +import ts from 'typescript'; + +import { compiler } from '../../compiler'; + +const objectReference = compiler.typeReferenceNode({ typeName: 'object' }); +const tReference = compiler.typeReferenceNode({ typeName: 'T' }); +const uReference = compiler.typeReferenceNode({ typeName: 'U' }); +export const undefinedReference = compiler.typeReferenceNode({ + typeName: 'undefined', +}); +export const neverReference = compiler.typeReferenceNode({ typeName: 'never' }); + +export const pathParamsTypeName = 'PathParams'; +export const responseBodyTypeTypeName = 'ResponseBodyType'; +const transformObjectTypeName = 'TransformObject'; + +export const createTransformObjectType = () => { + const tkType = compiler.indexedAccessTypeNode({ + indexType: compiler.typeReferenceNode({ typeName: 'K' }), + objectType: tReference, + }); + const nullType = compiler.literalTypeNode({ literal: compiler.null() }); + const type = compiler.typeAliasDeclaration({ + name: transformObjectTypeName, + type: compiler.mappedTypeNode({ + type: ts.factory.createConditionalTypeNode( + tkType, + objectReference, + compiler.typeReferenceNode({ + typeArguments: [tkType], + typeName: transformObjectTypeName, + }), + compiler.typeUnionNode({ + types: [ + compiler.typeReferenceNode({ typeName: 'string' }), + ts.factory.createConditionalTypeNode( + nullType, + tkType, + undefinedReference, + neverReference, + ), + ], + }), + ), + typeParameter: compiler.typeParameterDeclaration({ + constraint: compiler.typeOperatorNode({ + operator: 'keyof', + type: tReference, + }), + name: 'K', + }), + }), + typeParameters: [ + compiler.typeParameterDeclaration({ + name: 'T', + }), + ], + }); + return type; +}; + +export const createPathParamsType = () => { + const type = compiler.typeAliasDeclaration({ + name: pathParamsTypeName, + type: ts.factory.createConditionalTypeNode( + tReference, + ts.factory.createTypeLiteralNode([ + ts.factory.createPropertySignature( + undefined, + ts.factory.createIdentifier('path'), + ts.factory.createToken(ts.SyntaxKind.QuestionToken), + ts.factory.createInferTypeNode( + compiler.typeParameterDeclaration({ + name: 'U', + }), + ), + ), + ]), + ts.factory.createConditionalTypeNode( + uReference, + objectReference, + compiler.typeReferenceNode({ + typeArguments: [uReference], + typeName: transformObjectTypeName, + }), + neverReference, + ), + neverReference, + ), + typeParameters: [ + compiler.typeParameterDeclaration({ + name: 'T', + }), + ], + }); + return type; +}; + +export const createResponseBodyTypeType = () => { + const type = compiler.typeAliasDeclaration({ + name: responseBodyTypeTypeName, + type: ts.factory.createConditionalTypeNode( + compiler.typeReferenceNode({ typeName: 'void' }), + tReference, + compiler.typeUnionNode({ + types: [ + compiler.typeReferenceNode({ + typeArguments: [ + tReference, + compiler.keywordTypeNode({ keyword: 'void' }), + ], + typeName: 'Exclude', + }), + undefinedReference, + ], + }), + tReference, + ), + typeParameters: [ + compiler.typeParameterDeclaration({ + name: 'T', + }), + ], + }); + return type; +}; diff --git a/packages/openapi-ts/src/plugins/msw/plugin.ts b/packages/openapi-ts/src/plugins/msw/plugin.ts new file mode 100644 index 000000000..1d99e0c62 --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/plugin.ts @@ -0,0 +1,200 @@ +import type ts from 'typescript'; + +import { compiler } from '../../compiler'; +import type { Identifier } from '../../generate/files'; +import type { IR } from '../../ir/types'; +import { irRef } from '../../utils/ref'; +import { stringCase } from '../../utils/stringCase'; +import { + importIdentifierData, + importIdentifierResponse, +} from '../@hey-api/typescript/ref'; +import type { Plugin } from '../types'; +import { + createPathParamsType, + createResponseBodyTypeType, + createTransformObjectType, + neverReference, + pathParamsTypeName, + responseBodyTypeTypeName, + undefinedReference, +} from './interfaces'; +import { getSuccessResponse, responseOptions } from './response'; +import { schemaToExpression } from './schema'; +import type { Config } from './types'; + +export const mswId = 'msw'; + +const nameTransformer = (name: string) => `${name}-handler`; + +const resolverStatements = ({ + context, + identifierResponse, + operation, +}: { + context: IR.Context; + identifierResponse: Identifier; + operation: IR.OperationObject; +}) => { + const statements: Array = []; + + const successResponse = getSuccessResponse(operation) + + const response = compiler.constVariable({ + expression: compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: 'HttpResponse', + name: compiler.identifier({ text: 'json' }), + }), + parameters: [ + identifierResponse.name && successResponse?.schema ? schemaToExpression({ + context, + schema: successResponse.schema, + }) : compiler.identifier({ text: 'undefined' }), + responseOptions({ + responseSchema: successResponse, + }), + ], + types: [ + identifierResponse.name ? compiler.typeReferenceNode({ + typeArguments: [ + compiler.typeReferenceNode({ + typeName: identifierResponse.name, + }), + ], + typeName: responseBodyTypeTypeName, + }) : undefinedReference, + ], + }), + name: 'response', + }); + statements.push(response); + + statements.push( + compiler.returnStatement({ + expression: compiler.identifier({ text: 'response' }), + }), + ); + + return statements; +}; + +const operationToMswHandler = ({ + context, + operation, +}: { + context: IR.Context; + operation: IR.OperationObject; +}) => { + const file = context.file({ id: mswId })!; + + const identifier = file.identifier({ + $ref: `${irRef}${stringCase({ + case: 'camelCase', + value: operation.id, + })}`, + create: true, + nameTransformer, + namespace: 'value', + }); + + if (!identifier.name) { + return; + } + + const identifierData = importIdentifierData({ context, file, operation }); + const identifierResponse = importIdentifierResponse({ + context, + file, + operation, + }); + + // TODO: add support for baseUrl/servers + // replace our curly brackets with colon + const handlerPath = `*${operation.path.replace(/\{(.+?)\}/g, ':$1')}`; + + const statement = compiler.constVariable({ + exportConst: true, + expression: compiler.callExpression({ + functionName: compiler.propertyAccessExpression({ + expression: compiler.identifier({ text: 'http' }), + name: operation.method, + }), + parameters: [ + compiler.ots.string(handlerPath), + compiler.arrowFunction({ + async: true, + parameters: [ + // TODO: use cookies, params, request, requestId + ], + statements: resolverStatements({ + context, + identifierResponse, + operation, + }), + }), + ], + types: [ + identifierData.name + ? compiler.typeReferenceNode({ + typeArguments: [ + compiler.typeReferenceNode({ typeName: identifierData.name }), + ], + typeName: pathParamsTypeName, + }) + : neverReference, + identifierData.name + ? compiler.indexedAccessTypeNode({ + indexType: compiler.literalTypeNode({ + literal: compiler.stringLiteral({ text: 'body' }), + }), + objectType: compiler.typeReferenceNode({ + typeName: identifierData.name, + }), + }) + : neverReference, + identifierResponse.name + ? compiler.typeReferenceNode({ + typeArguments: [ + compiler.typeReferenceNode({ + typeName: identifierResponse.name, + }), + ], + typeName: responseBodyTypeTypeName, + }) + : undefinedReference, + ], + }), + name: identifier.name, + }); + file.add(statement); +}; + +export const handler: Plugin.Handler = ({ context, plugin }) => { + const file = context.createFile({ + exportFromIndex: plugin.exportFromIndex, + id: mswId, + identifierCase: 'camelCase', + path: plugin.output, + }); + + file.import({ + module: 'msw', + name: 'http', + }); + file.import({ + module: 'msw', + name: 'HttpResponse', + }); + + file.add(createTransformObjectType()); + file.add(createPathParamsType()); + file.add(createResponseBodyTypeType()); + + context.subscribe('operation', ({ operation }) => { + operationToMswHandler({ + context, + operation, + }); + }); +}; diff --git a/packages/openapi-ts/src/plugins/msw/response.ts b/packages/openapi-ts/src/plugins/msw/response.ts new file mode 100644 index 000000000..0bad68d0e --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/response.ts @@ -0,0 +1,46 @@ +import { compiler } from "../../compiler"; +import { operationResponsesMap } from "../../ir/operation"; +import type { IR } from "../../ir/types"; + +interface ResponseSchema { + schema: IR.SchemaObject | undefined; + statusCode: string; +} + +export const getSuccessResponse = (operation: IR.OperationObject): ResponseSchema | undefined => { + const { responses } = operationResponsesMap(operation); + if (!responses?.properties) { + return; + } + const statusCode = Object.keys(responses.properties)[0] + if (!statusCode) { + return; + } + return { + schema: responses.properties[statusCode], + statusCode, + } +} + +const getResponseStatus = (statusCode: string | undefined): number => { + if (!statusCode || statusCode === '2XX') { + return 200; + } + return Number.parseInt(statusCode, 10) +} + +export const responseOptions = ({ + responseSchema, +}: { + responseSchema: ResponseSchema | undefined; +}) => { + const status = getResponseStatus(responseSchema?.statusCode) + return compiler.objectExpression({ + obj: [ + { + key: 'status', + value: status, + }, + ], + }) +} diff --git a/packages/openapi-ts/src/plugins/msw/schema.ts b/packages/openapi-ts/src/plugins/msw/schema.ts new file mode 100644 index 000000000..6dd75538a --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/schema.ts @@ -0,0 +1,143 @@ +import type ts from "typescript"; + +import { compiler } from "../../compiler"; +import type { IR } from "../../ir/types" +import type { SchemaWithRequired, SchemaWithType } from "../../openApi/shared/types/schema"; + +const arrayTypeToExpression = ({ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + schema, +}: { + context: IR.Context; + schema: SchemaWithType<'array'>; +}) => { + // TODO: inject no/a few items based on argument + const elements: Array = []; + return compiler.arrayLiteralExpression({ + elements, + }) +} + +const objectTypeToExpression = ({ + context, + schema, +}: { + context: IR.Context; + schema: SchemaWithType<'object'>; +}) => { + const keys: Array = []; + for (const name in schema.properties) { + const property = schema.properties[name]! + const expression = schemaToExpression({ + context, + schema: property, + }) + keys.push({ + key: name, + value: expression, + }) + } + return compiler.objectExpression({ + obj: keys, + }) +} + +const stringTypeToExpression = ({ + schema, +}: { + context: IR.Context; + schema: SchemaWithType<'string'>; +}) => { + // TODO: handle enums, need to either set a random value or import enum if TypeScript + if (typeof schema.const === 'string') { + return compiler.ots.string(schema.const); + } + return compiler.ots.string(''); +} + +// TODO: use faker + examples +const schemaTypeToExpression = ({ + context, + schema, +}: { + context: IR.Context; + schema: SchemaWithRequired +}) => { + // TODO: handle enum and tuple + switch (schema.type) { + // TODO: handle array + case 'array': + return arrayTypeToExpression({ + context, + schema: schema as SchemaWithType<'array'>, + }) + case 'boolean': + return compiler.ots.boolean(true); + case 'integer': + case 'number': + return compiler.ots.number(0); + case 'null': + return compiler.null() + // TODO: handle object + case 'object': + return objectTypeToExpression({ + context, + schema: schema as SchemaWithType<'object'>, + }) + case 'string': + return stringTypeToExpression({ + context, + schema: schema as SchemaWithType<'string'>, + }) + case 'never': + case 'undefined': + case 'unknown': + case 'void': + default: + return compiler.identifier({ text: 'undefined' }) + } +} + +export const schemaToExpression = ({ + context, + schema, +}: { + context: IR.Context; + schema: IR.SchemaObject; +}) => { + let expression: ts.Expression; + + if (schema.$ref) { + const ref = context.resolveIrRef(schema.$ref); + expression = schemaToExpression({ + context, + schema: ref, + }) + } else if (schema.type) { + expression = schemaTypeToExpression({ + context, + schema: schema as SchemaWithRequired, + }) + } else if (schema.items) { + if (schema.logicalOperator === 'or' && schema.items[0]) { + expression = schemaToExpression({ + context, + schema: schema.items[0], + }) + } else { + console.warn( + '🚨', + `unhandled schema items with logical operator "${schema.logicalOperator}"`, + JSON.stringify(schema), + ); + } + } else { + console.warn( + '🚨', + 'unhandled schema', + JSON.stringify(schema), + ); + } + + return expression!; +} diff --git a/packages/openapi-ts/src/plugins/msw/types.d.ts b/packages/openapi-ts/src/plugins/msw/types.d.ts new file mode 100644 index 000000000..d1e6fcd8c --- /dev/null +++ b/packages/openapi-ts/src/plugins/msw/types.d.ts @@ -0,0 +1,10 @@ +import type { Plugin } from '../types'; + +export interface Config extends Plugin.Name<'msw'> { + /** + * Name of the generated file. + * + * @default 'msw' + */ + output?: string; +} diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 396b33364..11a2d5cec 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -19,6 +19,7 @@ export type PluginNames = | '@tanstack/svelte-query' | '@tanstack/vue-query' | 'fastify' + | 'msw' | 'zod'; export type AnyPluginName = PluginNames | (string & {}); diff --git a/packages/openapi-ts/src/plugins/zod/plugin.ts b/packages/openapi-ts/src/plugins/zod/plugin.ts index 9c5321ee2..efea067f1 100644 --- a/packages/openapi-ts/src/plugins/zod/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/plugin.ts @@ -4,16 +4,12 @@ import { compiler } from '../../compiler'; import { operationResponsesMap } from '../../ir/operation'; import { deduplicateSchema } from '../../ir/schema'; import type { IR } from '../../ir/types'; +import type { SchemaWithType } from '../../openApi/shared/types/schema'; import { numberRegExp } from '../../utils/regexp'; import { operationIrRef } from '../shared/utils/ref'; import type { Plugin } from '../types'; import type { Config } from './types'; -interface SchemaWithType['type']> - extends Omit { - type: Extract['type'], T>; -} - interface Result { circularReferenceTracker: Set; hasCircularReference: boolean; diff --git a/packages/openapi-ts/test/openapi-ts.config.ts b/packages/openapi-ts/test/openapi-ts.config.ts index cb450d46f..bfedcdf80 100644 --- a/packages/openapi-ts/test/openapi-ts.config.ts +++ b/packages/openapi-ts/test/openapi-ts.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ // exclude: '^#/components/schemas/ModelWithCircularReference$', // include: // '^(#/components/schemas/import|#/paths/api/v{api-version}/simple/options)$', - path: './packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml', + path: './packages/openapi-ts/test/spec/3.1.x/full.json', // path: './test/spec/v3-transforms.json', // path: 'https://mongodb-mms-prod-build-server.s3.amazonaws.com/openapi/2caffd88277a4e27c95dcefc7e3b6a63a3b03297-v2-2023-11-15.json', // path: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', @@ -55,9 +55,9 @@ export default defineConfig({ }, // @ts-ignore { - enums: 'typescript', + // enums: 'typescript', // enums: 'typescript+namespace', - // enums: 'javascript', + enums: 'javascript', enumsCase: 'camelCase', // exportInlineEnums: true, // identifierCase: 'preserve', @@ -73,6 +73,10 @@ export default defineConfig({ // name: '@tanstack/react-query', }, // @ts-ignore + { + name: 'msw', + }, + // @ts-ignore { // name: 'zod', }, diff --git a/packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml b/packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml deleted file mode 100644 index 4af61c5fc..000000000 --- a/packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml +++ /dev/null @@ -1,25 +0,0 @@ -openapi: 3.1.1 -info: - title: OpenAPI 3.1.1 body nested array example - version: 1 -paths: - /foo: - post: - requestBody: - content: - 'multipart/form-data': - schema: - properties: - foo: - items: - items: - type: integer - type: array - type: array - required: - - foo - type: object - required: true - responses: - '200': - description: OK diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00c996ffb..e7cffb320 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -796,6 +796,9 @@ importers: glob: specifier: 10.4.3 version: 10.4.3 + msw: + specifier: 2.7.0 + version: 2.7.0(@types/node@22.10.5)(typescript@5.5.3) node-fetch: specifier: 3.3.2 version: 3.3.2 @@ -1676,6 +1679,15 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + '@changesets/apply-release-plan@7.0.7': resolution: {integrity: sha512-qnPOcmmmnD0MfMg9DjU1/onORFyRpDXkMMl2IJg9mECY6RnxL3wN0TCCc92b2sXt1jt8DgjAUUsZYGUGTdYIXA==} @@ -2672,6 +2684,10 @@ packages: cpu: [x64] os: [win32] + '@mswjs/interceptors@0.37.5': + resolution: {integrity: sha512-AAwRb5vXFcY4L+FvZ7LZusDuZ0vEe0Zm8ohn1FM6/X7A3bj4mqmkAcGRWuvC2JwSygNwHAAmMnAI73vPHeqsHA==} + engines: {node: '>=18'} + '@napi-rs/nice-android-arm-eabi@1.0.1': resolution: {integrity: sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==} engines: {node: '>= 10'} @@ -2920,6 +2936,15 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@parcel/watcher-android-arm64@2.5.0': resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==} engines: {node: '>= 10.0.0'} @@ -4215,6 +4240,9 @@ packages: '@types/sockjs@0.3.36': resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + '@types/statuses@2.0.5': + resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -6430,6 +6458,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql@16.10.0: + resolution: {integrity: sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gzip-size@7.0.0: resolution: {integrity: sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6474,6 +6506,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -6746,6 +6781,9 @@ packages: resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==} engines: {node: '>=16'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -7489,6 +7527,16 @@ packages: msgpackr@1.11.2: resolution: {integrity: sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==} + msw@2.7.0: + resolution: {integrity: sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} @@ -7802,6 +7850,9 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -7958,6 +8009,9 @@ packages: path-to-regexp@0.1.10: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -9133,6 +9187,9 @@ packages: streamx@2.21.1: resolution: {integrity: sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -11732,6 +11789,19 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@bundled-es-modules/cookie@2.0.1': + dependencies: + cookie: 0.7.2 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.1 + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + '@changesets/apply-release-plan@7.0.7': dependencies: '@changesets/config': 3.0.5 @@ -12605,6 +12675,15 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': optional: true + '@mswjs/interceptors@0.37.5': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + '@napi-rs/nice-android-arm-eabi@1.0.1': optional: true @@ -13166,6 +13245,15 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + '@parcel/watcher-android-arm64@2.5.0': optional: true @@ -14492,6 +14580,8 @@ snapshots: dependencies: '@types/node': 22.10.5 + '@types/statuses@2.0.5': {} + '@types/tough-cookie@4.0.5': {} '@types/unist@3.0.3': {} @@ -17236,6 +17326,8 @@ snapshots: graphemer@1.4.0: {} + graphql@16.10.0: {} + gzip-size@7.0.0: dependencies: duplexer: 0.1.2 @@ -17309,6 +17401,8 @@ snapshots: he@1.2.0: {} + headers-polyfill@4.0.3: {} + highlight.js@10.7.3: {} hookable@5.5.3: {} @@ -17567,6 +17661,8 @@ snapshots: is-network-error@1.1.0: {} + is-node-process@1.2.0: {} + is-number@7.0.0: {} is-path-inside@4.0.0: {} @@ -18383,6 +18479,31 @@ snapshots: msgpackr-extract: 3.0.3 optional: true + msw@2.7.0(@types/node@22.10.5)(typescript@5.5.3): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.1(@types/node@22.10.5) + '@mswjs/interceptors': 0.37.5 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + graphql: 16.10.0 + headers-polyfill: 4.0.3 + 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 + type-fest: 4.32.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - '@types/node' + muggle-string@0.4.1: {} multicast-dns@7.2.5: @@ -19262,6 +19383,8 @@ snapshots: outdent@0.5.0: {} + outvariant@1.4.3: {} + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -19433,6 +19556,8 @@ snapshots: path-to-regexp@0.1.10: {} + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} path-type@5.0.0: {} @@ -20714,6 +20839,8 @@ snapshots: optionalDependencies: bare-events: 2.5.3 + strict-event-emitter@0.5.1: {} + string-argv@0.3.2: {} string-width@4.2.3: