diff --git a/packages/openapi-ts-tests/main/package.json b/packages/openapi-ts-tests/main/package.json index ce2b69b12f..8c640e2273 100644 --- a/packages/openapi-ts-tests/main/package.json +++ b/packages/openapi-ts-tests/main/package.json @@ -31,6 +31,8 @@ "@hey-api/custom-client": "workspace:*", "@hey-api/openapi-ts": "workspace:*", "@pinia/colada": "0.17.7", + "@nestjs/common": "11.1.6", + "@nestjs/core": "11.1.6", "@tanstack/angular-query-experimental": "5.73.3", "@tanstack/react-query": "5.73.3", "@tanstack/solid-query": "5.73.3", diff --git a/packages/openapi-ts-tests/main/test/clients.test.ts b/packages/openapi-ts-tests/main/test/clients.test.ts index 118a0f1b9e..b9f9d81c30 100644 --- a/packages/openapi-ts-tests/main/test/clients.test.ts +++ b/packages/openapi-ts-tests/main/test/clients.test.ts @@ -510,3 +510,161 @@ describe('my-client', () => { ); }); }); + +describe.only('client-nestjs', () => { + const namespace = 'clients'; + + const outputDir = path.join( + __dirname, + 'generated', + '3.1.x', + namespace, + 'client-nestjs', + ); + + const createConfig = ( + userConfig: Omit & Pick, 'input'>, + ): UserConfig => ({ + ...userConfig, + input: path.join(getSpecsPath(), '3.1.x', 'full.yaml'), + logs: { + level: 'silent', + }, + output: + typeof userConfig.output === 'string' + ? path.join(outputDir, userConfig.output) + : { + ...userConfig.output, + path: path.join(outputDir, userConfig.output.path), + }, + }); + + const scenarios = [ + { + config: createConfig({ + output: 'custom-client-name', + plugins: [ + { + clientName: 'MyApi', + name: '@hey-api/client-nestjs', + }, + ], + }), + description: 'custom client name', + }, + { + config: createConfig({ + output: 'custom-module-name', + plugins: [ + { + clientName: 'CustomClient', + moduleName: 'MyCustomModule', + name: '@hey-api/client-nestjs', + }, + ], + }), + description: 'custom module name', + }, + { + config: createConfig({ + output: 'custom-client-class-name', + plugins: [ + { + clientClassName: 'MyHttpClient', + clientName: 'TestApi', + name: '@hey-api/client-nestjs', + }, + ], + }), + description: 'custom client class name', + }, + { + config: createConfig({ + output: 'throw-on-error-true', + plugins: [ + { + name: '@hey-api/client-nestjs', + throwOnError: true, + }, + ], + }), + description: 'throw on error enabled', + }, + { + config: createConfig({ + output: 'throw-on-error-false', + plugins: [ + { + name: '@hey-api/client-nestjs', + throwOnError: false, + }, + ], + }), + description: 'throw on error disabled', + }, + { + config: createConfig({ + output: 'bundled', + plugins: [ + { + bundle: true, + name: '@hey-api/client-nestjs', + }, + ], + }), + description: 'bundled client', + }, + { + config: createConfig({ + output: 'no-bundle', + plugins: [ + { + bundle: false, + name: '@hey-api/client-nestjs', + }, + ], + }), + description: 'client without bundle', + }, + { + config: createConfig({ + output: 'custom-config-combined', + plugins: [ + { + clientClassName: 'SuperClient', + clientName: 'AdvancedApi', + moduleName: 'AdvancedApiModule', + name: '@hey-api/client-nestjs', + throwOnError: true, + }, + ], + }), + description: 'combined custom configuration', + }, + ]; + + it.each(scenarios)('$description', async ({ config }) => { + await createClient(config); + + const outputPath = + typeof config.output === 'string' ? config.output : config.output.path; + const filePaths = getFilePaths(outputPath); + + await Promise.all( + filePaths.map(async (filePath) => { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + + await expect(fileContent).toMatchFileSnapshot( + path.join( + __dirname, + '__snapshots__', + '3.1.x', + namespace, + 'client-nestjs', + filePath.slice(outputDir.length + 1), + ), + ); + }), + ); + }); +}); diff --git a/packages/openapi-ts/package.json b/packages/openapi-ts/package.json index e2b1c0a6b0..38db225413 100644 --- a/packages/openapi-ts/package.json +++ b/packages/openapi-ts/package.json @@ -110,6 +110,7 @@ "@angular/platform-browser-dynamic": "19.2.15", "@angular/router": "19.2.15", "@config/vite-base": "workspace:*", + "@nestjs/common": "11.1.6", "@types/bun": "1.3.1", "@types/cross-spawn": "6.0.6", "@types/semver": "7.7.1", diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts index 98e6676278..98eacb7e0a 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts @@ -5,6 +5,7 @@ import type { HeyApiClientFetchPlugin } from '~/plugins/@hey-api/client-fetch'; import type { HeyApiClientNextPlugin } from '~/plugins/@hey-api/client-next'; import type { HeyApiClientNuxtPlugin } from '~/plugins/@hey-api/client-nuxt'; import type { HeyApiClientOfetchPlugin } from '~/plugins/@hey-api/client-ofetch'; +import type { HeyApiClientNestjsPlugin } from '~/plugins/@hey-api/client-nestjs'; export interface PluginHandler { (...args: Parameters): void; @@ -13,6 +14,7 @@ export interface PluginHandler { (...args: Parameters): void; (...args: Parameters): void; (...args: Parameters): void; + (...args: Parameters): void; } /** diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/__tests__/nestjs-generator.test.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/__tests__/nestjs-generator.test.ts new file mode 100644 index 0000000000..a6077398d9 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/__tests__/nestjs-generator.test.ts @@ -0,0 +1,814 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { generateNestjsClient } from '../nestjs-generator'; +import { + createMockContextWithOperations, + createMockOperations, + createMockPlugin, +} from './test-helpers'; + +describe('nestjs-generator', () => { + describe('generateNestjsClient', () => { + it('should generate client with all components in single file', () => { + const plugin = createMockPlugin( + { clientName: 'Api' }, + createMockContextWithOperations(), + ); + + // Mock the forEach method to yield operations + const operations = createMockOperations(); + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operations.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + // Mock querySymbol to return mock type symbols + plugin.querySymbol = vi.fn().mockImplementation((args) => { + if (args.role === 'data') { + return { placeholder: `${args.resourceId}Data` }; + } + if (args.role === 'responses') { + return { placeholder: `${args.resourceId}Response` }; + } + return undefined; + }); + + generateNestjsClient({ plugin }); + + // Verify plugin.forEach was called to collect operations + expect(plugin.forEach).toHaveBeenCalledWith( + 'operation', + expect.any(Function), + ); + + // Verify symbol registration was called for the client + expect(plugin.registerSymbol).toHaveBeenCalledWith( + expect.objectContaining({ + exported: true, + name: 'ApiClient', + }), + ); + + // Verify symbol value was set with generated statements + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle custom client class name', () => { + const plugin = createMockPlugin({ + clientClassName: 'CustomApiClient', + clientName: 'Api', + }); + + plugin.forEach = vi.fn(); + plugin.querySymbol = vi.fn(); + + generateNestjsClient({ plugin }); + + expect(plugin.registerSymbol).toHaveBeenCalledWith( + expect.objectContaining({ + exported: true, + name: 'CustomApiClient', + }), + ); + }); + + it('should handle custom module name', () => { + const plugin = createMockPlugin({ + clientName: 'Api', + moduleName: 'CustomApiModule', + }); + + plugin.forEach = vi.fn(); + plugin.querySymbol = vi.fn(); + + generateNestjsClient({ plugin }); + + // Module name is used internally but not in registration + expect(plugin.registerSymbol).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should group operations by tags', () => { + const plugin = createMockPlugin( + { clientName: 'TestApi' }, + createMockContextWithOperations(), + ); + + const operations = createMockOperations(); + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operations.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'MockType', + }); + + generateNestjsClient({ plugin }); + + // Should process operations and generate client + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + + // Verify the generated value contains expected structures + const setSymbolCall = plugin.setSymbolValue.mock.calls[0]; + expect(setSymbolCall).toBeDefined(); + expect(Array.isArray(setSymbolCall[1])).toBe(true); + }); + + it('should handle operations without tags using default', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithoutTags = [ + { + method: 'get' as const, + operation: { + id: 'healthCheck', + method: 'get' as const, + operationId: 'healthCheck', + path: '/health', + }, + path: '/health', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithoutTags.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'HealthCheckData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle operations with multiple tags', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithMultipleTags = [ + { + method: 'get' as const, + operation: { + id: 'getPets', + method: 'get' as const, + operationId: 'getPets', + path: '/pets', + tags: ['pets', 'animals', 'catalog'], + }, + path: '/pets', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithMultipleTags.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'GetPetsData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should deduplicate service class names', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + // Create operations that would generate duplicate class names + const operationsWithSimilarTags = [ + { + method: 'get' as const, + operation: { + id: 'getPets1', + method: 'get' as const, + operationId: 'getPets1', + path: '/pets', + tags: ['pets'], + }, + path: '/pets', + }, + { + method: 'get' as const, + operation: { + id: 'getPets2', + method: 'get' as const, + operationId: 'getPets2', + path: '/pets/v2', + tags: ['Pets'], // Different case - would normalize to same name + }, + path: '/pets/v2', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithSimilarTags.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'MockData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle empty operations list', () => { + const plugin = createMockPlugin({ clientName: 'EmptyApi' }); + + plugin.forEach = vi.fn().mockImplementation((_eventType, _callback) => { + // Don't call callback - no operations + }); + + plugin.querySymbol = vi.fn(); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalledWith( + 'operation', + expect.any(Function), + ); + expect(plugin.registerSymbol).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should query types for operation data and responses', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operations = [ + { + method: 'get' as const, + operation: { + id: 'getUser', + method: 'get' as const, + operationId: 'getUser', + path: '/users/{id}', + tags: ['users'], + }, + path: '/users/{id}', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operations.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'MockType', + }); + + generateNestjsClient({ plugin }); + + // Should query symbols for data and responses + expect(plugin.querySymbol).toHaveBeenCalledWith( + expect.objectContaining({ + category: 'type', + resource: 'operation', + resourceId: 'getUser', + role: 'data', + tool: 'typescript', + }), + ); + + expect(plugin.querySymbol).toHaveBeenCalledWith( + expect.objectContaining({ + category: 'type', + resource: 'operation', + resourceId: 'getUser', + role: 'responses', + }), + ); + }); + + it('should generate imports for operation types', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operations = createMockOperations(); + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operations.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockImplementation((args) => { + if (args.role === 'data') { + return { placeholder: `${args.resourceId}Data` }; + } + if (args.role === 'responses') { + return { placeholder: `${args.resourceId}Response` }; + } + return undefined; + }); + + generateNestjsClient({ plugin }); + + expect(plugin.setSymbolValue).toHaveBeenCalled(); + const generatedStatements = plugin.setSymbolValue.mock.calls[0][1]; + expect(Array.isArray(generatedStatements)).toBe(true); + }); + + it('should handle different HTTP methods', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithDifferentMethods = [ + { + method: 'get' as const, + operation: { + id: 'getItems', + method: 'get' as const, + path: '/items', + tags: ['items'], + }, + path: '/items', + }, + { + method: 'post' as const, + operation: { + body: { required: true }, + id: 'createItem', + method: 'post' as const, + path: '/items', + tags: ['items'], + }, + path: '/items', + }, + { + method: 'put' as const, + operation: { + body: { required: true }, + id: 'updateItem', + method: 'put' as const, + path: '/items/{id}', + tags: ['items'], + }, + path: '/items/{id}', + }, + { + method: 'patch' as const, + operation: { + body: { required: false }, + id: 'patchItem', + method: 'patch' as const, + path: '/items/{id}', + tags: ['items'], + }, + path: '/items/{id}', + }, + { + method: 'delete' as const, + operation: { + id: 'deleteItem', + method: 'delete' as const, + path: '/items/{id}', + tags: ['items'], + }, + path: '/items/{id}', + }, + { + method: 'head' as const, + operation: { + id: 'headItem', + method: 'head' as const, + path: '/items/{id}', + tags: ['items'], + }, + path: '/items/{id}', + }, + { + method: 'options' as const, + operation: { + id: 'optionsItem', + method: 'options' as const, + path: '/items', + tags: ['items'], + }, + path: '/items', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithDifferentMethods.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'MockType', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle path parameters in operation paths', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithPathParams = [ + { + method: 'get' as const, + operation: { + id: 'getUser', + method: 'get' as const, + parameters: { + path: { + userId: { + name: 'userId', + required: true, + schema: { type: 'integer' as const }, + }, + }, + }, + path: '/users/{userId}', + tags: ['users'], + }, + path: '/users/{userId}', + }, + { + method: 'get' as const, + operation: { + id: 'getUserPost', + method: 'get' as const, + parameters: { + path: { + postId: { + name: 'postId', + required: true, + schema: { type: 'integer' as const }, + }, + userId: { + name: 'userId', + required: true, + schema: { type: 'integer' as const }, + }, + }, + }, + path: '/users/{userId}/posts/{postId}', + tags: ['users'], + }, + path: '/users/{userId}/posts/{postId}', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithPathParams.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'MockType', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle operations with query parameters', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithQueryParams = [ + { + method: 'get' as const, + operation: { + id: 'searchUsers', + method: 'get' as const, + parameters: { + query: { + limit: { + name: 'limit', + required: false, + schema: { type: 'integer' as const }, + }, + offset: { + name: 'offset', + required: false, + schema: { type: 'integer' as const }, + }, + search: { + name: 'search', + required: false, + schema: { type: 'string' as const }, + }, + }, + }, + path: '/users', + tags: ['users'], + }, + path: '/users', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithQueryParams.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'SearchUsersData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle operations with header parameters', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithHeaders = [ + { + method: 'get' as const, + operation: { + id: 'getSecureData', + method: 'get' as const, + parameters: { + header: { + authorization: { + name: 'authorization', + required: true, + schema: { type: 'string' as const }, + }, + 'x-api-key': { + name: 'x-api-key', + required: false, + schema: { type: 'string' as const }, + }, + }, + }, + path: '/secure', + tags: ['security'], + }, + path: '/secure', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithHeaders.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'GetSecureDataData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle operations with request body', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithBody = [ + { + method: 'post' as const, + operation: { + body: { + mediaType: 'application/json', + required: true, + schema: { type: 'object' as const }, + }, + id: 'createUser', + method: 'post' as const, + path: '/users', + tags: ['users'], + }, + path: '/users', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithBody.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'CreateUserData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle deprecated operations', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const deprecatedOperations = [ + { + method: 'get' as const, + operation: { + deprecated: true, + description: 'This endpoint is deprecated', + id: 'oldEndpoint', + method: 'get' as const, + path: '/old', + summary: 'Old endpoint', + tags: ['legacy'], + }, + path: '/old', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + deprecatedOperations.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'OldEndpointData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle operations with summary and description', () => { + const plugin = createMockPlugin({ clientName: 'Api' }); + + const operationsWithDocs = [ + { + method: 'get' as const, + operation: { + description: + 'This is a detailed description of what this endpoint does', + id: 'documentedEndpoint', + method: 'get' as const, + path: '/documented', + summary: 'A documented endpoint', + tags: ['docs'], + }, + path: '/documented', + }, + ]; + + plugin.forEach = vi.fn().mockImplementation((eventType, callback) => { + if (eventType === 'operation') { + operationsWithDocs.forEach((operationData) => { + callback({ + method: operationData.method, + operation: operationData.operation, + path: operationData.path, + type: 'operation', + }); + }); + } + }); + + plugin.querySymbol = vi.fn().mockReturnValue({ + placeholder: 'DocumentedEndpointData', + }); + + generateNestjsClient({ plugin }); + + expect(plugin.forEach).toHaveBeenCalled(); + expect(plugin.setSymbolValue).toHaveBeenCalled(); + }); + + it('should handle snake_case and kebab-case in client names', () => { + const testCases = [ + { clientName: 'my-api', expected: 'MyApiClient' }, + { clientName: 'my_api', expected: 'MyApiClient' }, + { clientName: 'myApi', expected: 'MyApiClient' }, + { clientName: 'MyApi', expected: 'MyApiClient' }, + { clientName: 'MYAPI', expected: 'MyapiClient' }, + ]; + + testCases.forEach(({ clientName, expected }) => { + const plugin = createMockPlugin({ clientName }); + plugin.forEach = vi.fn(); + plugin.querySymbol = vi.fn(); + + generateNestjsClient({ plugin }); + + expect(plugin.registerSymbol).toHaveBeenCalledWith( + expect.objectContaining({ + exported: true, + name: expected, + }), + ); + }); + }); + }); +}); diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/__tests__/test-helpers.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/__tests__/test-helpers.ts new file mode 100644 index 0000000000..1df787b301 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/__tests__/test-helpers.ts @@ -0,0 +1,281 @@ +import { vi } from 'vitest'; + +import type { HeyApiClientNestjsPlugin } from '../types'; + +/** + * Creates a mock plugin instance for testing generators + */ +export const createMockPlugin = ( + config: Partial = {}, + contextOverrides: any = {}, +): any => { + const mockContext: any = { + ir: { + components: { + parameters: {}, + requestBodies: {}, + schemas: {}, + }, + paths: {}, + servers: [], + }, + package: { + dependencies: {}, + name: '@hey-api/openapi-ts', + version: '1.0.0', + }, + plugins: {}, + ...contextOverrides, + }; + + const defaultConfig: any = { + clientName: 'Api', + output: './generated', + throwOnError: false, + ...config, + }; + + const symbolCounter = { value: 0 }; + + return { + api: { + getSelector: vi.fn((type: string, value?: string) => { + if (value) { + return ['@hey-api/client-nestjs', type, value]; + } + return ['@hey-api/client-nestjs', type]; + }), + }, + config: defaultConfig, + context: mockContext, + forEach: vi.fn(), + getPlugin: vi.fn(), + getPluginOrThrow: vi.fn().mockReturnValue({ + api: { + getSelector: vi.fn((type: string, id: string) => { + return ['@hey-api/typescript', type, id]; + }), + }, + }), + getSymbol: vi.fn(), + output: defaultConfig.output, + querySymbol: vi.fn().mockImplementation((args: any) => { + if (args.role === 'data') { + return { placeholder: `${args.resourceId}Data` }; + } + if (args.role === 'responses') { + return { placeholder: `${args.resourceId}Response` }; + } + return undefined; + }), + referenceSymbol: vi.fn(), + registerSymbol: vi.fn().mockImplementation((symbol: any) => ({ + exported: symbol.exported, + id: symbolCounter.value++, + placeholder: symbol.name, + })), + setSymbolValue: vi.fn(), + }; +}; + +/** + * Creates mock service groups for testing + */ +export const createMockServiceGroups = () => { + const serviceGroups = new Map(); + + serviceGroups.set('pets', { + className: 'ApiPetsService', + tag: 'pets', + }); + + serviceGroups.set('users', { + className: 'ApiUsersService', + tag: 'users', + }); + + serviceGroups.set('store', { + className: 'ApiStoreService', + tag: 'store', + }); + + return serviceGroups; +}; + +/** + * Creates mock operations for testing + */ +export const createMockOperations = (): Array => [ + { + method: 'get' as const, + operation: { + description: 'Returns a list of pets', + id: 'getPets', + method: 'get' as const, + operationId: 'getPets', + parameters: { + query: { + limit: { + required: false, + schema: { type: 'integer' as const }, + }, + }, + }, + path: '/pets', + responses: { + '200': { + description: 'A list of pets', + }, + }, + summary: 'List all pets', + tags: ['pets'], + }, + path: '/pets', + }, + { + method: 'post' as const, + operation: { + body: { + required: true, + }, + description: 'Creates a new pet', + id: 'createPet', + method: 'post' as const, + operationId: 'createPet', + path: '/pets', + responses: { + '201': { + description: 'Pet created', + }, + }, + summary: 'Create a pet', + tags: ['pets'], + }, + path: '/pets', + }, + { + method: 'get' as const, + operation: { + description: 'Returns a pet by ID', + id: 'getPetById', + method: 'get' as const, + operationId: 'getPetById', + parameters: { + path: { + petId: { + required: true, + schema: { type: 'integer' as const }, + }, + }, + }, + path: '/pets/{petId}', + responses: { + '200': { + description: 'Pet found', + }, + '404': { + description: 'Pet not found', + }, + }, + summary: 'Get pet by ID', + tags: ['pets'], + }, + path: '/pets/{petId}', + }, + { + method: 'get' as const, + operation: { + description: 'Returns a list of users', + id: 'getUsers', + method: 'get' as const, + operationId: 'getUsers', + parameters: { + header: { + authorization: { + required: true, + schema: { type: 'string' as const }, + }, + }, + query: { + page: { + required: false, + schema: { type: 'integer' as const }, + }, + size: { + required: false, + schema: { type: 'integer' as const }, + }, + }, + }, + path: '/users', + responses: { + '200': { + description: 'A list of users', + }, + }, + summary: 'List all users', + tags: ['users'], + }, + path: '/users', + }, + { + method: 'post' as const, + operation: { + body: { + required: true, + }, + description: 'Place an order for a pet', + id: 'placeOrder', + method: 'post' as const, + operationId: 'placeOrder', + path: '/store/orders', + responses: { + '200': { + description: 'Order placed', + }, + }, + summary: 'Place an order', + tags: ['store'], + }, + path: '/store/orders', + }, +]; + +/** + * Creates a mock context with operations + */ +export const createMockContextWithOperations = (): any => { + const operations = createMockOperations(); + const paths: any = {}; + + // Group operations by path + for (const { method, operation, path } of operations) { + if (!paths[path]) { + paths[path] = {}; + } + paths[path][method] = operation; + } + + return { + ir: { + components: { + parameters: {}, + requestBodies: {}, + schemas: {}, + }, + paths, + servers: [ + { + description: 'Production server', + url: 'https://api.example.com/v1', + }, + ], + }, + package: { + dependencies: {}, + name: '@hey-api/openapi-ts', + version: '1.0.0', + }, + plugins: {}, + }; +}; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/api.ts new file mode 100644 index 0000000000..b9ecb4bee9 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/api.ts @@ -0,0 +1,27 @@ +import type { Selector } from '@hey-api/codegen-core'; + +import type { Plugin } from '../../types'; + +type SelectorType = 'client'; + +export type IApi = { + /** + * @param type Selector type. + * @param value Depends on `type`: + * - `client`: never + * - `client-service`: never + * - `module`: never + * - `services`: never + * - `index`: never + * @returns Selector array + */ + getSelector: (type: SelectorType, value?: string) => Selector; +}; + +export class Api implements IApi { + constructor(public meta: Plugin.Name<'@hey-api/client-nestjs'>) {} + + getSelector(...args: ReadonlyArray): Selector { + return [this.meta.name, ...(args as Selector)]; + } +} diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/bundle/types.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/bundle/types.ts new file mode 100644 index 0000000000..46c6ab4848 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/bundle/types.ts @@ -0,0 +1,167 @@ +import type { FactoryProvider, ModuleMetadata } from '@nestjs/common'; +import type { + AxiosError, + AxiosRequestConfig, + AxiosRequestHeaders, + AxiosResponse, + CreateAxiosDefaults, +} from 'axios'; + +import type { Auth } from '../../client-core/bundle/auth'; +import type { Config as CoreConfig } from '../../client-core/bundle/types'; + +/** + * Configuration interface for NestJS modules + */ +export interface ClientModuleConfig { + /** + * Custom axios configuration options + */ + axiosConfig?: AxiosRequestConfig; + + /** + * Base URL for API requests + */ + baseUrl?: string; + + /** + * Default headers to include with requests + */ + headers?: Record; +} +export const ClientModuleConfig = Symbol('ClientModuleConfig'); + +/** + * Async configuration for NestJS modules using factory pattern + */ +export interface ClientModuleAsyncConfig { + /** + * Modules to import for dependency injection + */ + imports?: ModuleMetadata['imports']; + + /** + * Dependencies to inject into the factory function + */ + inject?: FactoryProvider['inject']; + + /** + * Configuration class to use + */ + useClass?: (...args: any[]) => ClientModuleConfig; + + /** + * Factory function to create configuration + */ + useFactory?: ( + ...args: any[] + ) => ClientModuleConfig | Promise; +} + +export interface ClientOptions { + baseURL?: string; + throwOnError?: boolean; +} + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseURL?: T['baseURL']; + /** + * 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?: + | AxiosRequestHeaders + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + >; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +/** + * Request options interface similar to axios client + */ +export interface RequestOptions< + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + 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 interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +/** + * Options type for service method calls + */ +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, +> = OmitKeys, 'body' | 'path' | 'query' | 'url'> & + TData; + +/** + * Request result type that handles both success and error cases + */ +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, +> = ThrowOnError extends true + ? Promise< + AxiosResponse< + TData extends Record ? TData[keyof TData] : TData + > + > + : Promise< + | (AxiosResponse< + TData extends Record ? TData[keyof TData] : TData + > & { error: undefined }) + | (AxiosError< + TError extends Record ? TError[keyof TError] : TError + > & { + data: undefined; + error: TError extends Record + ? TError[keyof TError] + : TError; + }) + >; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/config.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/config.ts new file mode 100644 index 0000000000..45417cb8a9 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/config.ts @@ -0,0 +1,24 @@ +import { definePluginConfig } from '../../shared/utils/config'; +import { clientDefaultConfig, clientDefaultMeta } from '../client-core/config'; +import { Api } from './api'; +import { clientPluginHandler } from './plugin'; +import type { HeyApiClientNestjsPlugin } from './types'; + +export const defaultConfig: HeyApiClientNestjsPlugin['Config'] = { + ...clientDefaultMeta, + api: new Api({ + name: '@hey-api/client-nestjs', + }), + config: { + ...clientDefaultConfig, + clientName: 'Api', + throwOnError: false, + }, + handler: clientPluginHandler as HeyApiClientNestjsPlugin['Handler'], + name: '@hey-api/client-nestjs', +}; + +/** + * Type helper for `@hey-api/client-nestjs` plugin, returns {@link Plugin.Config} object + */ +export const defineConfig = definePluginConfig(defaultConfig); diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/index.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/index.ts new file mode 100644 index 0000000000..141048490c --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/index.ts @@ -0,0 +1,3 @@ +export { Api } from './api'; +export { defaultConfig, defineConfig } from './config'; +export type { HeyApiClientNestjsPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/nestjs-generator.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/nestjs-generator.ts new file mode 100644 index 0000000000..8e3eec2dd4 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/nestjs-generator.ts @@ -0,0 +1,1540 @@ +import ts from 'typescript'; + +import type { IR } from '../../../ir/types'; +import { tsc } from '../../../tsc'; +import { stringCase } from '../../../utils/stringCase'; +import type { HeyApiClientNestjsPlugin } from './types'; +import { + createClientClassName, + createClientConfigToken, + createModuleClassName, + createServiceClassName, + getClientName, +} from './utils'; + +/** + * Represents a group of operations for a single tag + */ +interface ServiceGroup { + className: string; + operations: Array<{ + method: keyof IR.PathItemObject; + operation: IR.OperationObject; + path: string; + }>; + tag: string; +} + +/** + * Main generation orchestrator - generates all NestJS client artifacts + */ +export const generateNestjsClient = ({ + plugin, +}: { + plugin: Parameters[0]['plugin']; +}) => { + const clientName = getClientName(plugin.config); + + // Step 1: Collect and group operations first + const operations: Array<{ + method: keyof IR.PathItemObject; + operation: IR.OperationObject; + path: string; + }> = []; + + plugin.forEach('operation', ({ method, operation, path }) => { + operations.push({ method, operation, path }); + }); + + // Step 2: Group operations by tags and deduplicate class names + const serviceGroups = groupOperationsByTags(operations); + const processedGroups = processServiceGroups(serviceGroups, clientName); + + // Step 3: Generate everything into a single file + generateSingleFile({ + clientName, + plugin, + serviceGroups: processedGroups, + }); +}; + +/** + * Generates everything into a single file in the correct order: + * 1. Imports + * 2. API Client Service Class + * 3. Services by Tags Classes + * 4. Module Class + */ +const generateSingleFile = ({ + clientName, + plugin, + serviceGroups, +}: { + clientName: string; + plugin: Parameters[0]['plugin']; + serviceGroups: Map; +}) => { + const clientClassName = + plugin.config.clientClassName || createClientClassName(clientName); + const moduleClassName = + plugin.config.moduleName || createModuleClassName(clientName); + const configToken = createClientConfigToken(clientName); + + // Register single symbol for the entire client file + const clientSymbol = plugin.registerSymbol({ + exported: true, + name: clientClassName, + }); + + // Collect all operation-specific types + const typesToImport = new Set(); + + for (const group of serviceGroups.values()) { + for (const { operation } of group.operations) { + const dataSymbol = plugin.querySymbol({ + category: 'type', + resource: 'operation', + resourceId: operation.id, + role: 'data', + tool: 'typescript', + }); + if (dataSymbol?.placeholder) { + typesToImport.add(dataSymbol.placeholder); + } + + const responseSymbol = plugin.querySymbol({ + category: 'type', + resource: 'operation', + resourceId: operation.id, + role: 'responses', + }); + if (responseSymbol?.placeholder) { + typesToImport.add(responseSymbol.placeholder); + } + } + } + + // Get unique service class names + const uniqueServiceClassNames = Array.from( + new Set(Array.from(serviceGroups.values()).map((group) => group.className)), + ); + + // === SECTION 1: IMPORTS === + const imports = [ + // NestJS imports + tsc.namedImportDeclarations({ + imports: ['Injectable', 'Inject', 'Module', 'DynamicModule'], + module: '@nestjs/common', + }), + + // Axios default import + tsc.defaultImportDeclaration({ + module: 'axios', + name: 'axios', + }), + + // Axios type imports + tsc.namedImportDeclarations({ + imports: [ + { asType: true, name: 'AxiosInstance' }, + { asType: true, name: 'AxiosRequestConfig' }, + { asType: true, name: 'AxiosResponse' }, + { asType: true, name: 'RawAxiosRequestHeaders' }, + ], + module: 'axios', + }), + + // Client types imports + tsc.namedImportDeclarations({ + imports: [ + { asType: true, name: 'ClientModuleConfig' }, + { asType: true, name: 'ClientModuleAsyncConfig' }, + { asType: true, name: 'RequestOptions' }, + ], + module: './types.gen', + }), + + // Operation-specific types import + ...(typesToImport.size > 0 + ? [ + tsc.namedImportDeclarations({ + imports: Array.from(typesToImport).map((name) => ({ + asType: true, + name, + })), + module: './types.gen', + }), + ] + : []), + ]; + + // === SECTION 2: API CLIENT SERVICE CLASS === + const clientClass = tsc.classDeclaration({ + decorator: { + args: [], + name: 'Injectable', + }, + exportClass: true, + name: clientClassName, + nodes: [ + // Private axios instance property + tsc.propertyDeclaration({ + modifiers: ['private'], + name: 'axiosInstance', + type: tsc.typeReferenceNode({ typeName: 'AxiosInstance' }), + }), + + // Constructor with dependency injection + createClientConstructor(configToken), + + // Generic request method + createRequestMethod(), + + // HTTP method shortcuts + createGetMethod(), + createPostMethod(), + createPutMethod(), + createPatchMethod(), + createDeleteMethod(), + createHeadMethod(), + createOptionsMethod(), + ], + }); + + // === SECTION 3: SERVICES BY TAGS CLASSES === + const serviceClasses: Array = []; + + for (const [_tag, group] of serviceGroups) { + // Create constructor + const constructor = tsc.constructorDeclaration({ + parameters: [ + { + accessLevel: 'private', + isReadOnly: true, + name: 'client', + type: tsc.typeReferenceNode({ typeName: clientClassName }), + }, + ], + statements: [], + }); + + // Create service methods for each operation + const methods: Array = [constructor]; + + for (const { operation, path } of group.operations) { + const methodName = generateOperationMethodName(operation); + const method = generateOperationMethod( + operation, + methodName, + path, + plugin, + ); + methods.push(method); + } + + // Create the service class + const serviceClass = tsc.classDeclaration({ + decorator: { + args: [], + name: 'Injectable', + }, + exportClass: true, + name: group.className, + nodes: methods, + }); + + serviceClasses.push(serviceClass); + } + + // === SECTION 4: MODULE CLASS === + const allServices = [clientClassName, ...uniqueServiceClassNames]; + + // Create the forRoot static method + const forRootMethod = tsc.methodDeclaration({ + accessLevel: 'public', + isStatic: true, + name: 'forRoot', + parameters: [ + { + name: 'config', + type: tsc.typeReferenceNode({ typeName: 'ClientModuleConfig' }), + }, + ], + returnType: tsc.typeReferenceNode({ typeName: 'DynamicModule' }), + statements: [ + tsc.returnStatement({ + expression: tsc.objectExpression({ + obj: [ + { + key: 'module', + value: tsc.identifier({ text: moduleClassName }), + }, + { + key: 'providers', + value: tsc.arrayLiteralExpression({ + elements: [ + // Config provider + tsc.objectExpression({ + obj: [ + { + key: 'provide', + value: tsc.stringLiteral({ text: configToken }), + }, + { + key: 'useValue', + value: tsc.identifier({ text: 'config' }), + }, + ], + }), + // All service providers + ...allServices.map((serviceName) => + tsc.identifier({ text: serviceName }), + ), + ], + }), + }, + { + key: 'exports', + value: tsc.arrayLiteralExpression({ + elements: allServices.map((serviceName) => + tsc.identifier({ text: serviceName }), + ), + }), + }, + ], + }), + }), + ], + }); + + // Create the forRootAsync static method + const forRootAsyncMethod = tsc.methodDeclaration({ + accessLevel: 'public', + isStatic: true, + name: 'forRootAsync', + parameters: [ + { + name: 'options', + type: tsc.typeReferenceNode({ typeName: 'ClientModuleAsyncConfig' }), + }, + ], + returnType: tsc.typeReferenceNode({ typeName: 'DynamicModule' }), + statements: [ + tsc.returnStatement({ + expression: tsc.objectExpression({ + obj: [ + { + key: 'module', + value: tsc.identifier({ text: moduleClassName }), + }, + { + key: 'imports', + value: tsc.conditionalExpression({ + condition: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'imports' }), + }), + whenFalse: tsc.arrayLiteralExpression({ elements: [] }), + whenTrue: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'imports' }), + }), + }), + }, + { + key: 'providers', + value: tsc.arrayLiteralExpression({ + elements: [ + // Async config provider + tsc.objectExpression({ + obj: [ + { + key: 'provide', + value: tsc.stringLiteral({ text: configToken }), + }, + { + key: 'useFactory', + value: tsc.conditionalExpression({ + condition: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'useFactory' }), + }), + whenFalse: tsc.arrowFunction({ + parameters: [], + returnType: 'ClientModuleConfig', + statements: [ + tsc.returnStatement({ + expression: tsc.objectExpression({ obj: [] }), + }), + ], + }), + whenTrue: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'useFactory' }), + }), + }), + }, + { + key: 'inject', + value: tsc.conditionalExpression({ + condition: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'inject' }), + }), + whenFalse: tsc.arrayLiteralExpression({ + elements: [], + }), + whenTrue: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'inject' }), + }), + }), + }, + ], + }), + // All service providers + ...allServices.map((serviceName) => + tsc.identifier({ text: serviceName }), + ), + ], + }), + }, + { + key: 'exports', + value: tsc.arrayLiteralExpression({ + elements: allServices.map((serviceName) => + tsc.identifier({ text: serviceName }), + ), + }), + }, + ], + }), + }), + ], + }); + + const moduleClass = tsc.classDeclaration({ + decorator: { + args: [{}], + name: 'Module', + }, + exportClass: true, + name: moduleClassName, + nodes: [forRootMethod, forRootAsyncMethod], + }); + + // Combine everything in the correct order + const allStatements = [ + ...imports, + clientClass, + ...serviceClasses, + moduleClass, + ]; + + // Set the symbol value with all statements + plugin.setSymbolValue(clientSymbol, allStatements); +}; + +/** + * Creates the client constructor + */ +const createClientConstructor = (configToken: string) => { + return tsc.constructorDeclaration({ + parameters: [ + { + accessLevel: 'private', + decorators: [ + { + args: [configToken], + name: 'Inject', + }, + ], + isReadOnly: true, + name: 'config', + type: tsc.typeReferenceNode({ typeName: 'ClientModuleConfig' }), + }, + ], + statements: [ + // Create axios configuration + tsc.constVariable({ + expression: tsc.objectExpression({ + obj: [ + { + key: 'baseURL', + value: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'config' }), + name: tsc.identifier({ text: 'baseUrl' }), + }), + }, + { + key: 'headers', + value: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'config' }), + name: tsc.identifier({ text: 'headers' }), + }), + }, + { + key: 'timeout', + value: tsc.ots.number(5000), + }, + { + spread: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'config' }), + name: tsc.identifier({ text: 'axiosConfig' }), + }), + }, + ], + }), + name: 'axiosConfig', + }), + + // Initialize axios instance + tsc.expressionToStatement({ + expression: tsc.binaryExpression({ + left: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'axiosInstance' }), + }), + operator: '=', + right: tsc.callExpression({ + functionName: 'axios.create', + parameters: [tsc.identifier({ text: 'axiosConfig' })], + }), + }), + }), + ], + }); +}; + +/** + * Creates the generic request method + */ +const createRequestMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'request', + parameters: [ + { + name: 'options', + type: tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + // Prepare axios request config + tsc.constVariable({ + expression: tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'method' }), + }), + }, + { + key: 'url', + value: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'url' }), + }), + }, + { + key: 'headers', + value: tsc.asExpression({ + expression: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'headers' }), + }), + type: tsc.typeReferenceNode({ + typeName: 'RawAxiosRequestHeaders', + }), + }), + }, + { + key: 'params', + value: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'query' }), + }), + }, + { + key: 'data', + value: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'body' }), + }), + }, + { + key: 'timeout', + value: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: tsc.identifier({ text: 'timeout' }), + }), + }, + ], + }), + name: 'config', + typeName: 'AxiosRequestConfig', + }), + + // Make the request + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'axiosInstance' }), + }), + parameters: [tsc.identifier({ text: 'config' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +/** + * Creates HTTP method shortcuts (GET, POST, etc.) + */ +const createGetMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'get', + parameters: [ + { + name: 'url', + type: tsc.typeReferenceNode({ typeName: 'string' }), + }, + { + name: 'options', + type: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + tsc.typeUnionNode({ + types: [ + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'method' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'url' }), + }), + ], + }), + ], + typeName: 'Omit', + }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'request' }), + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.stringLiteral({ text: 'GET' }), + }, + { + key: 'url', + value: tsc.identifier({ text: 'url' }), + }, + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }), + ], + types: [tsc.typeReferenceNode({ typeName: 'T' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +const createPostMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'post', + parameters: [ + { + name: 'url', + type: tsc.typeReferenceNode({ typeName: 'string' }), + }, + { + name: 'data', + type: tsc.typeReferenceNode({ typeName: 'any' }), + }, + { + name: 'options', + type: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + tsc.typeUnionNode({ + types: [ + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'method' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'url' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'body' }), + }), + ], + }), + ], + typeName: 'Omit', + }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'request' }), + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.stringLiteral({ text: 'POST' }), + }, + { + key: 'url', + value: tsc.identifier({ text: 'url' }), + }, + { + key: 'body', + value: tsc.identifier({ text: 'data' }), + }, + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }), + ], + types: [tsc.typeReferenceNode({ typeName: 'T' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +const createPutMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'put', + parameters: [ + { + name: 'url', + type: tsc.typeReferenceNode({ typeName: 'string' }), + }, + { + name: 'data', + type: tsc.typeReferenceNode({ typeName: 'any' }), + }, + { + name: 'options', + type: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + tsc.typeUnionNode({ + types: [ + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'method' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'url' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'body' }), + }), + ], + }), + ], + typeName: 'Omit', + }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'request' }), + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.stringLiteral({ text: 'PUT' }), + }, + { + key: 'url', + value: tsc.identifier({ text: 'url' }), + }, + { + key: 'body', + value: tsc.identifier({ text: 'data' }), + }, + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }), + ], + types: [tsc.typeReferenceNode({ typeName: 'T' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +const createPatchMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'patch', + parameters: [ + { + name: 'url', + type: tsc.typeReferenceNode({ typeName: 'string' }), + }, + { + name: 'data', + type: tsc.typeReferenceNode({ typeName: 'any' }), + }, + { + name: 'options', + type: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + tsc.typeUnionNode({ + types: [ + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'method' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'url' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'body' }), + }), + ], + }), + ], + typeName: 'Omit', + }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'request' }), + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.stringLiteral({ text: 'PATCH' }), + }, + { + key: 'url', + value: tsc.identifier({ text: 'url' }), + }, + { + key: 'body', + value: tsc.identifier({ text: 'data' }), + }, + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }), + ], + types: [tsc.typeReferenceNode({ typeName: 'T' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +const createDeleteMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'delete', + parameters: [ + { + name: 'url', + type: tsc.typeReferenceNode({ typeName: 'string' }), + }, + { + name: 'options', + type: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + tsc.typeUnionNode({ + types: [ + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'method' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'url' }), + }), + ], + }), + ], + typeName: 'Omit', + }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'request' }), + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.stringLiteral({ text: 'DELETE' }), + }, + { + key: 'url', + value: tsc.identifier({ text: 'url' }), + }, + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }), + ], + types: [tsc.typeReferenceNode({ typeName: 'T' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +const createHeadMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'head', + parameters: [ + { + name: 'url', + type: tsc.typeReferenceNode({ typeName: 'string' }), + }, + { + name: 'options', + type: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + tsc.typeUnionNode({ + types: [ + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'method' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'url' }), + }), + ], + }), + ], + typeName: 'Omit', + }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'request' }), + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.stringLiteral({ text: 'HEAD' }), + }, + { + key: 'url', + value: tsc.identifier({ text: 'url' }), + }, + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }), + ], + types: [tsc.typeReferenceNode({ typeName: 'T' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +const createOptionsMethod = () => { + return tsc.methodDeclaration({ + accessLevel: 'public', + name: 'options', + parameters: [ + { + name: 'url', + type: tsc.typeReferenceNode({ typeName: 'string' }), + }, + { + name: 'options', + type: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: 'RequestOptions' }), + tsc.typeUnionNode({ + types: [ + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'method' }), + }), + tsc.literalTypeNode({ + literal: tsc.stringLiteral({ text: 'url' }), + }), + ], + }), + ], + typeName: 'Omit', + }), + }, + ], + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [tsc.typeReferenceNode({ typeName: 'T' })], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }), + statements: [ + tsc.returnStatement({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: tsc.identifier({ text: 'request' }), + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: tsc.stringLiteral({ text: 'OPTIONS' }), + }, + { + key: 'url', + value: tsc.identifier({ text: 'url' }), + }, + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }), + ], + types: [tsc.typeReferenceNode({ typeName: 'T' })], + }), + }), + ], + types: [ + { + default: 'any', + name: 'T', + }, + ], + }); +}; + +/** + * Groups operations by their tags + */ +const groupOperationsByTags = ( + operations: Array<{ + method: keyof IR.PathItemObject; + operation: IR.OperationObject; + path: string; + }>, +): Map => { + const groups = new Map(); + + for (const operationData of operations) { + const { operation } = operationData; + const tags = + operation.tags && operation.tags.length > 0 + ? operation.tags + : ['default']; + + for (const tag of tags) { + if (!groups.has(tag)) { + groups.set(tag, { + className: tag, // Will be processed later with naming conventions + operations: [], + tag, + }); + } + + groups.get(tag)!.operations.push(operationData); + } + } + + return groups; +}; + +/** + * Processes service groups and applies naming conventions with deduplication + * FIX for Issue #3: Duplicate Service Class Names + */ +const processServiceGroups = ( + groups: Map, + clientName: string, +): Map => { + const processedGroups = new Map(); + const usedClassNames = new Set(); + + for (const [tag, group] of groups) { + let className = createServiceClassName(clientName, tag); + + // Handle duplicate class names - add numeric suffix + let counter = 2; + const baseClassName = className; + while (usedClassNames.has(className)) { + className = `${baseClassName}${counter}`; + counter++; + } + usedClassNames.add(className); + + const processedGroup: ServiceGroup = { + ...group, + className, + }; + processedGroups.set(tag, processedGroup); + } + + return processedGroups; +}; + +/** + * Generates method name for an operation in a service + */ +const generateOperationMethodName = (operation: IR.OperationObject): string => { + // Use operationId if available, otherwise generate from method + path + if (operation.operationId) { + return stringCase({ + case: 'camelCase', + value: operation.operationId, + }); + } + + // Fallback: generate from method and path + const pathParts = operation.path + .split('/') + .filter((part) => part && !part.startsWith('{')) + .map((part) => stringCase({ case: 'PascalCase', value: part })); + + const methodName = stringCase({ case: 'camelCase', value: operation.method }); + const pathName = pathParts.join(''); + + return pathName ? `${methodName}${pathName}` : methodName; +}; + +/** + * Generates a single operation method with proper typing and implementation + */ +const generateOperationMethod = ( + operation: IR.OperationObject, + methodName: string, + _path: string, + plugin: Parameters[0]['plugin'], +) => { + // Generate JSDoc comment + const comments = []; + if (operation.summary) { + comments.push(operation.summary); + } + if (operation.description && operation.description !== operation.summary) { + comments.push(operation.description); + } + if (operation.deprecated) { + comments.push('@deprecated'); + } + + // Get data type (request parameters) + const dataSymbol = plugin.querySymbol({ + category: 'type', + resource: 'operation', + resourceId: operation.id, + role: 'data', + tool: 'typescript', + }); + const dataTypeName = dataSymbol?.placeholder || 'unknown'; + + // Get response type + const responseSymbol = plugin.querySymbol({ + category: 'type', + resource: 'operation', + resourceId: operation.id, + role: 'responses', + }); + const responseTypeName = responseSymbol?.placeholder; + + // Create the method parameter type based on the operation + const hasParams = hasOperationParameters(operation); + + // All methods should have at least an options parameter (optional if no required params) + const parameters = hasParams + ? [ + { + name: 'options', + type: tsc.typeReferenceNode({ typeName: dataTypeName || 'unknown' }), + }, + ] + : [ + { + isOptional: true, + name: 'options', + type: tsc.typeReferenceNode({ typeName: dataTypeName || 'unknown' }), + }, + ]; + + // Generate method implementation + const methodCall = generateMethodImplementation(operation, true); + + // Create return type - get 200 response or fallback to any response + const returnType = tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ + typeArguments: [ + // Use the proper response type from TypeScript plugin + responseTypeName + ? tsc.indexedAccessTypeNode({ + indexType: tsc.literalTypeNode({ + literal: tsc.ots.number(200), + }), + objectType: tsc.typeReferenceNode({ + typeName: responseTypeName, + }), + }) + : tsc.keywordTypeNode({ keyword: 'unknown' }), + ], + typeName: 'AxiosResponse', + }), + ], + typeName: 'Promise', + }); + + return tsc.methodDeclaration({ + accessLevel: 'public', + comment: comments.length > 0 ? comments : undefined, + isAsync: true, + name: methodName, + parameters, + returnType, + statements: [methodCall], + }); +}; + +/** + * Checks if an operation has parameters + */ +const hasOperationParameters = (operation: IR.OperationObject): boolean => { + const hasPath = + operation.parameters?.path && + Object.keys(operation.parameters.path).length > 0; + const hasQuery = + operation.parameters?.query && + Object.keys(operation.parameters.query).length > 0; + const hasHeader = + operation.parameters?.header && + Object.keys(operation.parameters.header).length > 0; + const hasBody = !!operation.body; + + return hasPath || hasQuery || hasHeader || hasBody; +}; + +/** + * Generates the URL expression with path parameter interpolation + */ +const generateUrlExpression = ( + operation: IR.OperationObject, + hasParams: boolean, +) => { + const path = operation.path; + + // Check if path has parameters like /users/{id} + const pathParams = path.match(/\{([^}]+)\}/g); + + if (!pathParams || pathParams.length === 0) { + // No path parameters, return static string + return tsc.stringLiteral({ text: path }); + } + + if (!hasParams) { + // Has path parameters but no options, return static string (shouldn't happen) + return tsc.stringLiteral({ text: path }); + } + + // Build string concatenation with path parameter interpolation + // e.g., /users/{id} becomes `/users/${options.path.id}` + // which in code is: '/users/' + options.path.id + const parts: Array = []; + let lastIndex = 0; + + pathParams.forEach((param) => { + const paramName = param.slice(1, -1); // Remove { and } + const paramIndex = path.indexOf(param, lastIndex); + + // Add the string part before the parameter + if (paramIndex > lastIndex) { + parts.push( + tsc.stringLiteral({ text: path.slice(lastIndex, paramIndex) }), + ); + } + + // Add the parameter access expression + parts.push( + tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: 'path', + }), + name: paramName, + }), + ); + + lastIndex = paramIndex + param.length; + }); + + // Add remaining string after last parameter + if (lastIndex < path.length) { + parts.push(tsc.stringLiteral({ text: path.slice(lastIndex) })); + } + + // If only one part, return it directly + if (parts.length === 1) { + return parts[0]; + } + + // Concatenate all parts with + operator using TypeScript factory directly + let expression = parts[0]; + for (let i = 1; i < parts.length; i++) { + expression = ts.factory.createBinaryExpression( + expression, + ts.SyntaxKind.PlusToken, + parts[i], + ); + } + + return expression; +}; + +/** + * Generates the method implementation that calls the client + */ +const generateMethodImplementation = ( + operation: IR.OperationObject, + hasParams: boolean, +) => { + const methodName = operation.method.toLowerCase(); + const urlExpression = generateUrlExpression(operation, hasParams); + + // Methods that require data (POST, PUT, PATCH) + const methodsWithData = ['post', 'put', 'patch']; + const hasData = methodsWithData.includes(methodName); + + if (hasData) { + // For methods with data: post(url, data, options) + return tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: 'client', + }), + name: methodName, + }), + parameters: [ + // URL parameter - always use actual path from OpenAPI + urlExpression, + // Data parameter + hasParams + ? tsc.conditionalExpression({ + condition: tsc.binaryExpression({ + left: tsc.stringLiteral({ text: 'body' }), + operator: 'in', + right: tsc.identifier({ text: 'options' }), + }), + whenFalse: tsc.identifier({ text: 'undefined' }), + whenTrue: tsc.propertyAccessExpression({ + expression: tsc.identifier({ text: 'options' }), + name: 'body', + }), + }) + : tsc.identifier({ text: 'undefined' }), + // Options parameter + hasParams + ? tsc.objectExpression({ + obj: [ + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }) + : tsc.objectExpression({ obj: [] }), + ], + }), + }), + }); + } else { + // For methods without data: get(url, options), delete(url, options) + return tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: tsc.this(), + name: 'client', + }), + name: methodName, + }), + parameters: [ + // URL parameter - always use actual path from OpenAPI + urlExpression, + // Options parameter + hasParams + ? tsc.objectExpression({ + obj: [ + { + spread: tsc.identifier({ text: 'options' }), + }, + ], + }) + : tsc.objectExpression({ obj: [] }), + ], + }), + }), + }); + } +}; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/plugin.ts new file mode 100644 index 0000000000..2b90c6fe33 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/plugin.ts @@ -0,0 +1,18 @@ +import { createClientConfigType } from '../client-core/createClientConfig'; +import { generateNestjsClient } from './nestjs-generator'; +import type { HeyApiClientNestjsPlugin } from './types'; + +export const clientPluginHandler = ({ + plugin, +}: { + plugin: Parameters[0]['plugin']; +}) => { + // Create client configuration type (reuse from client-core) + createClientConfigType({ + plugin, + }); + + // Generate all NestJS client artifacts (client, services, module, index) + // Now consolidated in a single generator with deduplication fixes for issues #3 and #4 + generateNestjsClient({ plugin }); +}; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/types.d.ts new file mode 100644 index 0000000000..fa18cc08f8 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/types.d.ts @@ -0,0 +1,46 @@ +import type { DefinePlugin, Plugin } from '../../types'; +import type { Client } from '../client-core/types'; +import type { IApi } from './api'; + +export type UserConfig = Plugin.Name<'@hey-api/client-nestjs'> & + Client.Config & { + /** + * Custom client class name + * @default `${clientName}Client` + */ + clientClassName?: string; + + /** + * Unique client identifier to avoid symbol conflicts when multiple + * OpenAPI clients are used in the same NestJS application. + * This will be used as a prefix for all generated classes, services, + * modules, and injection tokens. + * @default 'Api' + */ + clientName?: string; + + /** + * Custom module name + * @default `${clientName}Module` + */ + moduleName?: string; + + /** + * Custom service naming strategy + * @default (tag, clientName) => `${clientName}${PascalCase(tag)}Service` + */ + serviceNameBuilder?: (tag: string, clientName: string) => string; + + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: boolean; + }; + +export type HeyApiClientNestjsPlugin = DefinePlugin< + UserConfig, + UserConfig, + IApi +>; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/utils.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/utils.ts new file mode 100644 index 0000000000..e96365d266 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nestjs/utils.ts @@ -0,0 +1,75 @@ +import { stringCase } from '../../../utils/stringCase'; + +/** + * Utility function for generating unique injection tokens + */ +export const createClientConfigToken = (clientName: string): string => { + const tokenName = stringCase({ + case: 'SCREAMING_SNAKE_CASE', + value: `${clientName}_CLIENT_CONFIG`, + }); + return tokenName; +}; + +/** + * Utility functions for consistent naming + */ +export const createClientClassName = (clientName: string): string => + `${stringCase({ case: 'PascalCase', value: clientName })}Client`; + +export const createServiceClassName = ( + clientName: string, + tag: string, +): string => { + const pascalClientName = stringCase({ + case: 'PascalCase', + value: clientName, + }); + const pascalTag = stringCase({ case: 'PascalCase', value: tag }); + return `${pascalClientName}${pascalTag}Service`; +}; + +export const createModuleClassName = (clientName: string): string => { + const pascalClientName = stringCase({ + case: 'PascalCase', + value: clientName, + }); + return `${pascalClientName}Module`; +}; + +/** + * Utility for service file names + */ +export const createServiceFileName = ( + clientName: string, + tag: string, +): string => { + // Convert to snake_case and then replace underscores with hyphens for kebab-case + const kebabClientName = stringCase({ + case: 'snake_case', + value: clientName, + }).replace(/_/g, '-'); + const kebabTag = stringCase({ case: 'snake_case', value: tag }).replace( + /_/g, + '-', + ); + return `${kebabClientName}-${kebabTag}.service.ts`; +}; + +/** + * Utility for module file names + */ +export const createModuleFileName = (clientName: string): string => { + // Convert to snake_case and then replace underscores with hyphens for kebab-case + const kebabClientName = stringCase({ + case: 'snake_case', + value: clientName, + }).replace(/_/g, '-'); + return `${kebabClientName}.module.ts`; +}; + +/** + * Get the client name from plugin config with fallback + */ +export const getClientName = (config: { clientName?: string }): string => + config.clientName || 'Api'; diff --git a/packages/openapi-ts/src/plugins/config.ts b/packages/openapi-ts/src/plugins/config.ts index ee424efc86..b6f242e28c 100644 --- a/packages/openapi-ts/src/plugins/config.ts +++ b/packages/openapi-ts/src/plugins/config.ts @@ -15,6 +15,8 @@ import type { HeyApiClientNuxtPlugin } from '~/plugins/@hey-api/client-nuxt'; import { defaultConfig as heyApiClientNuxt } from '~/plugins/@hey-api/client-nuxt'; import type { HeyApiClientOfetchPlugin } from '~/plugins/@hey-api/client-ofetch'; import { defaultConfig as heyApiClientOfetch } from '~/plugins/@hey-api/client-ofetch'; +import type { HeyApiClientNestjsPlugin } from '~/plugins/@hey-api/client-nestjs'; +import { defaultConfig as heyApiClientNestjs } from '~/plugins/@hey-api/client-nestjs'; import type { HeyApiSchemasPlugin } from '~/plugins/@hey-api/schemas'; import { defaultConfig as heyApiSchemas } from '~/plugins/@hey-api/schemas'; import type { HeyApiSdkPlugin } from '~/plugins/@hey-api/sdk'; @@ -54,6 +56,7 @@ export interface PluginConfigMap { '@hey-api/client-fetch': HeyApiClientFetchPlugin['Types']; '@hey-api/client-ky': HeyApiClientKyPlugin['Types']; '@hey-api/client-next': HeyApiClientNextPlugin['Types']; + '@hey-api/client-nestjs': HeyApiClientNestjsPlugin['Types']; '@hey-api/client-nuxt': HeyApiClientNuxtPlugin['Types']; '@hey-api/client-ofetch': HeyApiClientOfetchPlugin['Types']; '@hey-api/schemas': HeyApiSchemasPlugin['Types']; @@ -79,6 +82,7 @@ export const defaultPluginConfigs: { '@angular/common': angularCommon, '@hey-api/client-angular': heyApiClientAngular, '@hey-api/client-axios': heyApiClientAxios, + '@hey-api/client-nestjs': heyApiClientNestjs, '@hey-api/client-fetch': heyApiClientFetch, '@hey-api/client-ky': heyApiClientKy, '@hey-api/client-next': heyApiClientNext, diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 064647a2a4..010a0b2826 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -10,7 +10,8 @@ export type PluginClientNames = | '@hey-api/client-ky' | '@hey-api/client-next' | '@hey-api/client-nuxt' - | '@hey-api/client-ofetch'; + | '@hey-api/client-ofetch' + | '@hey-api/client-nestjs'; export type PluginValidatorNames = 'arktype' | 'valibot' | 'zod'; diff --git a/packages/openapi-ts/src/tsc/classes.ts b/packages/openapi-ts/src/tsc/classes.ts index 9c9c71147c..92e4d0e0bd 100644 --- a/packages/openapi-ts/src/tsc/classes.ts +++ b/packages/openapi-ts/src/tsc/classes.ts @@ -62,6 +62,7 @@ export const createConstructorDeclaration = ({ * Create a class method declaration. * @param accessLevel - the access level of the method. * @param comment - comment to add to function. + * @param isAsync - if the function is async. * @param isStatic - if the function is static. * @param multiLine - if it should be multi line. * @param name - name of the method. @@ -73,6 +74,7 @@ export const createConstructorDeclaration = ({ export const createMethodDeclaration = ({ accessLevel, comment, + isAsync = false, isStatic = false, multiLine = true, name, @@ -83,6 +85,7 @@ export const createMethodDeclaration = ({ }: { accessLevel?: AccessLevel; comment?: Comments; + isAsync?: boolean; isStatic?: boolean; multiLine?: boolean; name: string; @@ -95,6 +98,10 @@ export const createMethodDeclaration = ({ ? [createModifier({ keyword: accessLevel })] : []; + if (isAsync) { + modifiers.push(createModifier({ keyword: 'async' })); + } + if (isStatic) { modifiers.push(createModifier({ keyword: 'static' })); } diff --git a/packages/openapi-ts/src/tsc/index.ts b/packages/openapi-ts/src/tsc/index.ts index 1a9729f290..45417966de 100644 --- a/packages/openapi-ts/src/tsc/index.ts +++ b/packages/openapi-ts/src/tsc/index.ts @@ -25,6 +25,7 @@ export const tsc = { conditionalExpression: types.createConditionalExpression, constVariable: module.createConstVariable, constructorDeclaration: classes.createConstructorDeclaration, + defaultImportDeclaration: module.createDefaultImportDeclaration, enumDeclaration: types.createEnumDeclaration, exportAllDeclaration: module.createExportAllDeclaration, exportNamedDeclaration: module.createNamedExportDeclarations, diff --git a/packages/openapi-ts/src/tsc/module.ts b/packages/openapi-ts/src/tsc/module.ts index 0312b21242..926471175f 100644 --- a/packages/openapi-ts/src/tsc/module.ts +++ b/packages/openapi-ts/src/tsc/module.ts @@ -175,6 +175,33 @@ export const createConstVariable = ({ return statement; }; +/** + * Create an import declaration. Example: `import X from './y'`. + * @param name - import name + * @param module - module containing imports + * @returns ts.ImportDeclaration + */ +export const createDefaultImportDeclaration = ({ + module, + name, +}: { + module: string; + name: string; +}): ts.ImportDeclaration => { + const importClause = ts.factory.createImportClause( + false, // isTypeOnly + createIdentifier({ text: name }), // name (default import) + undefined, // namedBindings + ); + const moduleSpecifier = ots.string(module); + const statement = ts.factory.createImportDeclaration( + undefined, // modifiers + importClause, + moduleSpecifier, + ); + return statement; +}; + /** * Create a named import declaration. Example: `import { X } from './y'`. * @param imports - named exports to import diff --git a/packages/openapi-ts/src/tsc/types.ts b/packages/openapi-ts/src/tsc/types.ts index f87aee522a..166b5bc725 100644 --- a/packages/openapi-ts/src/tsc/types.ts +++ b/packages/openapi-ts/src/tsc/types.ts @@ -4,6 +4,7 @@ import { escapeName } from '~/utils/escape'; import { validTypescriptIdentifierRegExp } from '~/utils/regexp'; import type { Modifier } from './utils'; +import { createCallExpression } from './module'; import { addLeadingComments, type Comments, @@ -19,6 +20,10 @@ export type AccessLevel = 'private' | 'protected' | 'public'; export type FunctionParameter = | { accessLevel?: AccessLevel; + decorators?: { + args: any[]; + name: string; + }[]; default?: any; isReadOnly?: boolean; isRequired?: boolean; @@ -216,9 +221,26 @@ export const toParameterDeclarations = ( }); } - const modifiers = parameter.accessLevel - ? [createModifier({ keyword: parameter.accessLevel })] - : []; + const modifiers: ts.ModifierLike[] = []; + + if (parameter.decorators) { + modifiers.push( + ...parameter.decorators.map((decorator) => + ts.factory.createDecorator( + createCallExpression({ + functionName: decorator.name, + parameters: decorator.args + .map((arg) => toExpression({ value: arg })) + .filter(isType), + }), + ), + ), + ); + } + + if (parameter.accessLevel) { + modifiers.push(createModifier({ keyword: parameter.accessLevel })); + } if (parameter.isReadOnly) { modifiers.push(createModifier({ keyword: 'readonly' })); diff --git a/packages/openapi-ts/tsdown.config.ts b/packages/openapi-ts/tsdown.config.ts index e769c9dad4..5df58b4df8 100644 --- a/packages/openapi-ts/tsdown.config.ts +++ b/packages/openapi-ts/tsdown.config.ts @@ -43,6 +43,7 @@ export default defineConfig((options) => ({ 'client-next', 'client-nuxt', 'client-ofetch', + 'client-nestjs', ]; for (const pluginName of pluginNames) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 534dfd44cb..e7df1fa243 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1283,7 +1283,7 @@ importers: version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.45)(rollup@3.29.5)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) vue: specifier: '>=3.5.13' - version: 3.5.13(typescript@5.9.3) + version: 3.5.23(typescript@5.9.3) devDependencies: '@config/vite-base': specifier: workspace:* @@ -1355,6 +1355,9 @@ importers: '@config/vite-base': specifier: workspace:* version: link:../config-vite-base + '@nestjs/common': + specifier: 11.1.6 + version: 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/bun': specifier: 1.3.1 version: 1.3.1(@types/react@19.0.1) @@ -1378,7 +1381,7 @@ importers: version: 1.14.0 nuxt: specifier: 3.14.1592 - version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.45)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.45)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) ofetch: specifier: 1.4.1 version: 1.4.1 @@ -1448,6 +1451,12 @@ importers: '@hey-api/openapi-ts': specifier: workspace:* version: link:../../openapi-ts + '@nestjs/common': + specifier: 11.1.6 + version: 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': + specifier: 11.1.6 + version: 11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@pinia/colada': specifier: 0.17.7 version: 0.17.7(pinia@3.0.3(typescript@5.9.3)(vue@3.5.23(typescript@5.9.3)))(vue@3.5.23(typescript@5.9.3)) @@ -2805,6 +2814,9 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} + '@borewit/text-codec@0.1.1': + resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} + '@braidai/lang@1.1.2': resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==} @@ -4431,6 +4443,10 @@ packages: '@loaderkit/resolve@1.0.4': resolution: {integrity: sha512-rJzYKVcV4dxJv+vW6jlvagF8zvGxHJ2+HTr1e2qOejfmGhAApgJHl8Aog4mMszxceTRiKTTbnpgmTO1bEZHV/A==} + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -4587,6 +4603,37 @@ packages: '@neoconfetti/svelte@2.0.0': resolution: {integrity: sha512-n/Uu7/XmHc8w0uBci0QWBjgbRzLhfWsH8yPJ5pMaseIvzSwabXvB30nb3JjzEYNBp9uGt4eCeY7LUmxAjnJV8A==} + '@nestjs/common@11.1.6': + resolution: {integrity: sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==} + peerDependencies: + class-transformer: '>=0.4.1' + class-validator: '>=0.13.2' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/core@11.1.6': + resolution: {integrity: sha512-siWX7UDgErisW18VTeJA+x+/tpNZrJewjTBsRPF3JVxuWRuAB1kRoiJcxHgln8Lb5UY9NdvklITR84DUEXD0Cg==} + engines: {node: '>= 20'} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/microservices': ^11.0.0 + '@nestjs/platform-express': ^11.0.0 + '@nestjs/websockets': ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + '@netlify/binary-info@1.0.0': resolution: {integrity: sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw==} @@ -4791,6 +4838,11 @@ packages: '@nuxt/kit': ^3.13.1 nuxi: ^3.13.1 + '@nuxt/opencollective@0.4.1': + resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==} + engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} + hasBin: true + '@nuxt/schema@3.14.1592': resolution: {integrity: sha512-A1d/08ueX8stTXNkvGqnr1eEXZgvKn+vj6s7jXhZNWApUSqMgItU4VK28vrrdpKbjIPwq2SwhnGOHUYvN9HwCQ==} engines: {node: ^14.18.0 || >=16.10.0} @@ -6319,6 +6371,13 @@ packages: '@vue/composition-api': optional: true + '@tokenizer/inflate@0.2.7': + resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -6898,18 +6957,12 @@ packages: '@vue/compiler-sfc@3.5.13': resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} - '@vue/compiler-sfc@3.5.21': - resolution: {integrity: sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==} - '@vue/compiler-sfc@3.5.23': resolution: {integrity: sha512-3QTEUo4qg7FtQwaDJa8ou1CUikx5WTtZlY61rRRDu3lK2ZKrGoAGG8mvDgOpDsQ4A1bez9s+WtBB6DS2KuFCPw==} '@vue/compiler-ssr@3.5.13': resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} - '@vue/compiler-ssr@3.5.21': - resolution: {integrity: sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==} - '@vue/compiler-ssr@3.5.23': resolution: {integrity: sha512-Hld2xphbMjXs9Q9WKxPf2EqmE+Rq/FEDnK/wUBtmYq74HCV4XDdSCheAaB823OQXIIFGq9ig/RbAZkF9s4U0Ow==} @@ -8981,6 +9034,9 @@ packages: resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} engines: {node: '>=6'} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -9035,6 +9091,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-type@21.0.0: + resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==} + engines: {node: '>=20'} + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -9232,9 +9292,6 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.10.1: - resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} - get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} @@ -9906,6 +9963,10 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} + iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -10193,6 +10254,10 @@ packages: resolution: {integrity: sha512-SuHqzPl7mYStna8WRotY8XX/EUZBjjv3QyKIByeCLFfC9uXT/OIHByEcA07PzbMfQAM0KYJtLgtpMRlIe5dErQ==} hasBin: true + load-esm@1.0.2: + resolution: {integrity: sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==} + engines: {node: '>=13.2.0'} + loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} @@ -12604,6 +12669,10 @@ packages: strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + strtok3@10.3.4: + resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} + engines: {node: '>=18'} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -12840,6 +12909,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + token-types@6.1.1: + resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} + engines: {node: '>=14.16'} + tokenx@1.1.0: resolution: {integrity: sha512-KCjtiC2niPwTSuz4ktM82Ki5bjqBwYpssiHDsGr5BpejN/B3ksacRvrsdoxljdMIh2nCX78alnDkeemBmYUmTA==} @@ -13071,6 +13144,14 @@ packages: ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + ultrahtml@1.6.0: resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} @@ -14266,7 +14347,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0)) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.1) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14280,11 +14361,11 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) browserslist: 4.25.4 copy-webpack-plugin: 12.0.2(webpack@5.98.0) css-loader: 7.1.2(webpack@5.98.0) @@ -14304,7 +14385,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14317,8 +14398,8 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) - webpack-dev-server: 5.2.0(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) optionalDependencies: @@ -14354,7 +14435,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0)) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.1) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14368,11 +14449,11 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) browserslist: 4.25.4 copy-webpack-plugin: 12.0.2(webpack@5.98.0) css-loader: 7.1.2(webpack@5.98.0) @@ -14392,7 +14473,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14405,8 +14486,8 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) - webpack-dev-server: 5.2.0(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) optionalDependencies: @@ -14480,7 +14561,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14493,7 +14574,7 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.4) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) webpack-dev-server: 5.2.2(webpack@5.98.0) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) @@ -14530,7 +14611,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4)) + '@angular-devkit/build-webpack': 0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0) '@angular-devkit/core': 19.2.19(chokidar@4.0.3) '@angular/build': 19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(@angular/compiler@19.2.15)(@angular/platform-server@19.2.0(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.15)(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/animations@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.9.3)))(terser@5.39.0)(typescript@5.9.3)(yaml@2.8.1) '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3) @@ -14544,7 +14625,7 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.10) '@babel/runtime': 7.26.10 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) + '@ngtools/webpack': 19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) @@ -14568,7 +14649,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14581,7 +14662,7 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 webpack: 5.98.0(esbuild@0.25.4) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) webpack-dev-server: 5.2.2(webpack@5.98.0) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) @@ -14613,12 +14694,12 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0)': + '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0))': dependencies: '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.4) - webpack-dev-server: 5.2.0(webpack@5.98.0) + webpack: 5.98.0(esbuild@0.25.0) + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) transitivePeerDependencies: - chokidar @@ -14626,16 +14707,16 @@ snapshots: dependencies: '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar - '@angular-devkit/build-webpack@0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4))': + '@angular-devkit/build-webpack@0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0)': dependencies: '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar @@ -16616,6 +16697,8 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} + '@borewit/text-codec@0.1.1': {} + '@braidai/lang@1.1.2': {} '@changesets/apply-release-plan@7.0.13': @@ -17781,6 +17864,8 @@ snapshots: dependencies: '@braidai/lang': 1.1.2 + '@lukeed/csprng@1.1.0': {} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.28.3 @@ -17916,6 +18001,30 @@ snapshots: '@neoconfetti/svelte@2.0.0': {} + '@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + file-type: 21.0.0 + iterare: 1.2.1 + load-esm: 1.0.2 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + transitivePeerDependencies: + - supports-color + + '@nestjs/core@11.1.6(@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + '@nestjs/common': 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nuxt/opencollective': 0.4.1 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 8.2.0 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + '@netlify/binary-info@1.0.0': {} '@netlify/blobs@9.1.2': @@ -18034,23 +18143,23 @@ snapshots: '@next/swc-win32-x64-msvc@15.2.4': optional: true - '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': + '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0))': dependencies: '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) '@ngtools/webpack@19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) - '@ngtools/webpack@19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4))': + '@ngtools/webpack@19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3) typescript: 5.9.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -18127,32 +18236,32 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - magicast - supports-color - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) transitivePeerDependencies: - magicast - supports-color - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - magicast - supports-color @@ -18217,13 +18326,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18252,9 +18361,9 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 unimport: 3.14.6(rollup@4.50.0) - vite: 6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) - vite-plugin-vue-inspector: 5.3.2(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -18264,13 +18373,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18299,9 +18408,9 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 unimport: 3.14.6(rollup@4.50.0) - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -18311,13 +18420,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18346,9 +18455,9 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 unimport: 3.14.6(rollup@4.50.0) - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -18484,6 +18593,10 @@ snapshots: - typescript - vue-tsc + '@nuxt/opencollective@0.4.1': + dependencies: + consola: 3.4.2 + '@nuxt/schema@3.14.1592(magicast@0.3.5)(rollup@3.29.5)': dependencies: c12: 2.0.1(magicast@0.3.5) @@ -20169,6 +20282,16 @@ snapshots: vue: 3.5.23(typescript@5.9.3) vue-demi: 0.14.10(vue@3.5.23(typescript@5.9.3)) + '@tokenizer/inflate@0.2.7': + dependencies: + debug: 4.4.1 + fflate: 0.8.2 + token-types: 6.1.1 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -20949,7 +21072,7 @@ snapshots: '@vue-macros/common@1.16.1(vue@3.5.23(typescript@5.9.3))': dependencies: - '@vue/compiler-sfc': 3.5.21 + '@vue/compiler-sfc': 3.5.23 ast-kit: 1.4.3 local-pkg: 1.1.2 magic-string-ast: 0.7.1 @@ -20983,7 +21106,7 @@ snapshots: '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/parser': 7.28.5 - '@vue/compiler-sfc': 3.5.21 + '@vue/compiler-sfc': 3.5.23 transitivePeerDependencies: - supports-color @@ -21038,18 +21161,6 @@ snapshots: postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-sfc@3.5.21': - dependencies: - '@babel/parser': 7.28.5 - '@vue/compiler-core': 3.5.21 - '@vue/compiler-dom': 3.5.21 - '@vue/compiler-ssr': 3.5.21 - '@vue/shared': 3.5.21 - estree-walker: 2.0.2 - magic-string: 0.30.21 - postcss: 8.5.6 - source-map-js: 1.2.1 - '@vue/compiler-sfc@3.5.23': dependencies: '@babel/parser': 7.28.5 @@ -21067,11 +21178,6 @@ snapshots: '@vue/compiler-dom': 3.5.13 '@vue/shared': 3.5.13 - '@vue/compiler-ssr@3.5.21': - dependencies: - '@vue/compiler-dom': 3.5.21 - '@vue/shared': 3.5.21 - '@vue/compiler-ssr@3.5.23': dependencies: '@vue/compiler-dom': 3.5.23 @@ -21096,38 +21202,38 @@ snapshots: dependencies: '@vue/devtools-kit': 8.0.3 - '@vue/devtools-core@7.6.8(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + vite-hot-client: 0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) vue: 3.5.23(typescript@5.9.3) transitivePeerDependencies: - vite - '@vue/devtools-core@7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) + vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) vue: 3.5.23(typescript@5.9.3) transitivePeerDependencies: - vite - '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)) + vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) vue: 3.5.23(typescript@5.9.3) transitivePeerDependencies: - vite @@ -21775,14 +21881,14 @@ snapshots: '@babel/core': 7.26.10 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) - babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0): + babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)): dependencies: '@babel/core': 7.26.9 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.26.10): dependencies: @@ -22369,7 +22475,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) core-js-compat@3.45.1: dependencies: @@ -22442,7 +22548,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) css-select@5.2.2: dependencies: @@ -22692,7 +22798,7 @@ snapshots: detective-vue2@2.2.0(typescript@5.9.3): dependencies: '@dependents/detective-less': 5.0.1 - '@vue/compiler-sfc': 3.5.21 + '@vue/compiler-sfc': 3.5.23 detective-es6: 5.0.1 detective-sass: 6.0.1 detective-scss: 5.0.1 @@ -23206,8 +23312,8 @@ snapshots: '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.17.0(jiti@2.6.1)) @@ -23234,33 +23340,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 eslint: 9.17.0(jiti@2.6.1) - get-tsconfig: 4.10.1 + get-tsconfig: 4.13.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -23271,7 +23377,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -23717,6 +23823,8 @@ snapshots: fast-redact@3.5.0: {} + fast-safe-stringify@2.1.1: {} + fast-uri@3.1.0: {} fastify-openapi-glue@4.8.0: @@ -23783,6 +23891,15 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-type@21.0.0: + dependencies: + '@tokenizer/inflate': 0.2.7 + strtok3: 10.3.4 + token-types: 6.1.1 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + file-uri-to-path@1.0.0: {} fill-range@7.1.1: @@ -23995,10 +24112,6 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.10.1: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@4.13.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -24711,6 +24824,8 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + iterare@1.2.1: {} + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -24965,7 +25080,7 @@ snapshots: dependencies: less: 4.2.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) less@4.2.2: dependencies: @@ -24990,7 +25105,7 @@ snapshots: dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) light-my-request@6.6.0: dependencies: @@ -25073,6 +25188,8 @@ snapshots: '@lmdb/lmdb-win32-x64': 3.2.6 optional: true + load-esm@1.0.2: {} + loader-runner@4.3.0: {} loader-utils@2.0.4: @@ -25541,7 +25658,7 @@ snapshots: dependencies: schema-utils: 4.3.2 tapable: 2.2.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) minimalistic-assert@1.0.1: {} @@ -26025,7 +26142,7 @@ snapshots: '@unhead/shared': 1.11.20 '@unhead/ssr': 1.11.20 '@unhead/vue': 1.11.20(vue@3.5.23(typescript@5.9.3)) - '@vue/shared': 3.5.21 + '@vue/shared': 3.5.20 acorn: 8.14.0 c12: 2.0.1(magicast@0.3.5) chokidar: 4.0.3 @@ -26146,7 +26263,7 @@ snapshots: '@unhead/shared': 1.11.20 '@unhead/ssr': 1.11.20 '@unhead/vue': 1.11.20(vue@3.5.23(typescript@5.9.3)) - '@vue/shared': 3.5.21 + '@vue/shared': 3.5.20 acorn: 8.14.0 c12: 2.0.1(magicast@0.3.5) chokidar: 4.0.3 @@ -26255,10 +26372,10 @@ snapshots: - vue-tsc - xml2js - nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.45)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): + nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.45)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) + '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/telemetry': 2.6.6(magicast@0.3.5) @@ -26267,7 +26384,7 @@ snapshots: '@unhead/shared': 1.11.20 '@unhead/ssr': 1.11.20 '@unhead/vue': 1.11.20(vue@3.5.23(typescript@5.9.3)) - '@vue/shared': 3.5.21 + '@vue/shared': 3.5.20 acorn: 8.14.0 c12: 2.0.1(magicast@0.3.5) chokidar: 4.0.3 @@ -26314,7 +26431,7 @@ snapshots: unhead: 1.11.20 unimport: 3.14.6(rollup@4.50.0) unplugin: 1.16.1 - unplugin-vue-router: 0.10.9(rollup@4.50.0)(vue-router@4.5.0(vue@3.5.23(typescript@5.9.3)))(vue@3.5.23(typescript@5.9.3)) + unplugin-vue-router: 0.10.9(rollup@4.50.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.9.3)))(vue@3.5.23(typescript@5.9.3)) unstorage: 1.17.0(@netlify/blobs@9.1.2)(db0@0.3.2)(ioredis@5.7.0) untyped: 1.5.2 vue: 3.5.23(typescript@5.9.3) @@ -26376,10 +26493,10 @@ snapshots: - vue-tsc - xml2js - nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.45)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): + nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.45)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) + '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.23(typescript@5.9.3)) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/telemetry': 2.6.6(magicast@0.3.5) @@ -26388,7 +26505,7 @@ snapshots: '@unhead/shared': 1.11.20 '@unhead/ssr': 1.11.20 '@unhead/vue': 1.11.20(vue@3.5.23(typescript@5.9.3)) - '@vue/shared': 3.5.21 + '@vue/shared': 3.5.20 acorn: 8.14.0 c12: 2.0.1(magicast@0.3.5) chokidar: 4.0.3 @@ -26435,7 +26552,7 @@ snapshots: unhead: 1.11.20 unimport: 3.14.6(rollup@4.50.0) unplugin: 1.16.1 - unplugin-vue-router: 0.10.9(rollup@4.50.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.9.3)))(vue@3.5.23(typescript@5.9.3)) + unplugin-vue-router: 0.10.9(rollup@4.50.0)(vue-router@4.5.0(vue@3.5.23(typescript@5.9.3)))(vue@3.5.23(typescript@5.9.3)) unstorage: 1.17.0(@netlify/blobs@9.1.2)(db0@0.3.2)(ioredis@5.7.0) untyped: 1.5.2 vue: 3.5.23(typescript@5.9.3) @@ -27012,25 +27129,25 @@ snapshots: ts-node: 10.9.2(@types/node@22.10.5)(typescript@5.9.3) optional: true - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)): dependencies: cosmiconfig: 9.0.0(typescript@5.8.3) jiti: 1.21.7 postcss: 8.5.2 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - typescript - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 1.21.7 postcss: 8.5.2 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - typescript @@ -27855,7 +27972,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass: 1.85.0 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) sass@1.85.0: dependencies: @@ -28222,7 +28339,7 @@ snapshots: dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) source-map-support@0.5.21: dependencies: @@ -28439,6 +28556,10 @@ snapshots: dependencies: js-tokens: 9.0.1 + strtok3@10.3.4: + dependencies: + '@tokenizer/token': 0.3.0 + styled-jsx@5.1.6(react@19.0.0): dependencies: client-only: 0.0.1 @@ -28666,7 +28787,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) optionalDependencies: esbuild: 0.25.0 @@ -28677,7 +28798,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) optionalDependencies: esbuild: 0.25.4 @@ -28765,6 +28886,12 @@ snapshots: toidentifier@1.0.1: {} + token-types@6.1.1: + dependencies: + '@borewit/text-codec': 0.1.1 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + tokenx@1.1.0: {} toml@3.0.0: {} @@ -29010,6 +29137,12 @@ snapshots: ufo@1.6.1: {} + uid@2.0.2: + dependencies: + '@lukeed/csprng': 1.1.0 + + uint8array-extras@1.5.0: {} + ultrahtml@1.6.0: {} unbox-primitive@1.1.0: @@ -29490,10 +29623,6 @@ snapshots: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) vite-hot-client: 2.1.0(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)) - vite-hot-client@0.2.4(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): - dependencies: - vite: 6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) - vite-hot-client@0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) @@ -29502,6 +29631,10 @@ snapshots: dependencies: vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) + vite-hot-client@0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): + dependencies: + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite-hot-client@2.1.0(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) @@ -29646,7 +29779,7 @@ snapshots: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29657,14 +29790,14 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.2 - vite: 6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29675,14 +29808,14 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.2 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29693,7 +29826,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.2 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: @@ -29730,7 +29863,7 @@ snapshots: - supports-color - vue - vite-plugin-vue-inspector@5.3.2(vite@6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29741,11 +29874,11 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 6.4.1(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29756,11 +29889,11 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29771,7 +29904,7 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -30235,7 +30368,7 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-dev-middleware@7.4.2(webpack@5.98.0): + webpack-dev-middleware@7.4.2(webpack@5.98.0(esbuild@0.25.0)): dependencies: colorette: 2.0.20 memfs: 4.38.2 @@ -30244,9 +30377,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-server@5.2.0(webpack@5.98.0): + webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -30273,10 +30406,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - bufferutil - debug @@ -30311,10 +30444,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - bufferutil - debug @@ -30332,7 +30465,7 @@ snapshots: webpack-subresource-integrity@5.1.0(webpack@5.98.0): dependencies: typed-assert: 1.0.9 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) webpack-virtual-modules@0.6.2: {}