fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #3109 from hey-api/fix/sdk-class-conflict

Structure API

authored by

Lubos and committed by
GitHub
fc1514a1 4689b684

+7452 -5007
+13
.changeset/solid-cars-accept.md
··· 1 + --- 2 + '@hey-api/openapi-ts': minor 3 + --- 4 + 5 + **plugin(sdk)**: **BREAKING**: Structure API 6 + 7 + ### Structure API 8 + 9 + The [SDK plugin](https://heyapi.dev/openapi-ts/plugins/sdk) now implements the Structure API, enabling more complex structures and fixing several known issues. 10 + 11 + Some Structure APIs are incompatible with the previous configuration, most notably the `methodNameBuilder` function, which accepted the operation object as an argument. You can read the [SDK Output](https://heyapi.dev/openapi-ts/plugins/sdk#output) section to familiarize yourself with the Structure API. 12 + 13 + Please [open an issue](https://github.com/hey-api/openapi-ts/issues) if you're unable to migrate your configuration to the new syntax.
+5
.changeset/tiny-clouds-dress.md
··· 1 + --- 2 + '@hey-api/codegen-core': minor 3 + --- 4 + 5 + **core**: Structure API
+5
.changeset/two-ways-help.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + **renderer**: correctly render default import
+13
.changeset/wet-radios-arrive.md
··· 1 + --- 2 + '@hey-api/openapi-ts': minor 3 + --- 4 + 5 + **plugin(@angular/common)**: **BREAKING**: Structure API 6 + 7 + ### Structure API 8 + 9 + The [Angular plugin](https://heyapi.dev/openapi-ts/plugins/angular) now implements the Structure API, enabling more complex structures and fixing several known issues. 10 + 11 + Some Structure APIs are incompatible with the previous configuration, most notably the `methodNameBuilder` function, which accepted the operation object as an argument. You can read the [SDK Output](https://heyapi.dev/openapi-ts/plugins/sdk#output) section to familiarize yourself with the Structure API. 12 + 13 + Please [open an issue](https://github.com/hey-api/openapi-ts/issues) if you're unable to migrate your configuration to the new syntax.
+107 -61
dev/openapi-ts.config.ts
··· 4 4 5 5 // @ts-ignore 6 6 import { customClientPlugin } from '@hey-api/custom-client/plugin'; 7 - // @ts-ignore 8 - import { defineConfig, reserved, utils } from '@hey-api/openapi-ts'; 7 + import { 8 + // @ts-ignore 9 + defineConfig, 10 + // @ts-ignore 11 + OperationPath, 12 + // @ts-ignore 13 + OperationStrategy, 14 + // @ts-ignore 15 + reserved, 16 + // @ts-ignore 17 + utils, 18 + } from '@hey-api/openapi-ts'; 9 19 10 20 // @ts-ignore 11 21 import { myClientPlugin } from '../packages/openapi-ts-tests/main/test/custom/client/plugin'; ··· 36 46 // openapi: '3.1.0', 37 47 // paths: {}, 38 48 // }, 39 - path: path.resolve( 40 - getSpecsPath(), 41 - // '3.0.x', 42 - '3.1.x', 43 - // 'circular.yaml', 44 - // 'dutchie.json', 45 - // 'enum-names-values.yaml', 46 - // 'full.yaml', 47 - 'integer-formats.yaml', 48 - // 'invalid', 49 - // 'object-property-names.yaml', 50 - // 'openai.yaml', 51 - // 'opencode.yaml', 52 - // 'pagination-ref.yaml', 53 - // 'schema-const.yaml', 54 - // 'sdk-instance.yaml', 55 - // 'sdk-method-class-conflict.yaml', 56 - // 'sdk-nested-classes.yaml', 57 - // 'sdk-nested-conflict.yaml', 58 - // 'string-with-format.yaml', 59 - // 'transformers.json', 60 - // 'transformers-recursive.json', 61 - // 'type-format.yaml', 62 - // 'validators.yaml', 63 - // 'validators-circular-ref.json', 64 - // 'validators-circular-ref-2.yaml', 65 - // 'zoom-video-sdk.json', 66 - ), 49 + // path: path.resolve( 50 + // getSpecsPath(), 51 + // // '2.0.x', 52 + // // '3.0.x', 53 + // '3.1.x', 54 + // // 'circular.yaml', 55 + // // 'dutchie.json', 56 + // // 'enum-names-values.yaml', 57 + // 'full.yaml', 58 + // // 'integer-formats.yaml', 59 + // // 'invalid', 60 + // // 'object-property-names.yaml', 61 + // // 'openai.yaml', 62 + // // 'opencode.yaml', 63 + // // 'pagination-ref.yaml', 64 + // // 'schema-const.yaml', 65 + // // 'sdk-instance.yaml', 66 + // // 'sdk-method-class-conflict.yaml', 67 + // // 'sdk-nested-classes.yaml', 68 + // // 'sdk-nested-conflict.yaml', 69 + // // 'string-with-format.yaml', 70 + // // 'transformers.json', 71 + // // 'transformers-recursive.json', 72 + // // 'type-format.yaml', 73 + // // 'validators.yaml', 74 + // // 'validators-circular-ref.json', 75 + // // 'validators-circular-ref-2.yaml', 76 + // // 'zoom-video-sdk.json', 77 + // ), 67 78 // path: 'https://get.heyapi.dev/hey-api/backend?branch=main&version=1.0.0', 68 79 // path: 'http://localhost:4000/', 69 80 // path: 'http://localhost:8000/openapi.json', 70 81 // path: 'https://mongodb-mms-prod-build-server.s3.amazonaws.com/openapi/2caffd88277a4e27c95dcefc7e3b6a63a3b03297-v2-2023-11-15.json', 71 - // path: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', 82 + path: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', 72 83 // watch: { 73 84 // enabled: true, 74 85 // interval: 500, ··· 106 117 // suffix: '.meh', 107 118 // }, 108 119 // format: 'prettier', 120 + // header: null, 121 + header: [ 122 + '/* eslint-disable */', 123 + '// This file is auto-generated by @hey-api/openapi-ts', 124 + ], 109 125 // importFileExtension: '.js', 110 126 // indexFile: false, 111 127 // lint: 'eslint', 112 - nameConflictResolver({ attempt, baseName }) { 113 - // console.log('resolving conflict for:', { attempt, baseName }); 114 - return attempt === 0 ? baseName : `${baseName}_N${attempt + 1}`; 115 - }, 128 + // nameConflictResolver({ attempt, baseName }) { 129 + // // console.log('resolving conflict for:', { attempt, baseName }); 130 + // return attempt === 0 ? baseName : `${baseName}_N${attempt + 1}`; 131 + // }, 116 132 path: path.resolve(__dirname, '.gen'), 117 133 // preferExportAll: true, 118 134 resolveModuleName: (moduleName) => { ··· 260 276 // name: '你們_errors_{{name}}', 261 277 // }, 262 278 // exportFromIndex: false, 263 - name: '@hey-api/typescript', 279 + // name: '@hey-api/typescript', 264 280 // requests: '我們_data_{{name}}', 265 281 // responses: { 266 282 // name: '我_responses_{{name}}', ··· 274 290 // }, 275 291 }, 276 292 { 277 - // asClass: true, 278 293 // auth: false, 279 - // classNameBuilder: '{{name}}', 280 - // classNameBuilder: '{{name}}Service', 281 - // classStructure: 'off', 282 294 // client: false, 283 295 // getSignature: ({ fields, signature, operation }) => { 284 296 // // ... 285 297 // fields.unwrap('path') 286 298 // }, 287 299 // include... 288 - instance: true, 289 300 name: '@hey-api/sdk', 290 - // operationId: false, 301 + operations: { 302 + // container: 'object', 303 + // containerName: { 304 + // // casing: 'snake_case', 305 + // name: 'OpencodeClient', 306 + // }, 307 + containerName: 'PetStore', 308 + // nesting(operation) { 309 + // if (operation.path === '/pet/{petId}' || operation.path === '/pet') { 310 + // return ['pet', operation.operationId?.replace(/Pet/, '') || operation.method.toLocaleLowerCase()]; 311 + // } 312 + // return OperationPath.fromOperationId()(operation); 313 + // }, 314 + // methodName: { 315 + // // casing: 'SCREAMING_SNAKE_CASE', 316 + // name: '{{name}}Yummy', 317 + // }, 318 + // methods: 'static', 319 + // nesting: 'id', 320 + // segmentName: '{{name}}Seggy', 321 + // strategy: 'single', 322 + // strategy(operation) { 323 + // const locations = OperationStrategy.byTags({ 324 + // fallback: 'default', 325 + // path: OperationPath.fromOperationId({ 326 + // delimiters: /[./]/, 327 + // }), 328 + // })(operation) 329 + // return locations.map((loc) => { 330 + // if (loc[0] && loc[1] && loc[0] === loc[1]) { 331 + // return loc.slice(1); 332 + // } 333 + // return loc; 334 + // }); 335 + // }, 336 + // strategyDefaultTag: 'DaxLikedThis', 337 + }, 291 338 // paramsStructure: 'flat', 292 339 // responseStyle: 'data', 293 340 // signature: 'auto', ··· 295 342 // signature: 'object', 296 343 // transformer: '@hey-api/transformers', 297 344 // transformer: true, 298 - // validator: 'valibot', 345 + validator: 'valibot', 299 346 // validator: { 300 347 // request: 'zod', 301 348 // response: 'zod', ··· 304 351 symbols: { 305 352 // getFilePath: (symbol) => { 306 353 // if (symbol.name) { 307 - // return utils.stringCase({ 308 - // case: 'camelCase', 309 - // value: symbol.name, 310 - // }); 354 + // return utils.toCase(symbol.name, 'camelCase'); 311 355 // } 312 356 // return; 313 357 // }, ··· 351 395 // name: '{{name}}MO', 352 396 // name: 'options', 353 397 }, 354 - name: '@tanstack/react-query', 398 + // name: '@tanstack/react-query', 355 399 queryKeys: { 356 400 // name: '{{name}}QK', 357 401 // name: 'options', ··· 394 438 { 395 439 // case: 'SCREAMING_SNAKE_CASE', 396 440 // comments: false, 397 - definitions: 'z{{name}}', 441 + // definitions: 'z{{name}}', 398 442 exportFromIndex: true, 399 443 // metadata: true, 400 444 name: 'valibot', ··· 402 446 // case: 'PascalCase', 403 447 // name: '{{name}}Data', 404 448 // }, 405 - responses: { 406 - // case: 'snake_case', 407 - name: 'z{{name}}TestResponse', 408 - }, 449 + // responses: { 450 + // // case: 'snake_case', 451 + // name: 'z{{name}}TestResponse', 452 + // }, 409 453 // webhooks: { 410 454 // name: 'q{{name}}CoolWebhook', 411 455 // }, ··· 421 465 symbols: { 422 466 // getFilePath: (symbol) => { 423 467 // if (symbol.name) { 424 - // return utils.stringCase({ 425 - // case: 'camelCase', 426 - // value: symbol.name, 427 - // }); 468 + // return utils.toCase(symbol.name, 'camelCase'); 428 469 // } 429 470 // return; 430 471 // }, ··· 496 537 }, 497 538 exportFromIndex: true, 498 539 metadata: true, 499 - name: 'zod', 540 + // name: 'zod', 500 541 // requests: { 501 542 // // case: 'SCREAMING_SNAKE_CASE', 502 543 // // name: 'z{{name}}TestData', ··· 605 646 { 606 647 exportFromIndex: true, 607 648 httpRequests: { 608 - asClass: true, 649 + // enabled: false, 650 + // containerName: 'AngularTest', 651 + // segmentName: '{{name}}Seggy', 652 + // strategy: 'byTags', 609 653 }, 610 654 httpResources: { 611 - asClass: true, 655 + // enabled: false, 656 + // segmentName: '{{name}}Seggy', 657 + strategy: 'byTags', 612 658 }, 613 659 // name: '@angular/common', 614 660 }, 615 661 { 616 662 exportFromIndex: true, 617 663 // mutationOptions: '{{name}}Mutationssss', 618 - name: '@pinia/colada', 664 + // name: '@pinia/colada', 619 665 // queryOptions: { 620 666 // name: '{{name}}Queryyyyy', 621 667 // },
+6 -1
dev/package.json
··· 1 1 { 2 - "name": "@test/openapi-ts-playground", 2 + "name": "@test/playground", 3 3 "version": "0.0.0", 4 4 "private": true, 5 5 "type": "module", ··· 10 10 "dev": "ts-node ./playground.ts" 11 11 }, 12 12 "devDependencies": { 13 + "@angular/common": "19.2.17", 14 + "@angular/core": "19.2.17", 13 15 "@hey-api/codegen-core": "workspace:*", 14 16 "@hey-api/openapi-ts": "workspace:*", 17 + "@opencode-ai/sdk": "1.0.170", 15 18 "@pinia/colada": "0.19.1", 16 19 "@tanstack/angular-query-experimental": "5.73.3", 17 20 "@tanstack/react-query": "5.73.3", ··· 19 22 "@tanstack/svelte-query": "5.73.3", 20 23 "@tanstack/vue-query": "5.73.3", 21 24 "arktype": "2.1.29", 25 + "nuxt": "3.14.1592", 22 26 "swr": "2.3.8", 23 27 "typescript": "5.9.3", 24 28 "valibot": "1.2.0", 29 + "vue": "3.5.25", 25 30 "zod": "4.1.12" 26 31 } 27 32 }
+7 -13
dev/playground.ts
··· 1 1 import type { DefinePlugin, IR } from '@hey-api/openapi-ts'; 2 - 3 - import { authSet } from './.gen/index.ts'; 2 + import { createOpencode } from '@opencode-ai/sdk'; 4 3 5 4 type MyPluginConfig = { readonly name: 'myplugin' }; 6 5 type MyPlugin = DefinePlugin<MyPluginConfig>; ··· 15 14 }); 16 15 }; 17 16 18 - console.log( 19 - authSet({ 20 - auth: { 21 - access: '', 22 - expires: 1, 23 - refresh: '', 24 - type: 'oauth', 25 - }, 26 - id: '123', 27 - }), 28 - ); 17 + async function run() { 18 + const { client, server } = await createOpencode(); 19 + console.log(client, server); 20 + } 21 + 22 + run();
+3 -3
docs/openapi-ts/community/contributing.md
··· 7 7 8 8 ## Foreword 9 9 10 - Hey API is building an OpenAPI to TypeScript code generator ecosystem. It’s trusted by thousands of companies – from YC startups to Fortune 500 enterprises – and powers products used by millions worldwide. 10 + Hey API is building an OpenAPI to TypeScript code generator ecosystem. It's trusted by thousands of companies – from YC startups to Fortune 500 enterprises – and powers products used by millions worldwide. 11 11 12 - We welcome contributors of all backgrounds and experience levels. Whether you’re fixing a typo or building a new feature, your input matters. If you need guidance, help with technical writing, or want to bring a feature idea to life, we’re here to support you. 12 + We welcome contributors of all backgrounds and experience levels. Whether you're fixing a typo or building a new feature, your input matters. If you need guidance, help with technical writing, or want to bring a feature idea to life, we're here to support you. 13 13 14 14 ::: tip 15 15 ··· 39 39 40 40 Ready to write some code? We have dedicated guides to help you [build](/openapi-ts/community/contributing/building), [develop](/openapi-ts/community/contributing/developing), and [test](/openapi-ts/community/contributing/testing) your feature before it's released. 41 41 42 - We are excited to see what you’ll contribute! 42 + We are excited to see what you'll contribute!
+30
docs/openapi-ts/configuration/output.md
··· 286 286 287 287 ::: 288 288 289 + ## File Header 290 + 291 + The generated output includes a notice in every file warning that any modifications will be lost when the files are regenerated. You can customize or disable this notice using the `header` option. 292 + 293 + ::: code-group 294 + 295 + <!-- prettier-ignore-start --> 296 + ```js [config] 297 + export default { 298 + input: 'hey-api/backend', // sign up at app.heyapi.dev 299 + output: { 300 + header: [ 301 + '/* eslint-disable */', // [!code ++] 302 + '// This file is auto-generated by @hey-api/openapi-ts', // [!code ++] 303 + ], 304 + path: 'src/client', 305 + }, 306 + }; 307 + ``` 308 + <!-- prettier-ignore-end --> 309 + 310 + ```ts [example] 311 + /* eslint-disable */ 312 + // This file is auto-generated by @hey-api/openapi-ts 313 + 314 + /** ... */ 315 + ``` 316 + 317 + ::: 318 + 289 319 ## TSConfig Path 290 320 291 321 We use the [TSConfig file](https://www.typescriptlang.org/tsconfig/) to generate output matching your project's settings. By default, we attempt to find a TSConfig file starting from the location of the `@hey-api/openapi-ts` configuration file and traversing up.
+2 -2
docs/openapi-ts/configuration/parser.md
··· 494 494 495 495 ## Hooks 496 496 497 - Hooks affect runtime behavior but aren’t tied to any single plugin. They can be configured globally via `hooks` or per plugin through the `~hooks` property. 497 + Hooks affect runtime behavior but aren't tied to any single plugin. They can be configured globally via `hooks` or per plugin through the `~hooks` property. 498 498 499 499 ::: code-group 500 500 ··· 527 527 528 528 ### Operations {#hooks-operations} 529 529 530 - Each operation has a list of classifiers that can include `query`, `mutation`, both, or none. Plugins may use these values to decide whether to generate specific output. For example, you usually don’t want to generate [TanStack Query options](/openapi-ts/plugins/tanstack-query#queries) for PATCH operations. 530 + Each operation has a list of classifiers that can include `query`, `mutation`, both, or none. Plugins may use these values to decide whether to generate specific output. For example, you usually don't want to generate [TanStack Query options](/openapi-ts/plugins/tanstack-query#queries) for PATCH operations. 531 531 532 532 #### Query operations {#hooks-query-operations} 533 533
+8
docs/openapi-ts/migrating.md
··· 13 13 14 14 The [Resolvers API](/openapi-ts/plugins/concepts/resolvers) has been simplified and expanded to provide a more consistent behavior across plugins. You can view a few common examples on the [Resolvers](/openapi-ts/plugins/concepts/resolvers) page. 15 15 16 + ### Structure API 17 + 18 + The [SDK plugin](/openapi-ts/plugins/sdk) and [Angular plugin](/openapi-ts/plugins/angular) now implement the Structure API, enabling more complex structures and fixing several known issues. 19 + 20 + Some Structure APIs are incompatible with the previous configuration, most notably the `methodNameBuilder` function, which accepted the operation object as an argument. You can read the [SDK Output](/openapi-ts/plugins/sdk#output) section to familiarize yourself with the Structure API. 21 + 22 + Please [open an issue](https://github.com/hey-api/openapi-ts/issues) if you're unable to migrate your configuration to the new syntax. 23 + 16 24 ## v0.89.0 17 25 18 26 ### Prefer named exports
+173 -85
docs/openapi-ts/plugins/sdk.md
··· 1 1 --- 2 - title: SDK 3 - description: Learn about files generated with @hey-api/openapi-ts. 2 + title: SDK Plugin 3 + description: Generate SDKs from OpenAPI with the SDK plugin for openapi-ts. Fully compatible with validators, transformers, and all core features. 4 4 --- 5 5 6 - # SDKs 6 + # SDK 7 + 8 + ### About 9 + 10 + The SDK plugin generates a high-level, ergonomic API layer on top of the low-level HTTP client. 7 11 8 - SDKs are located in the `sdk.gen.ts` file. SDKs are abstractions on top of clients and serve the same purpose. By default, `@hey-api/openapi-ts` will generate a flat SDK layer. Your choice to use SDKs depends on personal preferences and bundle size considerations. 12 + It exposes typed functions or classes for each operation, with built-in auth handling and optional request and response validation. 9 13 10 - ### Flat SDKs 14 + ## Features 15 + 16 + - high-level SDK layer on top of the HTTP client 17 + - typed functions or classes per operation 18 + - built-in authentication handling 19 + - optional request and response validation 11 20 12 - This is the default setting. Flat SDKs support tree-shaking and can lead to reduced bundle size over duplicated client calls. The function names are generated from operation IDs or operation location. 21 + ## Installation 13 22 14 - ### Class SDKs 23 + In your [configuration](/openapi-ts/get-started), add `@hey-api/sdk` to your plugins and you'll be ready to generate SDK artifacts. :tada: 15 24 16 - Class SDKs do not support tree-shaking which will lead to increased bundle sizes, but some people prefer this option for syntax reasons. The class names are generated from operation tags and method names are generated from operation IDs or operation location. 25 + ```js 26 + export default { 27 + input: 'hey-api/backend', // sign up at app.heyapi.dev 28 + output: 'src/client', 29 + plugins: [ 30 + // ...other plugins 31 + '@hey-api/sdk', // [!code ++] 32 + ], 33 + }; 34 + ``` 17 35 18 - ### No SDKs 36 + ## Output 19 37 20 - If you prefer to use clients directly or do not need the SDK layer, define `plugins` manually and omit the `@hey-api/sdk` plugin. Type support for clients is currently limited due to popularity of other options. If you'd like to use this option and need better types, please [open an issue](https://github.com/hey-api/openapi-ts/issues). 38 + The SDK plugin supports a wide range of configuration options. This guide focuses on two main SDK formats: tree-shakeable functions and instantiable classes, but you can apply the same concepts to create more advanced configurations. 21 39 22 - ## Configuration 40 + ## Flat 23 41 24 - You can modify the contents of `sdk.gen.ts` by configuring the `@hey-api/sdk` plugin. Note that you must specify the default plugins to preserve the default output. 42 + This is the default setting. Flat SDKs support tree-shaking, which can lead to a reduced bundle size. You select flat mode by setting `operations.strategy` to `flat`. 25 43 26 44 ::: code-group 27 45 28 - ```js [flat] 46 + ```ts [example] 47 + import type { AddPetData } from './types.gen'; 48 + 49 + export const addPet = (options: Options<AddPetData>) => { 50 + /** ... */ 51 + }; 52 + ``` 53 + 54 + ```js [config] 29 55 export default { 30 56 input: 'hey-api/backend', // sign up at app.heyapi.dev 31 57 output: 'src/client', 32 58 plugins: [ 33 59 // ...other plugins 34 60 { 35 - asClass: false, // default // [!code ++] 36 61 name: '@hey-api/sdk', 62 + operations: { 63 + strategy: 'flat', // [!code ++] 64 + }, 37 65 }, 38 66 ], 39 67 }; 40 68 ``` 41 69 42 - ```js [class] 70 + ::: 71 + 72 + ## Instance 73 + 74 + Class SDKs do not support tree-shaking, which results in a larger bundle size, but you may prefer their syntax. You select class mode by setting `operations.strategy` to `single`. 75 + 76 + ::: code-group 77 + 78 + ```ts [example] 79 + import type { AddPetData } from './types.gen'; 80 + 81 + export class Sdk extends HeyApiClient { 82 + public addPet(options: Options<AddPetData>) { 83 + /** ... */ 84 + } 85 + } 86 + ``` 87 + 88 + ```js [config] 43 89 export default { 44 90 input: 'hey-api/backend', // sign up at app.heyapi.dev 45 91 output: 'src/client', 46 92 plugins: [ 47 93 // ...other plugins 48 94 { 49 - asClass: true, // [!code ++] 50 95 name: '@hey-api/sdk', 96 + operations: { 97 + strategy: 'single', // [!code ++] 98 + }, 51 99 }, 52 100 ], 53 101 }; 54 102 ``` 55 103 56 - ```js [none] 104 + ::: 105 + 106 + ### Name 107 + 108 + As shown above, by default our SDK class is called `Sdk`. The first thing you'll likely want to do is change this to your preferred name, which you can do using `operation.containerName`. 109 + 110 + ::: code-group 111 + 112 + ```ts [example] 113 + import { client } from './client.gen'; 114 + import type { AddPetData, AddPetErrors, AddPetResponses } from './types.gen'; 115 + 116 + export class PetStore extends HeyApiClient { 117 + // [!code ++] 118 + /** ... */ 119 + } 120 + ``` 121 + 122 + ```js [config] 57 123 export default { 58 124 input: 'hey-api/backend', // sign up at app.heyapi.dev 59 125 output: 'src/client', 60 126 plugins: [ 61 - '@hey-api/typescript', 62 - '@hey-api/sdk', // [!code --] 127 + // ...other plugins 128 + { 129 + name: '@hey-api/sdk', 130 + operations: { 131 + containerName: 'PetStore', // [!code ++] 132 + strategy: 'single', 133 + }, 134 + }, 63 135 ], 64 136 }; 65 137 ``` 66 138 67 139 ::: 68 140 69 - ## Output 141 + ### Structure 70 142 71 - Below are different outputs depending on your chosen style. No SDKs approach will not generate the `sdk.gen.ts` file. 143 + While we try to infer the SDK structure from `operationId` fields, you'll likely want to customize it further. You can do this using `operations.nesting`. 72 144 73 - ::: code-group 145 + Similar to the `operations.strategy` option, we provide a few presets. However, you gain the most control by providing your own function. 74 146 75 - ```ts [flat] 76 - import type { Options } from './client'; 77 - import { client as _heyApiClient } from './client.gen'; 78 - import type { AddPetData, AddPetError, AddPetResponse } from './types.gen'; 147 + To demonstrate the power of this feature, let's nest a few endpoints inside a `Pet` class and rename them. Our original `addPet()` method will now become `pet.add()`. Notice that we use the built-in `OperationPath.fromOperationId()` helper to handle the remaining operations. 79 148 80 - export const addPet = (options: Options<AddPetData>) => 81 - (options?.client ?? _heyApiClient).post<AddPetResponse, AddPetError>({ 82 - url: '/pet', 83 - ...options, 84 - }); 85 - ``` 149 + ::: code-group 86 150 87 - ```ts [class] 88 - import type { Options } from './client'; 89 - import { client as _heyApiClient } from './client.gen'; 90 - import type { AddPetData, AddPetError, AddPetResponse } from './types.gen'; 151 + ```ts [example] 152 + import { client } from './client.gen'; 153 + import type { AddPetData, AddPetErrors, AddPetResponses } from './types.gen'; 154 + 155 + export class Pet extends HeyApiClient { 156 + public add(options: Options<PostPetData>) { 157 + // [!code ++] 158 + /** ... */ 159 + } 160 + } 91 161 92 - export class PetService { 93 - public static addPet(options: Options<AddPetData>) { 94 - return (options?.client ?? _heyApiClient).post<AddPetResponse, AddPetError>( 95 - { 96 - url: '/pet', 97 - ...options, 98 - }, 99 - ); 162 + export class PetStore extends HeyApiClient { 163 + get pet(): Pet { 164 + // [!code ++] 165 + /** ... */ 100 166 } 101 167 } 102 168 ``` 103 169 170 + <!-- prettier-ignore-start --> 171 + ```js [config] 172 + export default { 173 + input: 'hey-api/backend', // sign up at app.heyapi.dev 174 + output: 'src/client', 175 + plugins: [ 176 + // ...other plugins 177 + { 178 + name: '@hey-api/sdk', 179 + operations: { 180 + containerName: 'PetStore', 181 + nesting(operation) { 182 + if (operation.path === '/pet/{petId}' || operation.path === '/pet') { // [!code ++] 183 + return ['pet', operation.operationId?.replace(/Pet/, '') // [!code ++] 184 + || operation.method.toLocaleLowerCase()]; // [!code ++] 185 + } // [!code ++] 186 + return OperationPath.fromOperationId()(operation); // [!code ++] 187 + }, 188 + strategy: 'single', 189 + }, 190 + }, 191 + ], 192 + }; 193 + ``` 194 + <!-- prettier-ignore-end --> 195 + 104 196 ::: 105 197 106 - ## Usage 198 + ## Auth 107 199 108 - This is how you'd make the same request using each approach. 200 + Most APIs require some form of authentication, which is why the SDK plugin provides built-in auth mechanisms by default. All you need to do is return the data from the `auth()` function, and the SDK will handle serialization and encoding for you. There are several ways to do this, for example on the client instance. 109 201 110 202 ::: code-group 111 203 112 - ```ts [flat] 113 - import { addPet } from './client/sdk.gen'; 204 + ```ts [example] 205 + import { client } from './client.gen'; 114 206 115 - addPet({ 116 - body: { 117 - name: 'Kitty', 207 + client.setConfig({ 208 + auth() { 209 + return '<token>'; 118 210 }, 119 211 }); 120 212 ``` 121 213 122 - ```ts [class] 123 - import { PetService } from './client/sdk.gen'; 124 - 125 - PetService.addPet({ 126 - body: { 127 - name: 'Kitty', 128 - }, 129 - }); 214 + ```js [config] 215 + export default { 216 + input: 'hey-api/backend', // sign up at app.heyapi.dev 217 + output: 'src/client', 218 + plugins: [ 219 + // ...other plugins 220 + { 221 + auth: true, // [!code ++] 222 + name: '@hey-api/sdk', 223 + }, 224 + ], 225 + }; 130 226 ``` 131 227 132 - ```ts [none] 133 - import { client } from './client/client'; 134 - 135 - client.post({ 136 - body: { 137 - name: 'Kitty', 138 - }, 139 - url: '/pet', 140 - }); 141 - ``` 228 + ::: 142 229 230 + ::: info 231 + The SDK plugin currently supports only the `bearer` and `basic` auth schemes. [Open an issue](https://github.com/hey-api/openapi-ts/issues) if you'd like support for additional mechanisms. 143 232 ::: 144 233 145 234 ## Validators 146 235 147 - There are two ways to configure validators. If you only want to add validators to your SDKs, set `sdk.validator` to a validator plugin name. This will implicitly add the selected plugin with default values. 236 + Validating data at runtime comes with a performance cost, which is why it's not enabled by default. To enable validation, set `validator` to `zod` or one of the available [validator plugins](/openapi-ts/validators). This will implicitly add the selected plugin with default values. 148 237 149 - For a more granular approach, add a validator plugin and set `sdk.validator` to the plugin name or `true` to automatically select a plugin. Until you customize the validator plugin, both approaches will produce the same default output. 238 + For a more granular approach, manually add a validator plugin and set `validator` to the plugin name or `true` to automatically select a compatible plugin. Until you customize the validator plugin, both approaches will produce the same default output. 150 239 151 240 ::: code-group 152 241 153 - ```js [sdk] 154 - export default { 155 - input: 'hey-api/backend', // sign up at app.heyapi.dev 156 - output: 'src/client', 157 - plugins: [ 158 - { 159 - name: '@hey-api/sdk', 160 - validator: 'zod', // [!code ++] 161 - }, 162 - ], 163 - }; 242 + ```ts [example] 243 + import * as v from 'valibot'; 244 + 245 + export const addPet = (options: Options<AddPetData>) => 246 + (options.client ?? client).post<AddPetResponses, AddPetErrors>({ 247 + requestValidator: async (data) => await v.parseAsync(vAddPetData, data), // [!code ++] 248 + responseValidator: async (data) => 249 + await v.parseAsync(vAddPetResponse, data), // [!code ++] 250 + /** ... */ 251 + }); 164 252 ``` 165 253 166 - ```js [validator] 254 + ```js [config] 167 255 export default { 168 256 input: 'hey-api/backend', // sign up at app.heyapi.dev 169 257 output: 'src/client', 170 258 plugins: [ 171 259 { 172 260 name: '@hey-api/sdk', 173 - validator: true, // or 'zod' // [!code ++] 261 + validator: true, // or 'valibot' // [!code ++] 174 262 }, 175 263 { 176 - name: 'zod', // [!code ++] 264 + name: 'valibot', // customize (optional) // [!code ++] 177 265 // other options 178 266 }, 179 267 ],
+2 -1
examples/openapi-ts-angular-common/openapi-ts.config.ts
··· 17 17 exportFromIndex: true, 18 18 httpRequests: true, 19 19 httpResources: { 20 - asClass: true, 20 + containerName: '{{name}}ServiceResources', 21 + strategy: 'byTags', 21 22 }, 22 23 name: '@angular/common', 23 24 },
+4 -1
examples/openapi-ts-angular/openapi-ts.config.ts
··· 12 12 '@hey-api/client-angular', 13 13 '@hey-api/schemas', 14 14 { 15 - asClass: true, 16 15 name: '@hey-api/sdk', 16 + operations: { 17 + containerName: '{{name}}Service', 18 + strategy: 'byTags', 19 + }, 17 20 }, 18 21 { 19 22 enums: 'javascript',
+13 -3
packages/codegen-core/src/__tests__/exports.test.ts
··· 3 3 import * as index from '../index'; 4 4 5 5 const constExports = [ 6 - 'debug', 7 6 'defaultExtensions', 8 7 'defaultNameConflictResolvers', 9 8 'File', ··· 14 13 'isRef', 15 14 'isSymbol', 16 15 'isSymbolRef', 16 + 'log', 17 17 'nodeBrand', 18 18 'Project', 19 19 'ref', ··· 27 27 // Type-level test: will fail to compile if any type export is missing or renamed 28 28 export type _TypeExports = [ 29 29 index.AnalysisContext, 30 - index.AstContext, 31 30 index.BindingKind, 32 31 index.ExportMember, 33 32 index.ExportModule, ··· 36 35 index.FileIn, 37 36 index.FromRef<any>, 38 37 index.FromRefs<any>, 39 - index.IProject, 40 38 index.ImportMember, 41 39 index.ImportModule, 40 + index.IProject, 42 41 index.Language, 43 42 index.NameConflictResolver, 44 43 index.NameConflictResolvers, 45 44 index.Node, 45 + index.NodeName, 46 + index.NodeNameSanitizer, 47 + index.NodeRelationship, 48 + index.NodeScope, 46 49 index.Output, 47 50 index.Project, 48 51 index.ProjectRenderMeta, ··· 50 53 index.Refs<any>, 51 54 index.RenderContext, 52 55 index.Renderer, 56 + index.StructureInsert, 57 + index.StructureItem, 58 + index.StructureLocation, 59 + index.StructureModel, 60 + index.StructureNode, 61 + index.StructureShell, 62 + index.StructureShellResult, 53 63 index.Symbol, 54 64 index.SymbolIdentifier, 55 65 index.SymbolIn,
-1
packages/codegen-core/src/__tests__/project.test.ts
··· 48 48 const renderer = file.renderer!; 49 49 50 50 expect(renderer.render).toHaveBeenCalledWith({ 51 - astContext: expect.any(Object), 52 51 file, 53 52 meta: { hello: true }, 54 53 project: p,
+9 -10
packages/codegen-core/src/bindings.d.ts
··· 32 32 export interface ImportMember { 33 33 /** Whether this import is type-only. */ 34 34 isTypeOnly: boolean; 35 - /** Import flavor. */ 36 - kind: BindingKind; 37 35 /** 38 36 * The name this symbol will have locally in this file. 39 37 * This is where aliasing is applied: ··· 47 45 sourceName: string; 48 46 } 49 47 50 - export type ImportModule = Pick<ImportMember, 'isTypeOnly'> & { 51 - /** Source file. */ 52 - from: File; 53 - /** List of symbols imported from this module. */ 54 - imports: Array<ImportMember>; 55 - /** Namespace import: `import * as name from 'module'`. Mutually exclusive with `imports`. */ 56 - namespaceImport?: string; 57 - }; 48 + export type ImportModule = Pick<ImportMember, 'isTypeOnly'> & 49 + Pick<Partial<ImportMember>, 'localName'> & { 50 + /** Source file. */ 51 + from: File; 52 + /** List of symbols imported from this module. */ 53 + imports: Array<ImportMember>; 54 + /** Import flavor. */ 55 + kind: BindingKind; 56 + };
-36
packages/codegen-core/src/debug.ts
··· 1 - import colors from 'ansi-colors'; 2 - // @ts-expect-error 3 - import colorSupport from 'color-support'; 4 - 5 - colors.enabled = colorSupport().hasBasic; 6 - 7 - const DEBUG_GROUPS = { 8 - analyzer: colors.greenBright, 9 - dsl: colors.cyanBright, 10 - file: colors.yellowBright, 11 - registry: colors.blueBright, 12 - symbol: colors.magentaBright, 13 - } as const; 14 - 15 - export function debug(message: string, group: keyof typeof DEBUG_GROUPS) { 16 - const value = process.env.DEBUG; 17 - if (!value) return; 18 - 19 - const groups = value.split(',').map((x) => x.trim().toLowerCase()); 20 - 21 - if ( 22 - !( 23 - groups.includes('*') || 24 - groups.includes('heyapi:*') || 25 - groups.includes(`heyapi:${group}`) || 26 - groups.includes(group) 27 - ) 28 - ) { 29 - return; 30 - } 31 - 32 - const color = DEBUG_GROUPS[group] ?? colors.whiteBright; 33 - const prefix = color(`heyapi:${group}`); 34 - 35 - console.debug(`${prefix} ${message}`); 36 - }
+8 -7
packages/codegen-core/src/files/file.ts
··· 2 2 3 3 import type { ExportModule, ImportModule } from '../bindings'; 4 4 import { fileBrand } from '../brands'; 5 - import { debug } from '../debug'; 6 5 import type { Language } from '../languages/types'; 6 + import { log } from '../log'; 7 7 import type { INode } from '../nodes/node'; 8 - import type { NameScopes } from '../planner/types'; 8 + import type { NameScopes } from '../planner/scope'; 9 9 import type { IProject } from '../project/types'; 10 10 import type { Renderer } from '../renderer'; 11 11 import type { IFileIn } from './types'; 12 12 13 - export class File { 13 + export class File<Node extends INode = INode> { 14 14 /** 15 15 * Exports from this file. 16 16 */ ··· 42 42 /** 43 43 * Syntax nodes contained in this file. 44 44 */ 45 - private _nodes: Array<INode> = []; 45 + private _nodes: Array<Node> = []; 46 46 /** 47 47 * Renderer assigned to this file. 48 48 */ ··· 81 81 * Read-only accessor for the file extension. 82 82 */ 83 83 get extension(): string | undefined { 84 + if (this.external) return; 84 85 if (this._extension) return this._extension; 85 86 const language = this.language; 86 87 const extension = language ? this.project.extensions[language] : undefined; ··· 135 136 const name = this._logicalFilePath.split('/').pop(); 136 137 if (name) return name; 137 138 const message = `File ${this.toString()} has no name`; 138 - debug(message, 'file'); 139 + log.debug(message, 'file'); 139 140 throw new Error(message); 140 141 } 141 142 142 143 /** 143 144 * Syntax nodes contained in this file. 144 145 */ 145 - get nodes(): ReadonlyArray<INode> { 146 + get nodes(): ReadonlyArray<Node> { 146 147 return [...this._nodes]; 147 148 } 148 149 ··· 170 171 /** 171 172 * Add a syntax node to the file. 172 173 */ 173 - addNode(node: INode): void { 174 + addNode(node: Node): void { 174 175 this._nodes.push(node); 175 176 node.file = this; 176 177 }
+17 -3
packages/codegen-core/src/index.ts
··· 5 5 ImportModule, 6 6 } from './bindings'; 7 7 export { nodeBrand, symbolBrand } from './brands'; 8 - export { debug } from './debug'; 9 8 export type { 10 9 IProjectRenderMeta as ProjectRenderMeta, 11 10 ISymbolMeta as SymbolMeta, ··· 20 19 Language, 21 20 NameConflictResolvers, 22 21 } from './languages/types'; 23 - export type { AstContext } from './nodes/context'; 24 - export type { INode as Node } from './nodes/node'; 22 + export { log } from './log'; 23 + export type { 24 + INode as Node, 25 + NodeName, 26 + NodeNameSanitizer, 27 + NodeRelationship, 28 + NodeScope, 29 + } from './nodes/node'; 25 30 export type { IOutput as Output } from './output'; 26 31 export { 27 32 simpleNameConflictResolver, ··· 36 41 export { fromRef, fromRefs, isRef, ref, refs } from './refs/refs'; 37 42 export type { FromRef, FromRefs, Ref, Refs } from './refs/types'; 38 43 export type { RenderContext, Renderer } from './renderer'; 44 + export { StructureModel } from './structure/model'; 45 + export { StructureNode } from './structure/node'; 46 + export type { 47 + StructureInsert, 48 + StructureItem, 49 + StructureLocation, 50 + StructureShell, 51 + StructureShellResult, 52 + } from './structure/types'; 39 53 export { Symbol } from './symbols/symbol'; 40 54 export type { 41 55 BindingKind,
+115
packages/codegen-core/src/log.ts
··· 1 + import colors from 'ansi-colors'; 2 + // @ts-expect-error 3 + import colorSupport from 'color-support'; 4 + 5 + colors.enabled = colorSupport().hasBasic; 6 + 7 + /** 8 + * Accepts a value or a readonly array of values of type T. 9 + */ 10 + export type MaybeArray<T> = T | ReadonlyArray<T>; 11 + 12 + /** 13 + * Accepts a value or a function returning a value. 14 + */ 15 + export type MaybeFunc<T extends (...args: Array<any>) => any> = 16 + | T 17 + | ReturnType<T>; 18 + 19 + const DEBUG_NAMESPACE = 'heyapi'; 20 + 21 + const NO_WARNINGS = /^(1|true|yes|on)$/i.test( 22 + process.env.HEYAPI_DISABLE_WARNINGS ?? '', 23 + ); 24 + 25 + const DebugGroups = { 26 + analyzer: colors.greenBright, 27 + dsl: colors.cyanBright, 28 + file: colors.yellowBright, 29 + registry: colors.blueBright, 30 + symbol: colors.magentaBright, 31 + } as const; 32 + 33 + const WarnGroups = { 34 + deprecated: colors.magentaBright, 35 + } as const; 36 + 37 + let cachedDebugGroups: Set<string> | undefined; 38 + function getDebugGroups(): Set<string> { 39 + if (cachedDebugGroups) return cachedDebugGroups; 40 + 41 + const value = process.env.DEBUG; 42 + cachedDebugGroups = new Set( 43 + value ? value.split(',').map((x) => x.trim().toLowerCase()) : [], 44 + ); 45 + 46 + return cachedDebugGroups; 47 + } 48 + 49 + /** 50 + * Tracks which deprecations have been shown to avoid spam. 51 + */ 52 + const shownDeprecations = new Set<string>(); 53 + 54 + function debug(message: string, group: keyof typeof DebugGroups) { 55 + const groups = getDebugGroups(); 56 + if ( 57 + !( 58 + groups.has('*') || 59 + groups.has(`${DEBUG_NAMESPACE}:*`) || 60 + groups.has(`${DEBUG_NAMESPACE}:${group}`) || 61 + groups.has(group) 62 + ) 63 + ) { 64 + return; 65 + } 66 + 67 + const color = DebugGroups[group] ?? colors.whiteBright; 68 + const prefix = color(`${DEBUG_NAMESPACE}:${group}`); 69 + 70 + console.debug(`${prefix} ${message}`); 71 + } 72 + 73 + function warn(message: string, group: keyof typeof WarnGroups) { 74 + if (NO_WARNINGS) return; 75 + 76 + const color = WarnGroups[group] ?? colors.yellowBright; 77 + 78 + console.warn(color(`${message}`)); 79 + } 80 + 81 + function warnDeprecated({ 82 + context, 83 + field, 84 + replacement, 85 + }: { 86 + context?: string; 87 + field: string; 88 + replacement?: MaybeFunc<(field: string) => MaybeArray<string>>; 89 + }) { 90 + const key = context 91 + ? `${context}:${field}:${JSON.stringify(replacement)}` 92 + : `${field}:${JSON.stringify(replacement)}`; 93 + 94 + if (shownDeprecations.has(key)) return; 95 + shownDeprecations.add(key); 96 + 97 + let message = `\`${field}\` is deprecated.`; 98 + 99 + if (replacement) { 100 + const reps = 101 + typeof replacement === 'function' ? replacement(field) : replacement; 102 + const repArray = reps instanceof Array ? reps : [reps]; 103 + const repString = repArray.map((r) => `\`${r}\``).join(' or '); 104 + message += ` Use ${repString} instead.`; 105 + } 106 + 107 + const prefix = context ? `[${context}] ` : ''; 108 + warn(`${prefix}${message}`, 'deprecated'); 109 + } 110 + 111 + export const log = { 112 + debug, 113 + warn, 114 + warnDeprecated, 115 + };
-11
packages/codegen-core/src/nodes/context.d.ts
··· 1 - import type { INode } from './node'; 2 - 3 - /** 4 - * Context passed to `.toAst()` methods. 5 - */ 6 - export type AstContext = { 7 - /** 8 - * Returns the canonical node for accessing the provided node. 9 - */ 10 - getAccess<T extends INode>(node: T): T; 11 - };
+29 -6
packages/codegen-core/src/nodes/node.d.ts
··· 1 1 import type { File } from '../files/file'; 2 2 import type { Language } from '../languages/types'; 3 3 import type { IAnalysisContext } from '../planner/types'; 4 + import type { Ref } from '../refs/types'; 4 5 import type { Symbol } from '../symbols/symbol'; 5 - import type { AstContext } from './context'; 6 + 7 + export type MaybeRef<T> = T | Ref<T>; 8 + 9 + export type NodeName = MaybeRef<Symbol | string | number>; 10 + 11 + export type NodeNameSanitizer = (name: string) => string; 12 + 13 + export type NodeRelationship = 'container' | 'reference'; 14 + 15 + export type NodeScope = 'type' | 'value'; 6 16 7 17 export interface INode<T = unknown> { 8 18 /** Perform semantic analysis. */ 9 19 analyze(ctx: IAnalysisContext): void; 20 + /** Create a shallow copy of this node. */ 21 + clone(): this; 10 22 /** Whether this node is exported from its file. */ 11 23 exported?: boolean; 12 24 /** The file this node belongs to. */ 13 25 file?: File; 14 26 /** The programming language associated with this node */ 15 27 language: Language; 16 - /** Parent node in the syntax tree. */ 17 - parent?: INode; 18 - /** Root node of the syntax tree. */ 19 - root?: INode; 28 + /** The display name of this node. */ 29 + readonly name: Ref<NodeName> & { 30 + set(value: NodeName): void; 31 + toString(): string; 32 + }; 33 + /** Optional function to sanitize the node name. */ 34 + readonly nameSanitizer?: NodeNameSanitizer; 35 + /** Whether this node is a root node in the file. */ 36 + root?: boolean; 37 + /** The scope of this node. */ 38 + scope?: NodeScope; 39 + /** Semantic children in the structure hierarchy. */ 40 + structuralChildren?: Map<INode, NodeRelationship>; 41 + /** Semantic parents in the structure hierarchy. */ 42 + structuralParents?: Map<INode, NodeRelationship>; 20 43 /** The symbol associated with this node. */ 21 44 symbol?: Symbol; 22 45 /** Convert this node into AST representation. */ 23 - toAst(ctx: AstContext): T; 46 + toAst(): T; 24 47 /** Brand used for renderer dispatch. */ 25 48 readonly '~brand': string; 26 49 }
+73 -19
packages/codegen-core/src/planner/analyzer.ts
··· 1 1 import { isNodeRef, isSymbolRef } from '../guards'; 2 - import type { INode } from '../nodes/node'; 2 + import type { INode, NodeRelationship } from '../nodes/node'; 3 3 import { fromRef, isRef, ref } from '../refs/refs'; 4 4 import type { Ref } from '../refs/types'; 5 5 import type { Symbol } from '../symbols/symbol'; 6 - import type { IAnalysisContext, Input, NameScopes, Scope } from './types'; 6 + import type { NameScopes, Scope } from './scope'; 7 + import { createScope } from './scope'; 8 + import type { IAnalysisContext, Input } from './types'; 7 9 8 - const createScope = (parent?: Scope): Scope => ({ 9 - children: [], 10 - localNames: new Map(), 11 - parent, 12 - symbols: [], 13 - }); 10 + export class AnalysisContext implements IAnalysisContext { 11 + /** 12 + * Stack of parent nodes during analysis. 13 + * 14 + * The top of the stack is the current semantic container. 15 + */ 16 + private _parentStack: Array<INode> = []; 14 17 15 - export class AnalysisContext implements IAnalysisContext { 18 + scope: Scope; 16 19 scopes: Scope = createScope(); 17 20 symbol?: Symbol; 18 - scope: Scope = this.scopes; 19 21 20 - constructor(symbol?: Symbol) { 21 - this.symbol = symbol; 22 + constructor(node: INode) { 23 + this._parentStack.push(node); 24 + this.scope = this.scopes; 25 + this.symbol = node.symbol; 26 + } 27 + 28 + /** 29 + * Get the current semantic parent (top of stack). 30 + */ 31 + get currentParent(): INode | undefined { 32 + return this._parentStack[this._parentStack.length - 1]; 33 + } 34 + 35 + /** 36 + * Register a child node under the current parent. 37 + */ 38 + addChild(child: INode, relationship: NodeRelationship = 'container'): void { 39 + const parent = this.currentParent; 40 + if (!parent) return; 41 + 42 + if (!parent.structuralChildren) { 43 + parent.structuralChildren = new Map(); 44 + } 45 + parent.structuralChildren.set(child, relationship); 46 + 47 + if (!child.structuralParents) { 48 + child.structuralParents = new Map(); 49 + } 50 + child.structuralParents.set(parent, relationship); 22 51 } 23 52 24 53 addDependency(symbol: Ref<Symbol>): void { ··· 28 57 } 29 58 30 59 analyze(input: Input): void { 31 - const v = isRef(input) ? input : ref(input); 32 - if (isSymbolRef(v)) { 33 - this.addDependency(v); 34 - } else if (isNodeRef(v)) { 35 - fromRef(v).analyze(this); 60 + const value = isRef(input) ? input : ref(input); 61 + if (isSymbolRef(value)) { 62 + const symbol = fromRef(value); 63 + // avoid adding self as child 64 + if (symbol.node && this.currentParent !== symbol.node) { 65 + this.addChild(symbol.node, 'reference'); 66 + } 67 + this.addDependency(value); 68 + } else if (isNodeRef(value)) { 69 + const node = fromRef(value); 70 + this.addChild(node, 'container'); 71 + this.pushParent(node); 72 + node.analyze(this); 73 + this.popParent(); 36 74 } 37 75 } 38 76 ··· 57 95 return names; 58 96 } 59 97 98 + /** 99 + * Pop the current semantic parent. 100 + * Call this when exiting a container node. 101 + */ 102 + popParent(): void { 103 + this._parentStack.pop(); 104 + } 105 + 60 106 popScope(): void { 61 107 this.scope = this.scope.parent ?? this.scope; 62 108 } 63 109 110 + /** 111 + * Push a node as the current semantic parent. 112 + */ 113 + pushParent(node: INode): void { 114 + this._parentStack.push(node); 115 + } 116 + 64 117 pushScope(): void { 65 - const scope = createScope(this.scope); 118 + const scope = createScope({ parent: this.scope }); 66 119 this.scope.children.push(scope); 67 120 this.scope = scope; 68 121 } ··· 90 143 const cached = this.nodeCache.get(node); 91 144 if (cached) return cached; 92 145 93 - const ctx = new AnalysisContext(node.symbol); 146 + node.root = true; 147 + const ctx = new AnalysisContext(node); 94 148 node.analyze(ctx); 95 149 96 150 this.nodeCache.set(node, ctx);
+92 -55
packages/codegen-core/src/planner/planner.ts
··· 3 3 import type { ExportModule, ImportModule } from '../bindings'; 4 4 import type { IProjectRenderMeta } from '../extensions'; 5 5 import type { File } from '../files/file'; 6 - import type { IFileIn } from '../files/types'; 6 + import type { INode } from '../nodes/node'; 7 7 import { canShareName } from '../project/namespace'; 8 8 import type { IProject } from '../project/types'; 9 9 import { fromRef } from '../refs/refs'; ··· 12 12 import type { SymbolKind } from '../symbols/types'; 13 13 import type { AnalysisContext } from './analyzer'; 14 14 import { Analyzer } from './analyzer'; 15 - import type { AssignOptions, NameScopes } from './types'; 15 + import type { AssignOptions, Scope } from './scope'; 16 + import { createScope } from './scope'; 16 17 17 18 const isTypeOnlyKind = (kind: SymbolKind) => 18 19 kind === 'type' || kind === 'interface'; ··· 47 48 const symbol = node.symbol; 48 49 if (!symbol) return; 49 50 50 - const file = this.project.files.register(this.symbolToFileIn(symbol)); 51 + const file = this.project.files.register({ 52 + external: false, 53 + language: node.language, 54 + logicalFilePath: 55 + symbol.getFilePath?.(symbol) || this.project.defaultFileName, 56 + }); 51 57 file.addNode(node); 52 58 symbol.setFile(file); 53 59 for (const exportFrom of symbol.exportFrom) { ··· 60 66 ctx.walkScopes((dependency) => { 61 67 const dep = fromRef(dependency); 62 68 if (dep.external && dep.isCanonical && !dep.file) { 63 - const file = this.project.files.register(this.symbolToFileIn(dep)); 69 + const file = this.project.files.register({ 70 + external: true, 71 + language: dep.node?.language, 72 + logicalFilePath: dep.external, 73 + }); 64 74 dep.setFile(file); 65 75 } 66 76 }); ··· 76 86 this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => { 77 87 const symbol = node.symbol; 78 88 if (!symbol) return; 79 - this.assignTopLevelName(symbol, ctx); 89 + this.assignTopLevelName({ ctx, node, symbol }); 80 90 }); 81 91 82 92 this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => { ··· 86 96 const dep = fromRef(dependency); 87 97 // top-level or external symbol 88 98 if (dep.file) return; 89 - this.assignLocalName(dep, ctx, { 90 - scopesToUpdate: [file.allNames], 99 + // TODO: pass node 100 + this.assignLocalName({ 101 + ctx, 102 + file, 103 + scopesToUpdate: [createScope({ localNames: file.allNames })], 104 + symbol: dep, 91 105 }); 92 106 }); 93 107 }); ··· 102 116 */ 103 117 private resolveFilePaths(meta?: IProjectRenderMeta): void { 104 118 for (const file of this.project.files.registered()) { 105 - if (file.external) continue; 119 + if (file.external) { 120 + file.setFinalPath(file.logicalFilePath); 121 + continue; 122 + } 106 123 const finalName = this.project.fileName?.(file.name) || file.name; 107 124 file.setName(finalName); 108 125 const finalPath = file.finalPath; 109 126 if (finalPath) { 110 127 file.setFinalPath(path.resolve(this.project.root, finalPath)); 111 128 } 112 - const ctx: Omit<RenderContext, 'astContext'> = { 113 - file, 114 - meta, 115 - project: this.project, 116 - }; 129 + const ctx: RenderContext = { file, meta, project: this.project }; 117 130 const renderer = this.project.renderers.find((r) => r.supports(ctx)); 118 131 if (renderer) file.setRenderer(renderer); 119 132 } ··· 166 179 }); 167 180 exp.setFile(target); 168 181 sourceFile.set(exp.id, file); 169 - this.assignTopLevelName(exp, ctx); 182 + // TODO: pass node 183 + this.assignTopLevelName({ ctx, symbol: exp }); 170 184 171 185 let entry = fileMap.get(exp.finalName); 172 186 if (!entry) { ··· 253 267 if (!dep.file || dep.file.id === file.id) return; 254 268 255 269 if (dep.external) { 256 - this.assignTopLevelName(dep, ctx); 270 + // TODO: pass node 271 + this.assignTopLevelName({ ctx, symbol: dep }); 257 272 } 258 273 259 274 const fromFileId = dep.file.id; ··· 272 287 name: dep.finalName, 273 288 }); 274 289 imp.setFile(file); 275 - this.assignTopLevelName(imp, ctx, { 276 - scope: imp.file!.allNames, 290 + // TODO: pass node 291 + this.assignTopLevelName({ 292 + ctx, 293 + scope: createScope({ localNames: imp.file!.allNames }), 294 + symbol: imp, 277 295 }); 278 296 entry = { 279 297 dep, ··· 298 316 from: source, 299 317 imports: [], 300 318 isTypeOnly: true, 319 + kind: 'named', 301 320 }; 302 321 } 303 322 const isTypeOnly = [...entry.kinds].every((kind) => ··· 305 324 ); 306 325 if (entry.symbol.importKind === 'namespace') { 307 326 imp.imports = []; 308 - imp.namespaceImport = entry.symbol.finalName; 327 + imp.kind = 'namespace'; 328 + imp.localName = entry.symbol.finalName; 329 + } else if (entry.symbol.importKind === 'default') { 330 + imp.kind = 'default'; 331 + imp.localName = entry.symbol.finalName; 309 332 } else { 310 333 imp.imports.push({ 311 334 isTypeOnly, 312 - kind: entry.symbol.importKind, 313 335 localName: entry.symbol.finalName, 314 336 sourceName: entry.dep.finalName, 315 337 }); ··· 334 356 * Supports optional overrides for the naming scope and scopes to update. 335 357 */ 336 358 private assignTopLevelName( 337 - symbol: Symbol, 338 - ctx: AnalysisContext, 339 - options?: Partial<AssignOptions>, 359 + args: Partial<AssignOptions> & { 360 + ctx: AnalysisContext; 361 + node?: INode; 362 + symbol: Symbol; 363 + }, 340 364 ): void { 341 - if (!symbol.file) return; 342 - this.assignSymbolName(symbol, { 343 - scope: options?.scope ?? symbol.file.topLevelNames, 365 + if (!args.symbol.file) return; 366 + this.assignSymbolName({ 367 + ...args, 368 + file: args.symbol.file, 369 + scope: 370 + args?.scope ?? 371 + createScope({ localNames: args.symbol.file.topLevelNames }), 344 372 scopesToUpdate: [ 345 - symbol.file.allNames, 346 - ctx.scopes.localNames, 347 - ...(options?.scopesToUpdate ?? []), 373 + createScope({ localNames: args.symbol.file.allNames }), 374 + args.ctx.scopes, 375 + ...(args?.scopesToUpdate ?? []), 348 376 ], 349 377 }); 350 378 } ··· 357 385 * Updates all provided name scopes accordingly. 358 386 */ 359 387 private assignLocalName( 360 - symbol: Symbol, 361 - ctx: AnalysisContext, 362 - options: Pick<Partial<AssignOptions>, 'scope'> & 363 - Pick<AssignOptions, 'scopesToUpdate'>, 388 + args: Pick<Partial<AssignOptions>, 'scope'> & 389 + Pick<AssignOptions, 'scopesToUpdate'> & { 390 + ctx: AnalysisContext; 391 + /** The file the symbol belongs to. */ 392 + file: File; 393 + node?: INode; 394 + symbol: Symbol; 395 + }, 364 396 ): void { 365 - this.assignSymbolName(symbol, { 366 - scope: options.scope ?? ctx.localNames(ctx.scope), 367 - scopesToUpdate: options.scopesToUpdate, 397 + this.assignSymbolName({ 398 + ...args, 399 + scope: args.scope ?? args.ctx.scope, 368 400 }); 369 401 } 370 402 ··· 375 407 * 376 408 * Updates all specified name scopes with the assigned final name. 377 409 */ 378 - private assignSymbolName(symbol: Symbol, options: AssignOptions): void { 410 + private assignSymbolName( 411 + args: AssignOptions & { 412 + ctx: AnalysisContext; 413 + /** The file the symbol belongs to. */ 414 + file: File; 415 + node?: INode; 416 + symbol: Symbol; 417 + }, 418 + ): void { 419 + const { ctx, file, node, scope, scopesToUpdate, symbol } = args; 379 420 if (this.cacheResolvedNames.has(symbol.id)) return; 380 421 381 422 const baseName = symbol.name; 382 - let finalName = symbol.nameSanitizer?.(baseName) ?? baseName; 423 + let finalName = 424 + node?.nameSanitizer?.(baseName) ?? 425 + symbol.node?.nameSanitizer?.(baseName) ?? 426 + baseName; 383 427 let attempt = 1; 384 428 429 + const localNames = ctx.localNames(scope); 385 430 while (true) { 386 - const kinds = [...(options.scope.get(finalName) ?? [])]; 431 + const kinds = [...(localNames.get(finalName) ?? [])]; 387 432 388 433 const ok = kinds.every((kind) => canShareName(symbol.kind, kind)); 389 434 if (ok) break; 390 435 391 - const language = symbol.node?.language || symbol.file?.language; 436 + const language = node?.language || symbol.node?.language || file.language; 392 437 const resolver = 393 438 (language ? this.project.nameConflictResolvers[language] : undefined) ?? 394 439 this.project.defaultNameConflictResolver; ··· 397 442 throw new Error(`Unresolvable name conflict: ${symbol.toString()}`); 398 443 } 399 444 400 - finalName = symbol.nameSanitizer?.(resolvedName) ?? resolvedName; 445 + finalName = 446 + node?.nameSanitizer?.(resolvedName) ?? 447 + symbol.node?.nameSanitizer?.(resolvedName) ?? 448 + resolvedName; 401 449 attempt = attempt + 1; 402 450 } 403 451 404 452 symbol.setFinalName(finalName); 405 453 this.cacheResolvedNames.add(symbol.id); 406 - const updateScopes = [options.scope, ...options.scopesToUpdate]; 454 + const updateScopes = [scope, ...scopesToUpdate]; 407 455 for (const scope of updateScopes) { 408 456 this.updateScope(symbol, scope); 409 457 } ··· 414 462 * 415 463 * Ensures the name scope tracks all kinds associated with a given name. 416 464 */ 417 - private updateScope(symbol: Symbol, scope: NameScopes): void { 465 + private updateScope(symbol: Symbol, scope: Scope): void { 418 466 const name = symbol.finalName; 419 - const cache = scope.get(name) ?? new Set<SymbolKind>(); 467 + const cache = scope.localNames.get(name) ?? new Set(); 420 468 cache.add(symbol.kind); 421 - scope.set(name, cache); 422 - } 423 - 424 - private symbolToFileIn(symbol: Symbol): IFileIn { 425 - return { 426 - external: Boolean(symbol.external), 427 - language: symbol.node?.language, 428 - logicalFilePath: 429 - symbol.external || 430 - symbol.getFilePath?.(symbol) || 431 - this.project.defaultFileName, 432 - } satisfies IFileIn; 469 + scope.localNames.set(name, cache); 433 470 } 434 471 }
+35
packages/codegen-core/src/planner/scope.ts
··· 1 + import type { Ref } from '../refs/types'; 2 + import type { Symbol } from '../symbols/symbol'; 3 + import type { SymbolKind } from '../symbols/types'; 4 + 5 + export type NameScopes = Map<string, Set<SymbolKind>>; 6 + 7 + export type Scope = { 8 + /** Child scopes. */ 9 + children: Array<Scope>; 10 + /** Resolved names in this scope. */ 11 + localNames: NameScopes; 12 + /** Parent scope, if any. */ 13 + parent?: Scope; 14 + /** Symbols registered in this scope. */ 15 + symbols: Array<Ref<Symbol>>; 16 + }; 17 + 18 + export type AssignOptions = { 19 + /** The primary scope in which to assign a symbol's final name. */ 20 + scope: Scope; 21 + /** Additional scopes to update as side effects when assigning a symbol's final name. */ 22 + scopesToUpdate: ReadonlyArray<Scope>; 23 + }; 24 + 25 + export const createScope = ( 26 + args: { 27 + localNames?: NameScopes; 28 + parent?: Scope; 29 + } = {}, 30 + ): Scope => ({ 31 + children: [], 32 + localNames: args.localNames || new Map(), 33 + parent: args.parent, 34 + symbols: [], 35 + });
+1 -21
packages/codegen-core/src/planner/types.d.ts
··· 1 1 import type { Ref } from '../refs/types'; 2 2 import type { Symbol } from '../symbols/symbol'; 3 - import type { SymbolKind } from '../symbols/types'; 4 - 5 - export type AssignOptions = { 6 - /** The primary scope in which to assign a symbol's final name. */ 7 - scope: NameScopes; 8 - /** Additional scopes to update as side effects when assigning a symbol's final name. */ 9 - scopesToUpdate: ReadonlyArray<NameScopes>; 10 - }; 3 + import type { NameScopes, Scope } from './scope'; 11 4 12 5 export type Input = Ref<object> | object | string | number | undefined; 13 - 14 - export type NameScopes = Map<string, Set<SymbolKind>>; 15 6 16 7 export type NameConflictResolver = (args: { 17 8 attempt: number; 18 9 baseName: string; 19 10 }) => string | null; 20 - 21 - export type Scope = { 22 - /** Child scopes. */ 23 - children: Array<Scope>; 24 - /** Resolved names in this scope. */ 25 - localNames: NameScopes; 26 - /** Parent scope, if any. */ 27 - parent?: Scope; 28 - /** Symbols registered in this scope. */ 29 - symbols: Array<Ref<Symbol>>; 30 - }; 31 11 32 12 export interface IAnalysisContext { 33 13 /** Register a dependency on another symbol. */
+2 -13
packages/codegen-core/src/project/project.ts
··· 5 5 import { defaultExtensions } from '../languages/extensions'; 6 6 import { defaultNameConflictResolvers } from '../languages/resolvers'; 7 7 import type { Extensions, NameConflictResolvers } from '../languages/types'; 8 - import type { AstContext } from '../nodes/context'; 9 8 import { NodeRegistry } from '../nodes/registry'; 10 9 import type { IOutput } from '../output'; 11 10 import { Planner } from '../planner/planner'; ··· 61 60 render(meta?: IProjectRenderMeta): ReadonlyArray<IOutput> { 62 61 new Planner(this).plan(meta); 63 62 const files: Array<IOutput> = []; 64 - const astContext: AstContext = { 65 - getAccess(node) { 66 - return node; 67 - }, 68 - }; 69 63 for (const file of this.files.registered()) { 70 - if (file.finalPath && file.renderer) { 71 - const content = file.renderer.render({ 72 - astContext, 73 - file, 74 - meta, 75 - project: this, 76 - }); 64 + if (!file.external && file.finalPath && file.renderer) { 65 + const content = file.renderer.render({ file, meta, project: this }); 77 66 files.push({ content, path: file.finalPath }); 78 67 } 79 68 }
+12 -4
packages/codegen-core/src/refs/refs.ts
··· 1 - import type { FromRefs, Ref, Refs } from './types'; 1 + import type { FromRef, FromRefs, Ref, Refs } from './types'; 2 2 3 3 /** 4 4 * Wraps a single value in a Ref object. 5 + * 6 + * If the value is already a Ref, returns it as-is (idempotent). 5 7 * 6 8 * @example 7 9 * ```ts 8 10 * const r = ref(123); // { '~ref': 123 } 9 11 * console.log(r['~ref']); // 123 12 + * 13 + * const r2 = ref(r); // { '~ref': 123 } (not double-wrapped) 10 14 * ``` 11 15 */ 12 - export const ref = <T>(value: T): Ref<T> => ({ '~ref': value }); 16 + export const ref = <T>(value: T): Ref<T> => { 17 + if (isRef(value)) { 18 + return value as Ref<T>; 19 + } 20 + return { '~ref': value } as Ref<T>; 21 + }; 13 22 14 23 /** 15 24 * Converts a plain object to an object of Refs (deep, per property). ··· 42 51 */ 43 52 export const fromRef = <T extends Ref<unknown> | undefined>( 44 53 ref: T, 45 - ): T extends Ref<infer U> ? U : undefined => 46 - ref?.['~ref'] as T extends Ref<infer U> ? U : undefined; 54 + ): FromRef<T> => ref?.['~ref'] as FromRef<T>; 47 55 48 56 /** 49 57 * Converts an object of Refs back to a plain object (unwraps all refs).
+3 -3
packages/codegen-core/src/refs/types.d.ts
··· 8 8 * console.log(num['~ref']); // 42 9 9 * ``` 10 10 */ 11 - export type Ref<T> = { '~ref': T }; 11 + export type Ref<T> = T extends { ['~ref']: unknown } ? T : { '~ref': T }; 12 12 13 13 /** 14 14 * Maps every property of `T` to a `Ref` of that property. ··· 33 33 * type N = FromRef<{ '~ref': number }>; // number 34 34 * ``` 35 35 */ 36 - export type FromRef<T> = T extends Ref<infer V> ? V : T; 36 + export type FromRef<T> = T extends { '~ref': infer U } ? U : T; 37 37 38 38 /** 39 39 * Maps every property of a Ref-wrapped object back to its plain value. ··· 46 46 * ``` 47 47 */ 48 48 export type FromRefs<T> = { 49 - [K in keyof T]: T[K] extends Ref<infer V> ? V : T[K]; 49 + [K in keyof T]: T[K] extends Ref<infer U> ? U : T[K]; 50 50 };
+4 -8
packages/codegen-core/src/renderer.d.ts
··· 1 1 import type { IProjectRenderMeta } from './extensions'; 2 2 import type { File } from './files/file'; 3 - import type { AstContext } from './nodes/context'; 3 + import type { INode } from './nodes/node'; 4 4 import type { IProject } from './project/types'; 5 5 6 - export interface RenderContext { 7 - /** 8 - * The context passed to `.toAst()` methods. 9 - */ 10 - astContext: AstContext; 6 + export interface RenderContext<Node extends INode = INode> { 11 7 /** 12 8 * The current file. 13 9 */ 14 - file: File; 10 + file: File<Node>; 15 11 /** 16 12 * Arbitrary metadata. 17 13 */ ··· 26 22 /** Renders the given file. */ 27 23 render(ctx: RenderContext): string; 28 24 /** Returns whether this renderer can render the given file. */ 29 - supports(ctx: Omit<RenderContext, 'astContext'>): boolean; 25 + supports(ctx: RenderContext): boolean; 30 26 }
+90
packages/codegen-core/src/structure/model.ts
··· 1 + import { StructureNode } from './node'; 2 + import type { StructureInsert } from './types'; 3 + 4 + export class StructureModel { 5 + /** Root nodes mapped by their names. */ 6 + private _roots: Map<string, StructureNode> = new Map(); 7 + /** Node for data without a specific root. */ 8 + private _virtualRoot?: StructureNode; 9 + 10 + /** 11 + * Get all root nodes. 12 + */ 13 + get roots(): ReadonlyArray<StructureNode> { 14 + const roots = Array.from(this._roots.values()); 15 + if (this._virtualRoot) roots.unshift(this._virtualRoot); 16 + return roots; 17 + } 18 + 19 + /** 20 + * Insert data into the structure. 21 + */ 22 + insert(args: StructureInsert): void { 23 + const { data, locations, source } = args; 24 + for (const location of locations) { 25 + const { path, shell } = location; 26 + const fullPath = path.filter((s): s is string => Boolean(s)); 27 + const segments = fullPath.slice(0, -1); 28 + const name = fullPath[fullPath.length - 1]; 29 + 30 + if (!name) { 31 + throw new Error('Cannot insert data without path.'); 32 + } 33 + 34 + let cursor: StructureNode | null = null; 35 + 36 + for (const segment of segments) { 37 + if (!cursor) { 38 + cursor = this.root(segment); 39 + } else { 40 + cursor = cursor.child(segment); 41 + } 42 + 43 + if (shell && !cursor.shell) { 44 + cursor.shell = shell; 45 + cursor.shellSource = source; 46 + } 47 + } 48 + 49 + if (!cursor) { 50 + cursor = this.root(null); 51 + } 52 + 53 + cursor.items.push({ data, location: fullPath, source }); 54 + } 55 + } 56 + 57 + /** 58 + * Gets or creates a root by name. 59 + * 60 + * If the root doesn't exist, it's created automatically. 61 + * 62 + * @param name - The name of the root 63 + * @returns The root instance 64 + */ 65 + root(name: string | null): StructureNode { 66 + if (!name) { 67 + return (this._virtualRoot ??= new StructureNode('', undefined, { 68 + virtual: true, 69 + })); 70 + } 71 + if (!this._roots.has(name)) { 72 + this._roots.set(name, new StructureNode(name)); 73 + } 74 + return this._roots.get(name)!; 75 + } 76 + 77 + /** 78 + * Walk all nodes in the structure (depth-first, post-order). 79 + * 80 + * @returns Generator of all structure nodes 81 + */ 82 + *walk(): Generator<StructureNode> { 83 + if (this._virtualRoot) { 84 + yield* this._virtualRoot.walk(); 85 + } 86 + for (const root of this._roots.values()) { 87 + yield* root.walk(); 88 + } 89 + } 90 + }
+93
packages/codegen-core/src/structure/node.ts
··· 1 + import type { StructureItem, StructureShell } from './types'; 2 + 3 + export class StructureNode { 4 + /** Nested nodes within this node. */ 5 + children: Map<string, StructureNode> = new Map(); 6 + /** Items contained in this node. */ 7 + items: Array<StructureItem> = []; 8 + /** The name of this node (e.g., "Users", "Accounts"). */ 9 + name: string; 10 + /** Parent node in the hierarchy. Undefined if this is the root node. */ 11 + parent?: StructureNode; 12 + /** Shell claimed for this node. */ 13 + shell?: StructureShell; 14 + /** Source of the claimed shell. */ 15 + shellSource?: symbol; 16 + /** True if this is a virtual root. */ 17 + virtual: boolean; 18 + 19 + constructor( 20 + name: string, 21 + parent?: StructureNode, 22 + options?: { 23 + virtual?: boolean; 24 + }, 25 + ) { 26 + this.name = name; 27 + this.parent = parent; 28 + this.virtual = options?.virtual ?? false; 29 + } 30 + 31 + get isRoot(): boolean { 32 + return !this.parent; 33 + } 34 + 35 + /** 36 + * Gets or creates a child node. 37 + * 38 + * If the child doesn't exist, it's created automatically. 39 + * 40 + * @param name - The name of the child node 41 + * @returns The child node instance 42 + */ 43 + child(name: string): StructureNode { 44 + if (!this.children.has(name)) { 45 + this.children.set(name, new StructureNode(name, this)); 46 + } 47 + return this.children.get(name)!; 48 + } 49 + 50 + /** 51 + * Gets the full path of this node in the hierarchy. 52 + * 53 + * @returns An array of node names from the root to this node 54 + */ 55 + getPath(): ReadonlyArray<string> { 56 + const path: Array<string> = []; 57 + // eslint-disable-next-line @typescript-eslint/no-this-alias 58 + let cursor: StructureNode | undefined = this; 59 + while (cursor) { 60 + path.unshift(cursor.name); 61 + cursor = cursor.parent; 62 + } 63 + return path; 64 + } 65 + 66 + /** 67 + * Yields items from a specific source with typed data. 68 + * 69 + * @param source - The source symbol to filter by 70 + * @returns Generator of items from that source 71 + */ 72 + *itemsFrom<T = unknown>( 73 + source: symbol, 74 + ): Generator<StructureItem & { data: T }> { 75 + for (const item of this.items) { 76 + if (item.source === source) { 77 + yield item as StructureItem & { data: T }; 78 + } 79 + } 80 + } 81 + 82 + /** 83 + * Walk all nodes in the structure (depth-first, post-order). 84 + * 85 + * @returns Generator of all structure nodes 86 + */ 87 + *walk(): Generator<StructureNode> { 88 + for (const node of this.children.values()) { 89 + yield* node.walk(); 90 + } 91 + yield this; 92 + } 93 + }
+33
packages/codegen-core/src/structure/types.d.ts
··· 1 + import type { INode } from '../nodes/node'; 2 + import type { StructureNode } from './node'; 3 + 4 + export interface StructureInsert { 5 + /** Inserted data. */ 6 + data: unknown; 7 + /** Locations where the data should be inserted. */ 8 + locations: ReadonlyArray<StructureLocation>; 9 + /** Source of the inserted data. */ 10 + source: symbol; 11 + } 12 + 13 + export interface StructureItem 14 + extends Pick<StructureInsert, 'data' | 'source'> { 15 + /** Location of this item within the structure. */ 16 + location: ReadonlyArray<string>; 17 + } 18 + 19 + export interface StructureLocation { 20 + /** Path within the structure where the data should be inserted. */ 21 + path: ReadonlyArray<string>; 22 + /** Shell to apply at this location. */ 23 + shell?: StructureShell; 24 + } 25 + 26 + export interface StructureShell { 27 + define: (node: StructureNode) => StructureShellResult; 28 + } 29 + 30 + export interface StructureShellResult { 31 + dependencies?: Array<INode>; 32 + node: INode; 33 + }
+20 -42
packages/codegen-core/src/symbols/symbol.ts
··· 1 1 import { symbolBrand } from '../brands'; 2 - import { debug } from '../debug'; 3 2 import type { ISymbolMeta } from '../extensions'; 4 3 import type { File } from '../files/file'; 4 + import { log } from '../log'; 5 5 import type { INode } from '../nodes/node'; 6 - import type { 7 - BindingKind, 8 - ISymbolIn, 9 - SymbolKind, 10 - SymbolNameSanitizer, 11 - } from './types'; 6 + import type { BindingKind, ISymbolIn, SymbolKind } from './types'; 12 7 13 - export class Symbol { 8 + export class Symbol<Node extends INode = INode> { 14 9 /** 15 10 * Canonical symbol this stub resolves to, if any. 16 11 * ··· 79 74 */ 80 75 private _name: string; 81 76 /** 82 - * Optional function to sanitize the symbol name. 83 - * 84 - * @default undefined 85 - */ 86 - private _nameSanitizer?: SymbolNameSanitizer; 87 - /** 88 77 * Node that defines this symbol. 89 78 */ 90 - private _node?: INode; 79 + private _node?: Node; 91 80 92 81 /** Brand used for identifying symbols. */ 93 82 readonly '~brand' = symbolBrand; ··· 153 142 get finalName(): string { 154 143 if (!this.canonical._finalName) { 155 144 const message = `Symbol finalName has not been resolved yet for ${this.canonical.toString()}`; 156 - debug(message, 'symbol'); 145 + log.debug(message, 'symbol'); 157 146 throw new Error(message); 158 147 } 159 148 return this.canonical._finalName; ··· 202 191 } 203 192 204 193 /** 205 - * Optional function to sanitize the symbol name. 206 - */ 207 - get nameSanitizer(): SymbolNameSanitizer | undefined { 208 - return this.canonical._nameSanitizer; 209 - } 210 - 211 - /** 212 194 * Read‑only accessor for the defining node. 213 195 */ 214 - get node(): INode | undefined { 215 - return this.canonical._node; 196 + get node(): Node | undefined { 197 + return this.canonical._node as Node | undefined; 216 198 } 217 199 218 200 /** ··· 256 238 this.assertCanonical(); 257 239 if (this._file && this._file !== file) { 258 240 const message = `Symbol ${this.canonical.toString()} is already assigned to a different file.`; 259 - debug(message, 'symbol'); 241 + log.debug(message, 'symbol'); 260 242 throw new Error(message); 261 243 } 262 244 this._file = file; ··· 271 253 this.assertCanonical(); 272 254 if (this._finalName && this._finalName !== name) { 273 255 const message = `Symbol finalName has already been resolved for ${this.canonical.toString()}.`; 274 - debug(message, 'symbol'); 256 + log.debug(message, 'symbol'); 275 257 throw new Error(message); 276 258 } 277 259 this._finalName = name; ··· 308 290 } 309 291 310 292 /** 311 - * Sets a custom function to sanitize the symbol's name. 312 - * 313 - * @param fn — The name sanitizer function to apply. 314 - */ 315 - setNameSanitizer(fn: SymbolNameSanitizer): void { 316 - this.assertCanonical(); 317 - this._nameSanitizer = fn; 318 - } 319 - 320 - /** 321 293 * Binds the node that defines this symbol. 322 294 * 323 295 * This may only be set once. 324 296 */ 325 - setNode(node: INode): void { 297 + setNode(node: Node): void { 326 298 this.assertCanonical(); 327 299 if (this._node && this._node !== node) { 328 300 const message = `Symbol ${this.canonical.toString()} is already bound to a different node.`; 329 - debug(message, 'symbol'); 330 - throw new Error(message); 301 + log.debug(message, 'symbol'); 302 + // TODO: symbol <> node relationship needs to be refactor to 1:many 303 + // disabled in the meantime or it would throw 304 + // throw new Error(message); 331 305 } 332 306 this._node = node; 333 307 node.symbol = this; ··· 337 311 * Returns a debug‑friendly string representation identifying the symbol. 338 312 */ 339 313 toString(): string { 340 - return `[Symbol ${this.name}#${this.id}]`; 314 + const canonical = this.canonical; 315 + if (canonical._finalName && canonical._finalName !== canonical._name) { 316 + return `[Symbol ${canonical._name} → ${canonical._finalName}#${canonical.id}]`; 317 + } 318 + return `[Symbol ${canonical._name}#${canonical.id}]`; 341 319 } 342 320 343 321 /** ··· 353 331 private assertCanonical(): void { 354 332 if (this._canonical && this._canonical !== this) { 355 333 const message = `Illegal mutation of stub symbol ${this.toString()} → canonical: ${this._canonical.toString()}`; 356 - debug(message, 'symbol'); 334 + log.debug(message, 'symbol'); 357 335 throw new Error(message); 358 336 } 359 337 }
-2
packages/codegen-core/src/symbols/types.d.ts
··· 14 14 | 'type' 15 15 | 'var'; 16 16 17 - export type SymbolNameSanitizer = (name: string) => string; 18 - 19 17 export type ISymbolIn = { 20 18 /** 21 19 * Array of file names (without extensions) from which this symbol is re-exported.
+2 -2
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/@angular/common.gen.ts
··· 587 587 public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: () => Options<CallWithResponseAndNoContentResponseData, ThrowOnError> | undefined) { 588 588 return httpResource<CallWithResponseAndNoContentResponseResponse>(() => { 589 589 const opts = options ? options() : undefined; 590 - return opts ? inject(ResponseServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 590 + return opts ? inject(NoContentServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 591 591 }); 592 592 } 593 593 } ··· 597 597 public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: () => Options<CallWithResponseAndNoContentResponseData, ThrowOnError> | undefined) { 598 598 return httpResource<CallWithResponseAndNoContentResponseResponse>(() => { 599 599 const opts = options ? options() : undefined; 600 - return opts ? inject(ResponseServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 600 + return opts ? inject(NoContentServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 601 601 }); 602 602 } 603 603
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { Business, Domains, Locations, type Options, Providers } from './sdk.gen'; 3 + export { Business, Business2, Business3, Business4, Business5, Domains, Domains2, Domains3, Domains4, Locations, type Options, Providers, Providers2, Providers3, Providers4 } from './sdk.gen'; 4 4 export type { BusinessGetData, BusinessGetResponse, BusinessGetResponses, BusinessProvidersDomainsGetData, BusinessProvidersDomainsGetResponse, BusinessProvidersDomainsGetResponses, BusinessProvidersDomainsPostData, BusinessProvidersDomainsPostResponse, BusinessProvidersDomainsPostResponses, ClientOptions, GetData, GetResponse, GetResponses, PutBusinessProvidersDomainsData, PutBusinessProvidersDomainsResponse, PutBusinessProvidersDomainsResponses } from './types.gen';
+59 -5
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/sdk.gen.ts
··· 26 26 public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 27 27 return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 28 28 } 29 - 30 - public static putBusinessProvidersDomains<ThrowOnError extends boolean = false>(options?: Options<PutBusinessProvidersDomainsData, ThrowOnError>) { 31 - return (options?.client ?? client).put<PutBusinessProvidersDomainsResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 32 - } 33 29 } 34 30 35 31 export class Providers { ··· 44 40 static providers = Providers; 45 41 } 46 42 43 + export class Business2 { 44 + static business = Business; 45 + } 46 + 47 + export class Domains2 { 48 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsGetData, ThrowOnError>) { 49 + return (options?.client ?? client).get<BusinessProvidersDomainsGetResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 50 + } 51 + 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 54 + } 55 + } 56 + 57 + export class Providers2 { 58 + static domains = Domains2; 59 + } 60 + 61 + export class Business3 { 62 + static providers = Providers2; 63 + } 64 + 65 + export class Providers3 { 66 + static business = Business3; 67 + } 68 + 69 + export class Domains3 { 70 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsGetData, ThrowOnError>) { 71 + return (options?.client ?? client).get<BusinessProvidersDomainsGetResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 72 + } 73 + 74 + public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 75 + return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 76 + } 77 + } 78 + 79 + export class Providers4 { 80 + static domains = Domains3; 81 + } 82 + 83 + export class Business4 { 84 + static providers = Providers4; 85 + } 86 + 87 + export class Domains4 { 88 + public static putBusinessProvidersDomains<ThrowOnError extends boolean = false>(options?: Options<PutBusinessProvidersDomainsData, ThrowOnError>) { 89 + return (options?.client ?? client).put<PutBusinessProvidersDomainsResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 90 + } 91 + 92 + static business = Business4; 93 + } 94 + 95 + export class Business5 { 96 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessGetData, ThrowOnError>) { 97 + return (options?.client ?? client).get<BusinessGetResponses, unknown, ThrowOnError>({ url: '/locations/businesses', ...options }); 98 + } 99 + } 100 + 47 101 export class Locations { 48 102 public static get<ThrowOnError extends boolean = false>(options?: Options<GetData, ThrowOnError>) { 49 103 return (options?.client ?? client).get<GetResponses, unknown, ThrowOnError>({ url: '/locations', ...options }); 50 104 } 51 105 52 - static business = Business; 106 + static business = Business5; 53 107 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts
··· 4 4 5 5 import { serializeQueryKeyValue } from '../client'; 6 6 import { client } from '../client.gen'; 7 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 7 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 8 8 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooData } from '../types.gen'; 9 9 10 10 export type QueryKey<TOptions extends Options> = [ ··· 58 58 59 59 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, Options<FooPostData>, Error> => ({ 60 60 mutation: async (vars) => { 61 - const { data } = await FooBazService.fooService.post({ 61 + const { data } = await FooService.post({ 62 62 ...options, 63 63 ...vars, 64 64 throwOnError: true ··· 69 69 70 70 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, Options<FooPutData>, Error> => ({ 71 71 mutation: async (vars) => { 72 - const { data } = await FooBazService.fooService.put({ 72 + const { data } = await FooService.put({ 73 73 ...options, 74 74 ...vars, 75 75 throwOnError: true ··· 94 94 95 95 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, Options<FooBarPostData>, Error> => ({ 96 96 mutation: async (vars) => { 97 - const { data } = await FooBazService.fooService.barService.post({ 97 + const { data } = await BarService.post({ 98 98 ...options, 99 99 ...vars, 100 100 throwOnError: true ··· 105 105 106 106 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, Options<FooBarPutData>, Error> => ({ 107 107 mutation: async (vars) => { 108 - const { data } = await FooBazService.fooService.barService.put({ 108 + const { data } = await BarService.put({ 109 109 ...options, 110 110 ...vars, 111 111 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/@tanstack/angular-query-experimental.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/angular-query-experimental'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/@tanstack/react-query.gen.ts
··· 3 3 import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/react-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/@tanstack/solid-query.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/solid-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/@tanstack/svelte-query.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/svelte-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/@tanstack/vue-query.gen.ts
··· 3 3 import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/vue-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+37 -17
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/@angular/common.gen.ts
··· 55 55 } 56 56 } 57 57 58 - export class ODataControllerServiceRequests { 59 - public apiVVersionODataControllerCount<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionODataControllerCountData, ThrowOnError>): HttpRequest<unknown> { 58 + export class ODataControllerService { 59 + public count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionODataControllerCountData, ThrowOnError>): HttpRequest<unknown> { 60 60 return (options?.client ?? client).requestOptions({ 61 61 responseStyle: 'data', 62 62 method: 'GET', ··· 66 66 } 67 67 } 68 68 69 - export class VVersionServiceRequests { 70 - oDataControllerService = new ODataControllerServiceRequests(); 69 + export class VVersionService { 70 + private _oDataControllerService?: ODataControllerService; 71 + get oDataControllerService(): ODataControllerService { 72 + return this._oDataControllerService ??= new ODataControllerService(); 73 + } 71 74 } 72 75 73 - export class ApiServiceRequests { 74 - vVersionService = new VVersionServiceRequests(); 76 + export class ApiService { 77 + private _vVersionService?: VVersionService; 78 + get vVersionService(): VVersionService { 79 + return this._vVersionService ??= new VVersionService(); 80 + } 75 81 } 76 82 77 83 @Injectable({ providedIn: 'root' }) ··· 138 144 ...options 139 145 }); 140 146 } 141 - apiService = new ApiServiceRequests(); 147 + 148 + private _apiService?: ApiService; 149 + get apiService(): ApiService { 150 + return this._apiService ??= new ApiService(); 151 + } 142 152 } 143 153 144 154 @Injectable({ providedIn: 'root' }) ··· 599 609 } 600 610 } 601 611 602 - export class ODataControllerServiceResources { 603 - public apiVVersionODataControllerCount<ThrowOnError extends boolean = false>(options?: () => Options<ApiVVersionODataControllerCountData, ThrowOnError> | undefined) { 612 + export class ODataControllerService2 { 613 + public count<ThrowOnError extends boolean = false>(options?: () => Options<ApiVVersionODataControllerCountData, ThrowOnError> | undefined) { 604 614 return httpResource<ApiVVersionODataControllerCountResponse>(() => { 605 615 const opts = options ? options() : undefined; 606 - return opts ? inject(SimpleServiceRequests).apiService.vVersionService.oDataControllerService.apiVVersionODataControllerCount(opts) : undefined; 616 + return opts ? inject(SimpleServiceRequests).apiService.vVersionService.oDataControllerService.count(opts) : undefined; 607 617 }); 608 618 } 609 619 } 610 620 611 - export class VVersionServiceResources { 612 - oDataControllerService = new ODataControllerServiceResources(); 621 + export class VVersionService2 { 622 + private _oDataControllerService?: ODataControllerService2; 623 + get oDataControllerService(): ODataControllerService2 { 624 + return this._oDataControllerService ??= new ODataControllerService2(); 625 + } 613 626 } 614 627 615 - export class ApiServiceResources { 616 - vVersionService = new VVersionServiceResources(); 628 + export class ApiService2 { 629 + private _vVersionService?: VVersionService2; 630 + get vVersionService(): VVersionService2 { 631 + return this._vVersionService ??= new VVersionService2(); 632 + } 617 633 } 618 634 619 635 @Injectable({ providedIn: 'root' }) ··· 666 682 return opts ? inject(SimpleServiceRequests).putCallWithoutParametersAndResponse(opts) : undefined; 667 683 }); 668 684 } 669 - apiService = new ApiServiceResources(); 685 + 686 + private _apiService?: ApiService2; 687 + get apiService(): ApiService2 { 688 + return this._apiService ??= new ApiService2(); 689 + } 670 690 } 671 691 672 692 @Injectable({ providedIn: 'root' }) ··· 817 837 public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: () => Options<CallWithResponseAndNoContentResponseData, ThrowOnError> | undefined) { 818 838 return httpResource<CallWithResponseAndNoContentResponseResponse>(() => { 819 839 const opts = options ? options() : undefined; 820 - return opts ? inject(ResponseServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 840 + return opts ? inject(NoContentServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 821 841 }); 822 842 } 823 843 } ··· 827 847 public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: () => Options<CallWithResponseAndNoContentResponseData, ThrowOnError> | undefined) { 828 848 return httpResource<CallWithResponseAndNoContentResponseResponse>(() => { 829 849 const opts = options ? options() : undefined; 830 - return opts ? inject(ResponseServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 850 + return opts ? inject(NoContentServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 831 851 }); 832 852 } 833 853
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { Business, Domains, Locations, type Options, Providers } from './sdk.gen'; 3 + export { Business, Business2, Business3, Business4, Business5, Domains, Domains2, Domains3, Domains4, Locations, type Options, Providers, Providers2, Providers3, Providers4 } from './sdk.gen'; 4 4 export type { BusinessGetData, BusinessGetResponse, BusinessGetResponses, BusinessProvidersDomainsGetData, BusinessProvidersDomainsGetResponse, BusinessProvidersDomainsGetResponses, BusinessProvidersDomainsPostData, BusinessProvidersDomainsPostResponse, BusinessProvidersDomainsPostResponses, ClientOptions, GetData, GetResponse, GetResponses, PutBusinessProvidersDomainsData, PutBusinessProvidersDomainsResponse, PutBusinessProvidersDomainsResponses } from './types.gen';
+59 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/sdk.gen.ts
··· 26 26 public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 27 27 return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 28 28 } 29 - 30 - public static putBusinessProvidersDomains<ThrowOnError extends boolean = false>(options?: Options<PutBusinessProvidersDomainsData, ThrowOnError>) { 31 - return (options?.client ?? client).put<PutBusinessProvidersDomainsResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 32 - } 33 29 } 34 30 35 31 export class Providers { ··· 44 40 static providers = Providers; 45 41 } 46 42 43 + export class Business2 { 44 + static business = Business; 45 + } 46 + 47 + export class Domains2 { 48 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsGetData, ThrowOnError>) { 49 + return (options?.client ?? client).get<BusinessProvidersDomainsGetResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 50 + } 51 + 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 54 + } 55 + } 56 + 57 + export class Providers2 { 58 + static domains = Domains2; 59 + } 60 + 61 + export class Business3 { 62 + static providers = Providers2; 63 + } 64 + 65 + export class Providers3 { 66 + static business = Business3; 67 + } 68 + 69 + export class Domains3 { 70 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsGetData, ThrowOnError>) { 71 + return (options?.client ?? client).get<BusinessProvidersDomainsGetResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 72 + } 73 + 74 + public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 75 + return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 76 + } 77 + } 78 + 79 + export class Providers4 { 80 + static domains = Domains3; 81 + } 82 + 83 + export class Business4 { 84 + static providers = Providers4; 85 + } 86 + 87 + export class Domains4 { 88 + public static putBusinessProvidersDomains<ThrowOnError extends boolean = false>(options?: Options<PutBusinessProvidersDomainsData, ThrowOnError>) { 89 + return (options?.client ?? client).put<PutBusinessProvidersDomainsResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 90 + } 91 + 92 + static business = Business4; 93 + } 94 + 95 + export class Business5 { 96 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessGetData, ThrowOnError>) { 97 + return (options?.client ?? client).get<BusinessGetResponses, unknown, ThrowOnError>({ url: '/locations/businesses', ...options }); 98 + } 99 + } 100 + 47 101 export class Locations { 48 102 public static get<ThrowOnError extends boolean = false>(options?: Options<GetData, ThrowOnError>) { 49 103 return (options?.client ?? client).get<GetResponses, unknown, ThrowOnError>({ url: '/locations', ...options }); 50 104 } 51 105 52 - static business = Business; 106 + static business = Business5; 53 107 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts
··· 4 4 5 5 import { serializeQueryKeyValue } from '../client'; 6 6 import { client } from '../client.gen'; 7 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 7 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 8 8 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooData } from '../types.gen'; 9 9 10 10 export type QueryKey<TOptions extends Options> = [ ··· 58 58 59 59 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, Options<FooPostData>, Error> => ({ 60 60 mutation: async (vars) => { 61 - const { data } = await FooBazService.fooService.post({ 61 + const { data } = await FooService.post({ 62 62 ...options, 63 63 ...vars, 64 64 throwOnError: true ··· 69 69 70 70 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, Options<FooPutData>, Error> => ({ 71 71 mutation: async (vars) => { 72 - const { data } = await FooBazService.fooService.put({ 72 + const { data } = await FooService.put({ 73 73 ...options, 74 74 ...vars, 75 75 throwOnError: true ··· 94 94 95 95 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, Options<FooBarPostData>, Error> => ({ 96 96 mutation: async (vars) => { 97 - const { data } = await FooBazService.fooService.barService.post({ 97 + const { data } = await BarService.post({ 98 98 ...options, 99 99 ...vars, 100 100 throwOnError: true ··· 105 105 106 106 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, Options<FooBarPutData>, Error> => ({ 107 107 mutation: async (vars) => { 108 - const { data } = await FooBazService.fooService.barService.put({ 108 + const { data } = await BarService.put({ 109 109 ...options, 110 110 ...vars, 111 111 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/@tanstack/angular-query-experimental.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/angular-query-experimental'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/@tanstack/react-query.gen.ts
··· 3 3 import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/react-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/@tanstack/solid-query.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/solid-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/@tanstack/svelte-query.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/svelte-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/@tanstack/vue-query.gen.ts
··· 3 3 import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/vue-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+76 -34
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/@angular/common.gen.ts
··· 55 55 } 56 56 } 57 57 58 - export class ODataControllerServiceRequests { 59 - public apiVVersionODataControllerCount<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionODataControllerCountData, ThrowOnError>): HttpRequest<unknown> { 58 + export class ODataControllerService { 59 + public count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionODataControllerCountData, ThrowOnError>): HttpRequest<unknown> { 60 60 return (options?.client ?? client).requestOptions({ 61 61 responseStyle: 'data', 62 62 method: 'GET', ··· 66 66 } 67 67 } 68 68 69 - export class VVersionServiceRequests { 70 - oDataControllerService = new ODataControllerServiceRequests(); 69 + export class VVersionService { 70 + private _oDataControllerService?: ODataControllerService; 71 + get oDataControllerService(): ODataControllerService { 72 + return this._oDataControllerService ??= new ODataControllerService(); 73 + } 71 74 } 72 75 73 - export class ApiServiceRequests { 74 - vVersionService = new VVersionServiceRequests(); 76 + export class ApiService { 77 + private _vVersionService?: VVersionService; 78 + get vVersionService(): VVersionService { 79 + return this._vVersionService ??= new VVersionService(); 80 + } 75 81 } 76 82 77 83 @Injectable({ providedIn: 'root' }) ··· 138 144 ...options 139 145 }); 140 146 } 141 - apiService = new ApiServiceRequests(); 147 + 148 + private _apiService?: ApiService; 149 + get apiService(): ApiService { 150 + return this._apiService ??= new ApiService(); 151 + } 142 152 } 143 153 144 154 @Injectable({ providedIn: 'root' }) ··· 281 291 }); 282 292 } 283 293 284 - public duplicateName2<ThrowOnError extends boolean = false>(options?: Options<DuplicateName2Data, ThrowOnError>): HttpRequest<unknown> { 294 + public duplicateName2<ThrowOnError extends boolean = false>(options?: Options<DuplicateNameData, ThrowOnError>): HttpRequest<unknown> { 295 + return (options?.client ?? client).requestOptions({ 296 + responseStyle: 'data', 297 + method: 'DELETE', 298 + url: '/api/v{api-version}/duplicate', 299 + ...options 300 + }); 301 + } 302 + 303 + public duplicateName22<ThrowOnError extends boolean = false>(options?: Options<DuplicateName2Data, ThrowOnError>): HttpRequest<unknown> { 285 304 return (options?.client ?? client).requestOptions({ 286 305 responseStyle: 'data', 287 306 method: 'GET', ··· 319 338 ...options 320 339 }); 321 340 } 322 - 323 - public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: Options<CallWithResponseAndNoContentResponseData, ThrowOnError>): HttpRequest<unknown> { 324 - return (options?.client ?? client).requestOptions({ 325 - responseStyle: 'data', 326 - method: 'GET', 327 - url: '/api/v{api-version}/multiple-tags/response-and-no-content', 328 - ...options 329 - }); 330 - } 331 341 } 332 342 333 343 @Injectable({ providedIn: 'root' }) ··· 370 380 } 371 381 372 382 @Injectable({ providedIn: 'root' }) 383 + export class NoContentServiceRequests2 { 384 + public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: Options<CallWithResponseAndNoContentResponseData, ThrowOnError>): HttpRequest<unknown> { 385 + return (options?.client ?? client).requestOptions({ 386 + responseStyle: 'data', 387 + method: 'GET', 388 + url: '/api/v{api-version}/multiple-tags/response-and-no-content', 389 + ...options 390 + }); 391 + } 392 + } 393 + 394 + @Injectable({ providedIn: 'root' }) 373 395 export class MultipleTags1ServiceRequests { 374 396 public dummyA<ThrowOnError extends boolean = false>(options?: Options<DummyAData, ThrowOnError>): HttpRequest<unknown> { 375 397 return (options?.client ?? client).requestOptions({ ··· 599 621 } 600 622 } 601 623 602 - export class ODataControllerServiceResources { 603 - public apiVVersionODataControllerCount<ThrowOnError extends boolean = false>(options?: () => Options<ApiVVersionODataControllerCountData, ThrowOnError> | undefined) { 624 + export class ODataControllerService2 { 625 + public count<ThrowOnError extends boolean = false>(options?: () => Options<ApiVVersionODataControllerCountData, ThrowOnError> | undefined) { 604 626 return httpResource<ApiVVersionODataControllerCountResponse>(() => { 605 627 const opts = options ? options() : undefined; 606 - return opts ? inject(SimpleServiceRequests).apiService.vVersionService.oDataControllerService.apiVVersionODataControllerCount(opts) : undefined; 628 + return opts ? inject(SimpleServiceRequests).apiService.vVersionService.oDataControllerService.count(opts) : undefined; 607 629 }); 608 630 } 609 631 } 610 632 611 - export class VVersionServiceResources { 612 - oDataControllerService = new ODataControllerServiceResources(); 633 + export class VVersionService2 { 634 + private _oDataControllerService?: ODataControllerService2; 635 + get oDataControllerService(): ODataControllerService2 { 636 + return this._oDataControllerService ??= new ODataControllerService2(); 637 + } 613 638 } 614 639 615 - export class ApiServiceResources { 616 - vVersionService = new VVersionServiceResources(); 640 + export class ApiService2 { 641 + private _vVersionService?: VVersionService2; 642 + get vVersionService(): VVersionService2 { 643 + return this._vVersionService ??= new VVersionService2(); 644 + } 617 645 } 618 646 619 647 @Injectable({ providedIn: 'root' }) ··· 666 694 return opts ? inject(SimpleServiceRequests).putCallWithoutParametersAndResponse(opts) : undefined; 667 695 }); 668 696 } 669 - apiService = new ApiServiceResources(); 697 + 698 + private _apiService?: ApiService2; 699 + get apiService(): ApiService2 { 700 + return this._apiService ??= new ApiService2(); 701 + } 670 702 } 671 703 672 704 @Injectable({ providedIn: 'root' }) ··· 783 815 }); 784 816 } 785 817 786 - public duplicateName2<ThrowOnError extends boolean = false>(options?: () => Options<DuplicateName2Data, ThrowOnError> | undefined) { 818 + public duplicateName2<ThrowOnError extends boolean = false>(options?: () => Options<DuplicateNameData, ThrowOnError> | undefined) { 787 819 return httpResource<unknown>(() => { 788 820 const opts = options ? options() : undefined; 789 - return opts ? inject(DuplicateServiceRequests).duplicateName2(opts) : undefined; 821 + return opts ? inject(DuplicateServiceRequests).duplicateName(opts) : undefined; 822 + }); 823 + } 824 + 825 + public duplicateName22<ThrowOnError extends boolean = false>(options?: () => Options<DuplicateName2Data, ThrowOnError> | undefined) { 826 + return httpResource<unknown>(() => { 827 + const opts = options ? options() : undefined; 828 + return opts ? inject(DuplicateServiceRequests).duplicateName22(opts) : undefined; 790 829 }); 791 830 } 792 831 ··· 813 852 return opts ? inject(NoContentServiceRequests).callWithNoContentResponse(opts) : undefined; 814 853 }); 815 854 } 816 - 817 - public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: () => Options<CallWithResponseAndNoContentResponseData, ThrowOnError> | undefined) { 818 - return httpResource<CallWithResponseAndNoContentResponseResponse>(() => { 819 - const opts = options ? options() : undefined; 820 - return opts ? inject(ResponseServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 821 - }); 822 - } 823 855 } 824 856 825 857 @Injectable({ providedIn: 'root' }) ··· 849 881 return httpResource<CallWithResponsesResponse>(() => { 850 882 const opts = options ? options() : undefined; 851 883 return opts ? inject(ResponseServiceRequests).callWithResponses(opts) : undefined; 884 + }); 885 + } 886 + } 887 + 888 + @Injectable({ providedIn: 'root' }) 889 + export class NoContentServiceResources2 { 890 + public callWithResponseAndNoContentResponse<ThrowOnError extends boolean = false>(options?: () => Options<CallWithResponseAndNoContentResponseData, ThrowOnError> | undefined) { 891 + return httpResource<CallWithResponseAndNoContentResponseResponse>(() => { 892 + const opts = options ? options() : undefined; 893 + return opts ? inject(ResponseServiceRequests).callWithResponseAndNoContentResponse(opts) : undefined; 852 894 }); 853 895 } 854 896 }
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { Business, Domains, Locations, type Options, Providers } from './sdk.gen'; 3 + export { Business, Business2, Business3, Business4, Business5, Domains, Domains2, Domains3, Domains4, Locations, type Options, Providers, Providers2, Providers3, Providers4 } from './sdk.gen'; 4 4 export type { BusinessGetData, BusinessGetResponse, BusinessGetResponses, BusinessProvidersDomainsGetData, BusinessProvidersDomainsGetResponse, BusinessProvidersDomainsGetResponses, BusinessProvidersDomainsPostData, BusinessProvidersDomainsPostResponse, BusinessProvidersDomainsPostResponses, ClientOptions, GetData, GetResponse, GetResponses, PutBusinessProvidersDomainsData, PutBusinessProvidersDomainsResponse, PutBusinessProvidersDomainsResponses } from './types.gen';
+59 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/sdk.gen.ts
··· 26 26 public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 27 27 return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 28 28 } 29 - 30 - public static putBusinessProvidersDomains<ThrowOnError extends boolean = false>(options?: Options<PutBusinessProvidersDomainsData, ThrowOnError>) { 31 - return (options?.client ?? client).put<PutBusinessProvidersDomainsResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 32 - } 33 29 } 34 30 35 31 export class Providers { ··· 44 40 static providers = Providers; 45 41 } 46 42 43 + export class Business2 { 44 + static business = Business; 45 + } 46 + 47 + export class Domains2 { 48 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsGetData, ThrowOnError>) { 49 + return (options?.client ?? client).get<BusinessProvidersDomainsGetResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 50 + } 51 + 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 54 + } 55 + } 56 + 57 + export class Providers2 { 58 + static domains = Domains2; 59 + } 60 + 61 + export class Business3 { 62 + static providers = Providers2; 63 + } 64 + 65 + export class Providers3 { 66 + static business = Business3; 67 + } 68 + 69 + export class Domains3 { 70 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsGetData, ThrowOnError>) { 71 + return (options?.client ?? client).get<BusinessProvidersDomainsGetResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 72 + } 73 + 74 + public static post<ThrowOnError extends boolean = false>(options?: Options<BusinessProvidersDomainsPostData, ThrowOnError>) { 75 + return (options?.client ?? client).post<BusinessProvidersDomainsPostResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 76 + } 77 + } 78 + 79 + export class Providers4 { 80 + static domains = Domains3; 81 + } 82 + 83 + export class Business4 { 84 + static providers = Providers4; 85 + } 86 + 87 + export class Domains4 { 88 + public static putBusinessProvidersDomains<ThrowOnError extends boolean = false>(options?: Options<PutBusinessProvidersDomainsData, ThrowOnError>) { 89 + return (options?.client ?? client).put<PutBusinessProvidersDomainsResponses, unknown, ThrowOnError>({ url: '/business/providers/domains', ...options }); 90 + } 91 + 92 + static business = Business4; 93 + } 94 + 95 + export class Business5 { 96 + public static get<ThrowOnError extends boolean = false>(options?: Options<BusinessGetData, ThrowOnError>) { 97 + return (options?.client ?? client).get<BusinessGetResponses, unknown, ThrowOnError>({ url: '/locations/businesses', ...options }); 98 + } 99 + } 100 + 47 101 export class Locations { 48 102 public static get<ThrowOnError extends boolean = false>(options?: Options<GetData, ThrowOnError>) { 49 103 return (options?.client ?? client).get<GetResponses, unknown, ThrowOnError>({ url: '/locations', ...options }); 50 104 } 51 105 52 - static business = Business; 106 + static business = Business5; 53 107 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts
··· 4 4 5 5 import { serializeQueryKeyValue } from '../client'; 6 6 import { client } from '../client.gen'; 7 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 7 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 8 8 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooData } from '../types.gen'; 9 9 10 10 export type QueryKey<TOptions extends Options> = [ ··· 58 58 59 59 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, Options<FooPostData>, Error> => ({ 60 60 mutation: async (vars) => { 61 - const { data } = await FooBazService.fooService.post({ 61 + const { data } = await FooService.post({ 62 62 ...options, 63 63 ...vars, 64 64 throwOnError: true ··· 69 69 70 70 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, Options<FooPutData>, Error> => ({ 71 71 mutation: async (vars) => { 72 - const { data } = await FooBazService.fooService.put({ 72 + const { data } = await FooService.put({ 73 73 ...options, 74 74 ...vars, 75 75 throwOnError: true ··· 94 94 95 95 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, Options<FooBarPostData>, Error> => ({ 96 96 mutation: async (vars) => { 97 - const { data } = await FooBazService.fooService.barService.post({ 97 + const { data } = await BarService.post({ 98 98 ...options, 99 99 ...vars, 100 100 throwOnError: true ··· 105 105 106 106 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, Options<FooBarPutData>, Error> => ({ 107 107 mutation: async (vars) => { 108 - const { data } = await FooBazService.fooService.barService.put({ 108 + const { data } = await BarService.put({ 109 109 ...options, 110 110 ...vars, 111 111 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/@tanstack/angular-query-experimental.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/angular-query-experimental'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/@tanstack/react-query.gen.ts
··· 3 3 import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/react-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/@tanstack/solid-query.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/solid-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/@tanstack/svelte-query.gen.ts
··· 3 3 import { type DefaultError, type MutationOptions, queryOptions } from '@tanstack/svelte-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: MutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: MutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: MutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: MutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+5 -5
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/@tanstack/vue-query.gen.ts
··· 3 3 import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/vue-query'; 4 4 5 5 import { client } from '../client.gen'; 6 - import { BarBazService, FooBazService, type Options } from '../sdk.gen'; 6 + import { BarBazService, BarService, FooBazService, FooService, type Options } from '../sdk.gen'; 7 7 import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; 8 8 9 9 export type QueryKey<TOptions extends Options> = [ ··· 57 57 export const fooPostMutation = (options?: Partial<Options<FooPostData>>): UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> => { 58 58 const mutationOptions: UseMutationOptions<FooPostResponse, DefaultError, Options<FooPostData>> = { 59 59 mutationFn: async (fnOptions) => { 60 - const { data } = await FooBazService.fooService.post({ 60 + const { data } = await FooService.post({ 61 61 ...options, 62 62 ...fnOptions, 63 63 throwOnError: true ··· 71 71 export const fooPutMutation = (options?: Partial<Options<FooPutData>>): UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> => { 72 72 const mutationOptions: UseMutationOptions<FooPutResponse, DefaultError, Options<FooPutData>> = { 73 73 mutationFn: async (fnOptions) => { 74 - const { data } = await FooBazService.fooService.put({ 74 + const { data } = await FooService.put({ 75 75 ...options, 76 76 ...fnOptions, 77 77 throwOnError: true ··· 100 100 export const fooBarPostMutation = (options?: Partial<Options<FooBarPostData>>): UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> => { 101 101 const mutationOptions: UseMutationOptions<FooBarPostResponse, DefaultError, Options<FooBarPostData>> = { 102 102 mutationFn: async (fnOptions) => { 103 - const { data } = await FooBazService.fooService.barService.post({ 103 + const { data } = await BarService.post({ 104 104 ...options, 105 105 ...fnOptions, 106 106 throwOnError: true ··· 114 114 export const fooBarPutMutation = (options?: Partial<Options<FooBarPutData>>): UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> => { 115 115 const mutationOptions: UseMutationOptions<FooBarPutResponse, DefaultError, Options<FooBarPutData>> = { 116 116 mutationFn: async (fnOptions) => { 117 - const { data } = await FooBazService.fooService.barService.put({ 117 + const { data } = await BarService.put({ 118 118 ...options, 119 119 ...fnOptions, 120 120 throwOnError: true
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/index.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - export { BarBazService, BarService, FooBazService, FooService, type Options } from './sdk.gen'; 3 + export { BarBazService, BarService, BarService2, FooBazService, FooService, FooService2, type Options } from './sdk.gen'; 4 4 export type { ClientOptions, FooBarPostData, FooBarPostResponse, FooBarPostResponses, FooBarPutData, FooBarPutResponse, FooBarPutResponses, FooPostData, FooPostResponse, FooPostResponses, FooPutData, FooPutResponse, FooPutResponses, GetFooBarData, GetFooBarResponse, GetFooBarResponses, GetFooData, GetFooResponse, GetFooResponses } from './types.gen';
+15 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/sdk.gen.ts
··· 48 48 static fooService = FooService; 49 49 } 50 50 51 + export class BarService2 { 52 + public static post<ThrowOnError extends boolean = false>(options?: Options<FooBarPostData, ThrowOnError>) { 53 + return (options?.client ?? client).post<FooBarPostResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 + } 55 + 56 + public static put<ThrowOnError extends boolean = false>(options?: Options<FooBarPutData, ThrowOnError>) { 57 + return (options?.client ?? client).put<FooBarPutResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 58 + } 59 + } 60 + 61 + export class FooService2 { 62 + static barService = BarService2; 63 + } 64 + 51 65 export class BarBazService { 52 66 public static getFooBar<ThrowOnError extends boolean = false>(options?: Options<GetFooBarData, ThrowOnError>) { 53 67 return (options?.client ?? client).get<GetFooBarResponses, unknown, ThrowOnError>({ url: '/foo/bar', ...options }); 54 68 } 55 69 56 - static fooService = FooService; 70 + static fooService = FooService2; 57 71 }
+6 -2
packages/openapi-ts-tests/main/test/plugins.test.ts
··· 550 550 plugins: [ 551 551 { 552 552 httpRequests: { 553 - asClass: true, 553 + containerName: '{{name}}ServiceRequests', 554 + segmentName: '{{name}}Service', 555 + strategy: 'byTags', 554 556 }, 555 557 httpResources: { 556 - asClass: true, 558 + containerName: '{{name}}ServiceResources', 559 + segmentName: '{{name}}Service', 560 + strategy: 'byTags', 557 561 }, 558 562 name: '@angular/common', 559 563 },
+574 -398
packages/openapi-ts-tests/sdks/__snapshots__/method-class-conflict/class/sdk.gen.ts
··· 18 18 meta?: Record<string, unknown>; 19 19 }; 20 20 21 + export class AccountingCompanies { 22 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompaniesCountData, ThrowOnError>) { 23 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompaniesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanies/$count', ...options }); 24 + } 25 + } 26 + 27 + export class Odata { 28 + public static accountingCompanies<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompaniesData, ThrowOnError>) { 29 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompaniesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanies', ...options }); 30 + } 31 + 32 + static accountingCompanies2 = AccountingCompanies; 33 + } 34 + 35 + export class VVersionApiVersion { 36 + static odata = Odata; 37 + } 38 + 39 + export class Api { 40 + static vVersionApiVersion = VVersionApiVersion; 41 + } 42 + 43 + export class AccountingCompanies2 { 44 + public static postApiV1AccountingCompanies<ThrowOnError extends boolean = false>(options?: Options<PostApiV1AccountingCompaniesData, ThrowOnError>) { 45 + return (options?.client ?? client).post<PostApiV1AccountingCompaniesResponses, unknown, ThrowOnError>({ 46 + url: '/api/v1/accounting-companies', 47 + ...options, 48 + headers: { 49 + 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 50 + ...options?.headers 51 + } 52 + }); 53 + } 54 + 55 + public static deleteApiV1AccountingCompaniesById<ThrowOnError extends boolean = false>(options: Options<DeleteApiV1AccountingCompaniesByIdData, ThrowOnError>) { 56 + return (options.client ?? client).delete<DeleteApiV1AccountingCompaniesByIdResponses, unknown, ThrowOnError>({ url: '/api/v1/accounting-companies/{id}', ...options }); 57 + } 58 + 59 + public static postApiV1AccountingCompaniesById<ThrowOnError extends boolean = false>(options: Options<PostApiV1AccountingCompaniesByIdData, ThrowOnError>) { 60 + return (options.client ?? client).post<PostApiV1AccountingCompaniesByIdResponses, unknown, ThrowOnError>({ 61 + url: '/api/v1/accounting-companies/{id}', 62 + ...options, 63 + headers: { 64 + 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 65 + ...options.headers 66 + } 67 + }); 68 + } 69 + 70 + static api = Api; 71 + } 72 + 21 73 export class AccountingCompanyMemberships { 74 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompanyMembershipsCountData, ThrowOnError>) { 75 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompanyMembershipsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanyMemberships/$count', ...options }); 76 + } 77 + } 78 + 79 + export class Odata2 { 80 + public static accountingCompanyMemberships<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompanyMembershipsData, ThrowOnError>) { 81 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompanyMembershipsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanyMemberships', ...options }); 82 + } 83 + 84 + static accountingCompanyMemberships2 = AccountingCompanyMemberships; 85 + } 86 + 87 + export class VVersionApiVersion2 { 88 + static odata = Odata2; 89 + } 90 + 91 + export class Api2 { 92 + static vVersionApiVersion = VVersionApiVersion2; 93 + } 94 + 95 + export class AccountingCompanyMemberships2 { 22 96 public static putApiV1AccountingCompanyMemberships<ThrowOnError extends boolean = false>(options?: Options<PutApiV1AccountingCompanyMembershipsData, ThrowOnError>) { 23 97 return (options?.client ?? client).put<PutApiV1AccountingCompanyMembershipsResponses, unknown, ThrowOnError>({ 24 98 url: '/api/v1/accounting-company-memberships', ··· 34 108 return (options.client ?? client).delete<DeleteApiV1AccountingCompanyMembershipsByIdResponses, unknown, ThrowOnError>({ url: '/api/v1/accounting-company-memberships/{id}', ...options }); 35 109 } 36 110 37 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompanyMembershipsCountData, ThrowOnError>) { 38 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompanyMembershipsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanyMemberships/$count', ...options }); 111 + static api = Api2; 112 + } 113 + 114 + export class BankAccounts { 115 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBankAccountsCountData, ThrowOnError>) { 116 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBankAccountsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BankAccounts/$count', ...options }); 117 + } 118 + } 119 + 120 + export class Odata3 { 121 + public static bankAccounts<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBankAccountsData, ThrowOnError>) { 122 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBankAccountsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BankAccounts', ...options }); 39 123 } 40 124 41 - public static get api() { 42 - return Api; 43 - } 125 + static bankAccounts2 = BankAccounts; 126 + } 127 + 128 + export class VVersionApiVersion3 { 129 + static odata = Odata3; 44 130 } 45 131 46 - export class BankAccounts { 132 + export class Api3 { 133 + static vVersionApiVersion = VVersionApiVersion3; 134 + } 135 + 136 + export class BankAccounts2 { 47 137 public static putApiV1BankAccounts<ThrowOnError extends boolean = false>(options?: Options<PutApiV1BankAccountsData, ThrowOnError>) { 48 138 return (options?.client ?? client).put<PutApiV1BankAccountsResponses, unknown, ThrowOnError>({ 49 139 url: '/api/v1/bank-accounts', ··· 59 149 return (options.client ?? client).delete<DeleteApiV1BankAccountsByIdResponses, unknown, ThrowOnError>({ url: '/api/v1/bank-accounts/{id}', ...options }); 60 150 } 61 151 62 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBankAccountsCountData, ThrowOnError>) { 63 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBankAccountsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BankAccounts/$count', ...options }); 152 + static api = Api3; 153 + } 154 + 155 + export class BusinessAccountantAssignments { 156 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsCountData, ThrowOnError>) { 157 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessAccountantAssignments/$count', ...options }); 158 + } 159 + } 160 + 161 + export class Odata4 { 162 + public static businessAccountantAssignments<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsData, ThrowOnError>) { 163 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessAccountantAssignments', ...options }); 64 164 } 65 165 66 - public static get api() { 67 - return Api; 68 - } 166 + static businessAccountantAssignments2 = BusinessAccountantAssignments; 167 + } 168 + 169 + export class VVersionApiVersion4 { 170 + static odata = Odata4; 171 + } 172 + 173 + export class Api4 { 174 + static vVersionApiVersion = VVersionApiVersion4; 69 175 } 70 176 71 - export class BusinessAccountantAssignments { 177 + export class BusinessAccountantAssignments2 { 72 178 public static putApiV1BusinessAccountantAssignments<ThrowOnError extends boolean = false>(options?: Options<PutApiV1BusinessAccountantAssignmentsData, ThrowOnError>) { 73 179 return (options?.client ?? client).put<PutApiV1BusinessAccountantAssignmentsResponses, unknown, ThrowOnError>({ 74 180 url: '/api/v1/business-accountant-assignments', ··· 84 190 return (options.client ?? client).delete<DeleteApiV1BusinessAccountantAssignmentsByIdResponses, unknown, ThrowOnError>({ url: '/api/v1/business-accountant-assignments/{id}', ...options }); 85 191 } 86 192 87 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsCountData, ThrowOnError>) { 88 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessAccountantAssignments/$count', ...options }); 89 - } 90 - 91 - public static get api() { 92 - return Api; 93 - } 193 + static api = Api4; 94 194 } 95 195 96 196 export class BusinessDocumentActivities { ··· 100 200 } 101 201 102 202 export class BusinessDocuments { 203 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentsCountData, ThrowOnError>) { 204 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocuments/$count', ...options }); 205 + } 206 + } 207 + 208 + export class Odata5 { 209 + public static businessDocumentActivities<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentActivitiesData, ThrowOnError>) { 210 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentActivitiesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocumentActivities', ...options }); 211 + } 212 + 213 + public static businessDocuments<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentsData, ThrowOnError>) { 214 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocuments', ...options }); 215 + } 216 + 217 + static businessDocumentActivities2 = BusinessDocumentActivities; 218 + 219 + static businessDocuments2 = BusinessDocuments; 220 + } 221 + 222 + export class VVersionApiVersion5 { 223 + static odata = Odata5; 224 + } 225 + 226 + export class Api5 { 227 + static vVersionApiVersion = VVersionApiVersion5; 228 + } 229 + 230 + export class BusinessDocuments2 { 103 231 public static getApiV1BusinessDocumentsByIdRaw<ThrowOnError extends boolean = false>(options: Options<GetApiV1BusinessDocumentsByIdRawData, ThrowOnError>) { 104 232 return (options.client ?? client).get<GetApiV1BusinessDocumentsByIdRawResponses, unknown, ThrowOnError>({ url: '/api/v1/business-documents/{id}/raw', ...options }); 105 233 } ··· 154 282 }); 155 283 } 156 284 157 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentsCountData, ThrowOnError>) { 158 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocuments/$count', ...options }); 159 - } 160 - 161 - public static get api() { 162 - return Api; 163 - } 285 + static api = Api5; 164 286 } 165 287 166 288 export class BusinessDocumentsSummaries { ··· 169 291 */ 170 292 public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentsSummariesCountData, ThrowOnError>) { 171 293 return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentsSummariesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocumentsSummaries/$count', ...options }); 294 + } 295 + } 296 + 297 + export class Odata6 { 298 + /** 299 + * @deprecated 300 + */ 301 + public static businessDocumentsSummaries<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentsSummariesData, ThrowOnError>) { 302 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentsSummariesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocumentsSummaries', ...options }); 172 303 } 173 304 174 - public static get api() { 175 - return Api; 176 - } 305 + static businessDocumentsSummaries2 = BusinessDocumentsSummaries; 306 + } 307 + 308 + export class VVersionApiVersion6 { 309 + static odata = Odata6; 310 + } 311 + 312 + export class Api6 { 313 + static vVersionApiVersion = VVersionApiVersion6; 314 + } 315 + 316 + export class BusinessDocumentsSummaries2 { 317 + static api = Api6; 177 318 } 178 319 179 320 export class Businesses { 321 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesCountData, ThrowOnError>) { 322 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses/$count', ...options }); 323 + } 324 + 325 + public static key<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesKey2Data, ThrowOnError>) { 326 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesKey2Responses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses/{key}', ...options }); 327 + } 328 + } 329 + 330 + export class Odata7 { 331 + public static businesses<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesData, ThrowOnError>) { 332 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses', ...options }); 333 + } 334 + 335 + public static businessesKey<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesKeyData, ThrowOnError>) { 336 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesKeyResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses({key})', ...options }); 337 + } 338 + 339 + static businesses2 = Businesses; 340 + } 341 + 342 + export class VVersionApiVersion7 { 343 + static odata = Odata7; 344 + } 345 + 346 + export class Api7 { 347 + static vVersionApiVersion = VVersionApiVersion7; 348 + } 349 + 350 + export class Businesses2 { 180 351 public static getApiV1BusinessesByIdDocumentTypesSummary<ThrowOnError extends boolean = false>(options: Options<GetApiV1BusinessesByIdDocumentTypesSummaryData, ThrowOnError>) { 181 352 return (options.client ?? client).get<GetApiV1BusinessesByIdDocumentTypesSummaryResponses, unknown, ThrowOnError>({ url: '/api/v1/businesses/{id}/document-types-summary', ...options }); 182 353 } ··· 222 393 return (options.client ?? client).post<PostApiV1BusinessesByIdDisconnectResponses, unknown, ThrowOnError>({ url: '/api/v1/businesses/{id}/disconnect', ...options }); 223 394 } 224 395 225 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesCountData, ThrowOnError>) { 226 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses/$count', ...options }); 227 - } 228 - 229 - public static key<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesKey2Data, ThrowOnError>) { 230 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesKey2Responses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses/{key}', ...options }); 231 - } 232 - 233 - public static get api() { 234 - return Api; 396 + static api = Api7; 397 + } 398 + 399 + export class BusinessesMemberships { 400 + public static putApiV1BusinessMemberships<ThrowOnError extends boolean = false>(options?: Options<PutApiV1BusinessMembershipsData, ThrowOnError>) { 401 + return (options?.client ?? client).put<PutApiV1BusinessMembershipsResponses, unknown, ThrowOnError>({ 402 + url: '/api/v1/business-memberships', 403 + ...options, 404 + headers: { 405 + 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 406 + ...options?.headers 407 + } 408 + }); 235 409 } 236 410 } 237 411 ··· 239 413 public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessSummariesCountData, ThrowOnError>) { 240 414 return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessSummariesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessSummaries/$count', ...options }); 241 415 } 416 + } 417 + 418 + export class Odata8 { 419 + public static businessSummaries<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessSummariesData, ThrowOnError>) { 420 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessSummariesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessSummaries', ...options }); 421 + } 242 422 243 - public static get api() { 244 - return Api; 423 + static businessSummaries2 = BusinessSummaries; 424 + } 425 + 426 + export class VVersionApiVersion8 { 427 + static odata = Odata8; 428 + } 429 + 430 + export class Api8 { 431 + static vVersionApiVersion = VVersionApiVersion8; 432 + } 433 + 434 + export class BusinessSummaries2 { 435 + static api = Api8; 436 + } 437 + 438 + export class Counterparties { 439 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataCounterpartiesCountData, ThrowOnError>) { 440 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataCounterpartiesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Counterparties/$count', ...options }); 441 + } 442 + } 443 + 444 + export class Odata9 { 445 + public static counterparties<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataCounterpartiesData, ThrowOnError>) { 446 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataCounterpartiesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Counterparties', ...options }); 245 447 } 448 + 449 + static counterparties2 = Counterparties; 246 450 } 247 451 248 - export class Counterparties { 452 + export class VVersionApiVersion9 { 453 + static odata = Odata9; 454 + } 455 + 456 + export class Api9 { 457 + static vVersionApiVersion = VVersionApiVersion9; 458 + } 459 + 460 + export class Counterparties2 { 249 461 public static postApiV1Counterparties<ThrowOnError extends boolean = false>(options?: Options<PostApiV1CounterpartiesData, ThrowOnError>) { 250 462 return (options?.client ?? client).post<PostApiV1CounterpartiesResponses, unknown, ThrowOnError>({ 251 463 url: '/api/v1/counterparties', ··· 272 484 }); 273 485 } 274 486 275 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataCounterpartiesCountData, ThrowOnError>) { 276 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataCounterpartiesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Counterparties/$count', ...options }); 487 + static api = Api9; 488 + } 489 + 490 + export class DataBoxCredentials { 491 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataDataBoxCredentialsCountData, ThrowOnError>) { 492 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataDataBoxCredentialsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/DataBoxCredentials/$count', ...options }); 493 + } 494 + } 495 + 496 + export class Odata10 { 497 + public static dataBoxCredentials<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataDataBoxCredentialsData, ThrowOnError>) { 498 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataDataBoxCredentialsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/DataBoxCredentials', ...options }); 277 499 } 278 500 279 - public static get api() { 280 - return Api; 281 - } 501 + static dataBoxCredentials2 = DataBoxCredentials; 502 + } 503 + 504 + export class VVersionApiVersion10 { 505 + static odata = Odata10; 506 + } 507 + 508 + export class Api10 { 509 + static vVersionApiVersion = VVersionApiVersion10; 282 510 } 283 511 284 - export class DataBoxCredentials { 512 + export class DataBoxCredentials2 { 285 513 public static putApiV1DataBoxCredentials<ThrowOnError extends boolean = false>(options?: Options<PutApiV1DataBoxCredentialsData, ThrowOnError>) { 286 514 return (options?.client ?? client).put<PutApiV1DataBoxCredentialsResponses, unknown, ThrowOnError>({ 287 515 url: '/api/v1/data-box-credentials', ··· 297 525 return (options.client ?? client).delete<DeleteApiV1DataBoxCredentialsByIdResponses, unknown, ThrowOnError>({ url: '/api/v1/data-box-credentials/{id}', ...options }); 298 526 } 299 527 300 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataDataBoxCredentialsCountData, ThrowOnError>) { 301 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataDataBoxCredentialsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/DataBoxCredentials/$count', ...options }); 528 + static api = Api10; 529 + } 530 + 531 + export class Dev { 532 + public static getApiDev<ThrowOnError extends boolean = false>(options?: Options<GetApiDevData, ThrowOnError>) { 533 + return (options?.client ?? client).get<GetApiDevResponses, unknown, ThrowOnError>({ url: '/api/dev', ...options }); 534 + } 535 + 536 + public static getApiDevReseedDb<ThrowOnError extends boolean = false>(options?: Options<GetApiDevReseedDbData, ThrowOnError>) { 537 + return (options?.client ?? client).get<GetApiDevReseedDbResponses, unknown, ThrowOnError>({ url: '/api/dev/reseed-db', ...options }); 538 + } 539 + 540 + public static getApiDevDbReset<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbResetData, ThrowOnError>) { 541 + return (options?.client ?? client).get<GetApiDevDbResetResponses, unknown, ThrowOnError>({ url: '/api/dev/db-reset', ...options }); 542 + } 543 + 544 + public static getApiDevDbReset2<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbReset2Data, ThrowOnError>) { 545 + return (options?.client ?? client).get<GetApiDevDbReset2Responses, unknown, ThrowOnError>({ url: '/api/dev/db/reset', ...options }); 546 + } 547 + 548 + public static getApiDevDbCreate<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbCreateData, ThrowOnError>) { 549 + return (options?.client ?? client).get<GetApiDevDbCreateResponses, unknown, ThrowOnError>({ url: '/api/dev/db/create', ...options }); 550 + } 551 + 552 + public static getApiDevDbTouch<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbTouchData, ThrowOnError>) { 553 + return (options?.client ?? client).get<GetApiDevDbTouchResponses, unknown, ThrowOnError>({ url: '/api/dev/db/touch', ...options }); 554 + } 555 + 556 + public static getApiDevSeed<ThrowOnError extends boolean = false>(options?: Options<GetApiDevSeedData, ThrowOnError>) { 557 + return (options?.client ?? client).get<GetApiDevSeedResponses, unknown, ThrowOnError>({ url: '/api/dev/seed', ...options }); 558 + } 559 + 560 + public static getApiDevSeedAll<ThrowOnError extends boolean = false>(options?: Options<GetApiDevSeedAllData, ThrowOnError>) { 561 + return (options?.client ?? client).get<GetApiDevSeedAllResponses, unknown, ThrowOnError>({ url: '/api/dev/seed-all', ...options }); 562 + } 563 + 564 + public static getApiDevSeedProd<ThrowOnError extends boolean = false>(options?: Options<GetApiDevSeedProdData, ThrowOnError>) { 565 + return (options?.client ?? client).get<GetApiDevSeedProdResponses, unknown, ThrowOnError>({ url: '/api/dev/seed-prod', ...options }); 566 + } 567 + 568 + public static getApiDevJobProcessRecurringTasks<ThrowOnError extends boolean = false>(options?: Options<GetApiDevJobProcessRecurringTasksData, ThrowOnError>) { 569 + return (options?.client ?? client).get<GetApiDevJobProcessRecurringTasksResponses, unknown, ThrowOnError>({ url: '/api/dev/job/process-recurring-tasks', ...options }); 570 + } 571 + 572 + public static getApiDevNotificationsSendTest<ThrowOnError extends boolean = false>(options?: Options<GetApiDevNotificationsSendTestData, ThrowOnError>) { 573 + return (options?.client ?? client).get<GetApiDevNotificationsSendTestResponses, unknown, ThrowOnError>({ url: '/api/dev/notifications/send-test', ...options }); 574 + } 575 + 576 + public static getApiDevConfirmEmail<ThrowOnError extends boolean = false>(options?: Options<GetApiDevConfirmEmailData, ThrowOnError>) { 577 + return (options?.client ?? client).get<GetApiDevConfirmEmailResponses, unknown, ThrowOnError>({ url: '/api/dev/confirm-email', ...options }); 578 + } 579 + 580 + public static getApiDevEmailSendTest<ThrowOnError extends boolean = false>(options?: Options<GetApiDevEmailSendTestData, ThrowOnError>) { 581 + return (options?.client ?? client).get<GetApiDevEmailSendTestResponses, unknown, ThrowOnError>({ url: '/api/dev/email/send-test', ...options }); 582 + } 583 + } 584 + 585 + export class DeviceTokens { 586 + public static deleteApiV1NotificationsDeviceTokens<ThrowOnError extends boolean = false>(options?: Options<DeleteApiV1NotificationsDeviceTokensData, ThrowOnError>) { 587 + return (options?.client ?? client).delete<DeleteApiV1NotificationsDeviceTokensResponses, unknown, ThrowOnError>({ 588 + url: '/api/v1/notifications/device-tokens', 589 + ...options, 590 + headers: { 591 + 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 592 + ...options?.headers 593 + } 594 + }); 302 595 } 303 596 304 - public static get api() { 305 - return Api; 597 + public static putApiV1NotificationsDeviceTokens<ThrowOnError extends boolean = false>(options?: Options<PutApiV1NotificationsDeviceTokensData, ThrowOnError>) { 598 + return (options?.client ?? client).put<PutApiV1NotificationsDeviceTokensResponses, unknown, ThrowOnError>({ 599 + url: '/api/v1/notifications/device-tokens', 600 + ...options, 601 + headers: { 602 + 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 603 + ...options?.headers 604 + } 605 + }); 306 606 } 307 607 } 308 608 ··· 310 610 public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataDocumentTypesCountData, ThrowOnError>) { 311 611 return (options?.client ?? client).get<ApiVVersionApiVersionOdataDocumentTypesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/DocumentTypes/$count', ...options }); 312 612 } 613 + } 614 + 615 + export class Odata11 { 616 + public static documentTypes<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataDocumentTypesData, ThrowOnError>) { 617 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataDocumentTypesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/DocumentTypes', ...options }); 618 + } 313 619 314 - public static get api() { 315 - return Api; 620 + static documentTypes2 = DocumentTypes; 621 + } 622 + 623 + export class VVersionApiVersion11 { 624 + static odata = Odata11; 625 + } 626 + 627 + export class Api11 { 628 + static vVersionApiVersion = VVersionApiVersion11; 629 + } 630 + 631 + export class DocumentTypes2 { 632 + static api = Api11; 633 + } 634 + 635 + export class Feedback { 636 + public static postApiV1Feedback<ThrowOnError extends boolean = false>(options?: Options<PostApiV1FeedbackData, ThrowOnError>) { 637 + return (options?.client ?? client).post<PostApiV1FeedbackResponses, unknown, ThrowOnError>({ 638 + url: '/api/v1/feedback', 639 + ...options, 640 + headers: { 641 + 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 642 + ...options?.headers 643 + } 644 + }); 316 645 } 317 646 } 318 647 319 648 export class Invitations { 649 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvitationsCountData, ThrowOnError>) { 650 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvitationsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invitations/$count', ...options }); 651 + } 652 + } 653 + 654 + export class Odata12 { 655 + public static invitations<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvitationsData, ThrowOnError>) { 656 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvitationsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invitations', ...options }); 657 + } 658 + 659 + static invitations2 = Invitations; 660 + } 661 + 662 + export class VVersionApiVersion12 { 663 + static odata = Odata12; 664 + } 665 + 666 + export class Api12 { 667 + static vVersionApiVersion = VVersionApiVersion12; 668 + } 669 + 670 + export class Invitations2 { 320 671 public static postApiV1Invitations<ThrowOnError extends boolean = false>(options?: Options<PostApiV1InvitationsData, ThrowOnError>) { 321 672 return (options?.client ?? client).post<PostApiV1InvitationsResponses, unknown, ThrowOnError>({ 322 673 url: '/api/v1/invitations', ··· 358 709 }); 359 710 } 360 711 361 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvitationsCountData, ThrowOnError>) { 362 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvitationsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invitations/$count', ...options }); 712 + static api = Api12; 713 + } 714 + 715 + export class Invoices { 716 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoicesCountData, ThrowOnError>) { 717 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoicesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invoices/$count', ...options }); 718 + } 719 + } 720 + 721 + export class Odata13 { 722 + public static invoices<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoicesData, ThrowOnError>) { 723 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoicesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invoices', ...options }); 363 724 } 364 725 365 - public static get api() { 366 - return Api; 367 - } 726 + static invoices2 = Invoices; 727 + } 728 + 729 + export class VVersionApiVersion13 { 730 + static odata = Odata13; 731 + } 732 + 733 + export class Api13 { 734 + static vVersionApiVersion = VVersionApiVersion13; 368 735 } 369 736 370 - export class Invoices { 737 + export class Invoices2 { 371 738 public static postApiV1Invoices<ThrowOnError extends boolean = false>(options?: Options<PostApiV1InvoicesData, ThrowOnError>) { 372 739 return (options?.client ?? client).post<PostApiV1InvoicesResponses, unknown, ThrowOnError>({ 373 740 url: '/api/v1/invoices', ··· 413 780 return (options.client ?? client).post<PostApiV1InvoicesByIdSnapshotResponses, unknown, ThrowOnError>({ url: '/api/v1/invoices/{id}/snapshot', ...options }); 414 781 } 415 782 416 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoicesCountData, ThrowOnError>) { 417 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoicesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invoices/$count', ...options }); 783 + static api = Api13; 784 + } 785 + 786 + export class InvoiceSettings { 787 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoiceSettingsCountData, ThrowOnError>) { 788 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoiceSettingsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/InvoiceSettings/$count', ...options }); 418 789 } 419 - 420 - public static get api() { 421 - return Api; 790 + } 791 + 792 + export class Odata14 { 793 + public static invoiceSettings<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoiceSettingsData, ThrowOnError>) { 794 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoiceSettingsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/InvoiceSettings', ...options }); 422 795 } 796 + 797 + static invoiceSettings2 = InvoiceSettings; 423 798 } 424 799 425 - export class InvoiceSettings { 800 + export class VVersionApiVersion14 { 801 + static odata = Odata14; 802 + } 803 + 804 + export class Api14 { 805 + static vVersionApiVersion = VVersionApiVersion14; 806 + } 807 + 808 + export class InvoiceSettings2 { 426 809 public static putApiV1InvoiceSettings<ThrowOnError extends boolean = false>(options?: Options<PutApiV1InvoiceSettingsData, ThrowOnError>) { 427 810 return (options?.client ?? client).put<PutApiV1InvoiceSettingsResponses, unknown, ThrowOnError>({ 428 811 url: '/api/v1/invoice-settings', ··· 434 817 }); 435 818 } 436 819 437 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoiceSettingsCountData, ThrowOnError>) { 438 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoiceSettingsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/InvoiceSettings/$count', ...options }); 439 - } 440 - 441 - public static get api() { 442 - return Api; 443 - } 820 + static api = Api14; 444 821 } 445 822 446 823 export class Licenses { 447 824 public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataLicensesCountData, ThrowOnError>) { 448 825 return (options?.client ?? client).get<ApiVVersionApiVersionOdataLicensesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Licenses/$count', ...options }); 449 826 } 827 + } 828 + 829 + export class Odata15 { 830 + public static licenses<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataLicensesData, ThrowOnError>) { 831 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataLicensesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Licenses', ...options }); 832 + } 450 833 451 - public static get api() { 452 - return Api; 834 + static licenses2 = Licenses; 835 + } 836 + 837 + export class VVersionApiVersion15 { 838 + static odata = Odata15; 839 + } 840 + 841 + export class Api15 { 842 + static vVersionApiVersion = VVersionApiVersion15; 843 + } 844 + 845 + export class Licenses2 { 846 + static api = Api15; 847 + } 848 + 849 + export class Notifications { 850 + public static postApiV1NotificationsTest<ThrowOnError extends boolean = false>(options?: Options<PostApiV1NotificationsTestData, ThrowOnError>) { 851 + return (options?.client ?? client).post<PostApiV1NotificationsTestResponses, unknown, ThrowOnError>({ 852 + url: '/api/v1/notifications/test', 853 + ...options, 854 + headers: { 855 + 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 856 + ...options?.headers 857 + } 858 + }); 453 859 } 454 860 } 455 861 456 862 export class PersonalDocuments { 863 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataPersonalDocumentsCountData, ThrowOnError>) { 864 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataPersonalDocumentsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/PersonalDocuments/$count', ...options }); 865 + } 866 + } 867 + 868 + export class Odata16 { 869 + public static personalDocuments<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataPersonalDocumentsData, ThrowOnError>) { 870 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataPersonalDocumentsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/PersonalDocuments', ...options }); 871 + } 872 + 873 + static personalDocuments2 = PersonalDocuments; 874 + } 875 + 876 + export class VVersionApiVersion16 { 877 + static odata = Odata16; 878 + } 879 + 880 + export class Api16 { 881 + static vVersionApiVersion = VVersionApiVersion16; 882 + } 883 + 884 + export class PersonalDocuments2 { 457 885 public static getApiV1PersonalDocumentsByIdRaw<ThrowOnError extends boolean = false>(options: Options<GetApiV1PersonalDocumentsByIdRawData, ThrowOnError>) { 458 886 return (options.client ?? client).get<GetApiV1PersonalDocumentsByIdRawResponses, unknown, ThrowOnError>({ url: '/api/v1/personal-documents/{id}/raw', ...options }); 459 887 } ··· 489 917 return (options?.client ?? client).get<GetApiV1PersonalDocumentsSummaryResponses, unknown, ThrowOnError>({ url: '/api/v1/personal-documents/summary', ...options }); 490 918 } 491 919 492 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataPersonalDocumentsCountData, ThrowOnError>) { 493 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataPersonalDocumentsCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/PersonalDocuments/$count', ...options }); 920 + static api = Api16; 921 + } 922 + 923 + export class RecurringTasks { 924 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataRecurringTasksCountData, ThrowOnError>) { 925 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataRecurringTasksCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/RecurringTasks/$count', ...options }); 494 926 } 495 - 496 - public static get api() { 497 - return Api; 927 + } 928 + 929 + export class Odata17 { 930 + public static recurringTasks<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataRecurringTasksData, ThrowOnError>) { 931 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataRecurringTasksResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/RecurringTasks', ...options }); 498 932 } 933 + 934 + static recurringTasks2 = RecurringTasks; 935 + } 936 + 937 + export class VVersionApiVersion17 { 938 + static odata = Odata17; 499 939 } 500 940 501 - export class RecurringTasks { 941 + export class Api17 { 942 + static vVersionApiVersion = VVersionApiVersion17; 943 + } 944 + 945 + export class RecurringTasks2 { 502 946 public static postApiV1RecurringTasks<ThrowOnError extends boolean = false>(options?: Options<PostApiV1RecurringTasksData, ThrowOnError>) { 503 947 return (options?.client ?? client).post<PostApiV1RecurringTasksResponses, unknown, ThrowOnError>({ 504 948 url: '/api/v1/recurring-tasks', ··· 525 969 }); 526 970 } 527 971 528 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataRecurringTasksCountData, ThrowOnError>) { 529 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataRecurringTasksCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/RecurringTasks/$count', ...options }); 972 + static api = Api17; 973 + } 974 + 975 + export class Root { 976 + public static get<ThrowOnError extends boolean = false>(options?: Options<GetData, ThrowOnError>) { 977 + return (options?.client ?? client).get<GetResponses, unknown, ThrowOnError>({ url: '/', ...options }); 978 + } 979 + } 980 + 981 + export class Tasks { 982 + public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksCountData, ThrowOnError>) { 983 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks/$count', ...options }); 530 984 } 531 985 532 - public static get api() { 533 - return Api; 986 + public static key<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksKey2Data, ThrowOnError>) { 987 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksKey2Responses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks/{key}', ...options }); 534 988 } 535 989 } 536 990 537 - export class Tasks { 991 + export class Odata18 { 992 + public static tasks<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksData, ThrowOnError>) { 993 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks', ...options }); 994 + } 995 + 996 + public static tasksKey<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksKeyData, ThrowOnError>) { 997 + return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksKeyResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks({key})', ...options }); 998 + } 999 + 1000 + static tasks2 = Tasks; 1001 + } 1002 + 1003 + export class VVersionApiVersion18 { 1004 + static odata = Odata18; 1005 + } 1006 + 1007 + export class Api18 { 1008 + static vVersionApiVersion = VVersionApiVersion18; 1009 + } 1010 + 1011 + export class Tasks2 { 538 1012 public static postApiV1Tasks<ThrowOnError extends boolean = false>(options?: Options<PostApiV1TasksData, ThrowOnError>) { 539 1013 return (options?.client ?? client).post<PostApiV1TasksResponses, unknown, ThrowOnError>({ 540 1014 url: '/api/v1/tasks', ··· 591 1065 return (options.client ?? client).get<GetApiV1TasksByIdDocumentsResponses, unknown, ThrowOnError>({ url: '/api/v1/tasks/{id}/documents', ...options }); 592 1066 } 593 1067 594 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksCountData, ThrowOnError>) { 595 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks/$count', ...options }); 596 - } 597 - 598 - public static key<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksKey2Data, ThrowOnError>) { 599 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksKey2Responses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks/{key}', ...options }); 600 - } 601 - 602 - public static get api() { 603 - return Api; 604 - } 1068 + static api = Api18; 605 1069 } 606 1070 607 - export class Odata { 608 - public static accountingCompanies<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompaniesData, ThrowOnError>) { 609 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompaniesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanies', ...options }); 610 - } 611 - 612 - public static accountingCompanyMemberships<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompanyMembershipsData, ThrowOnError>) { 613 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompanyMembershipsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanyMemberships', ...options }); 614 - } 615 - 616 - public static bankAccounts<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBankAccountsData, ThrowOnError>) { 617 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBankAccountsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BankAccounts', ...options }); 618 - } 619 - 620 - public static businessAccountantAssignments<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsData, ThrowOnError>) { 621 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessAccountantAssignmentsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessAccountantAssignments', ...options }); 622 - } 623 - 624 - public static businessDocumentActivities<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentActivitiesData, ThrowOnError>) { 625 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentActivitiesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocumentActivities', ...options }); 626 - } 627 - 628 - public static businessDocuments<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentsData, ThrowOnError>) { 629 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocuments', ...options }); 630 - } 631 - 632 - /** 633 - * @deprecated 634 - */ 635 - public static businessDocumentsSummaries<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessDocumentsSummariesData, ThrowOnError>) { 636 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessDocumentsSummariesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessDocumentsSummaries', ...options }); 637 - } 638 - 639 - public static businesses<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesData, ThrowOnError>) { 640 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses', ...options }); 641 - } 642 - 643 - public static businessesKey<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessesKeyData, ThrowOnError>) { 644 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessesKeyResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Businesses({key})', ...options }); 645 - } 646 - 647 - public static businessSummaries<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataBusinessSummariesData, ThrowOnError>) { 648 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataBusinessSummariesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/BusinessSummaries', ...options }); 649 - } 650 - 651 - public static counterparties<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataCounterpartiesData, ThrowOnError>) { 652 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataCounterpartiesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Counterparties', ...options }); 653 - } 654 - 655 - public static dataBoxCredentials<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataDataBoxCredentialsData, ThrowOnError>) { 656 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataDataBoxCredentialsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/DataBoxCredentials', ...options }); 657 - } 658 - 659 - public static documentTypes<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataDocumentTypesData, ThrowOnError>) { 660 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataDocumentTypesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/DocumentTypes', ...options }); 661 - } 662 - 663 - public static invitations<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvitationsData, ThrowOnError>) { 664 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvitationsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invitations', ...options }); 665 - } 666 - 667 - public static invoices<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoicesData, ThrowOnError>) { 668 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoicesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Invoices', ...options }); 669 - } 670 - 671 - public static invoiceSettings<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataInvoiceSettingsData, ThrowOnError>) { 672 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataInvoiceSettingsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/InvoiceSettings', ...options }); 673 - } 674 - 675 - public static licenses<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataLicensesData, ThrowOnError>) { 676 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataLicensesResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Licenses', ...options }); 677 - } 678 - 679 - public static personalDocuments<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataPersonalDocumentsData, ThrowOnError>) { 680 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataPersonalDocumentsResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/PersonalDocuments', ...options }); 681 - } 682 - 683 - public static recurringTasks<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataRecurringTasksData, ThrowOnError>) { 684 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataRecurringTasksResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/RecurringTasks', ...options }); 685 - } 686 - 687 - public static tasks<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksData, ThrowOnError>) { 688 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks', ...options }); 1071 + export class User { 1072 + public static confirmEmail<ThrowOnError extends boolean = false>(options: Options<MapIdentityApiApiVVersionApiVersionUserConfirmEmailData, ThrowOnError>) { 1073 + return (options.client ?? client).get<MapIdentityApiApiVVersionApiVersionUserConfirmEmailResponses, unknown, ThrowOnError>({ url: '/api/v1/user/confirmEmail', ...options }); 689 1074 } 690 - 691 - public static tasksKey<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataTasksKeyData, ThrowOnError>) { 692 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataTasksKeyResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks({key})', ...options }); 693 - } 694 - 695 - public static get accountingCompanies2() { 696 - return AccountingCompanies; 697 - } 698 - 699 - static accountingCompanyMemberships2 = AccountingCompanyMemberships; 700 - 701 - static bankAccounts2 = BankAccounts; 702 - 703 - static businessAccountantAssignments2 = BusinessAccountantAssignments; 704 - 705 - static businessDocumentActivities2 = BusinessDocumentActivities; 706 - 707 - static businessDocuments2 = BusinessDocuments; 708 - 709 - static businessDocumentsSummaries2 = BusinessDocumentsSummaries; 710 - 711 - static businesses2 = Businesses; 712 - 713 - static businessSummaries2 = BusinessSummaries; 714 - 715 - static counterparties2 = Counterparties; 716 - 717 - static dataBoxCredentials2 = DataBoxCredentials; 718 - 719 - static documentTypes2 = DocumentTypes; 720 - 721 - static invitations2 = Invitations; 722 - 723 - static invoices2 = Invoices; 724 - 725 - static invoiceSettings2 = InvoiceSettings; 726 - 727 - static licenses2 = Licenses; 728 - 729 - static personalDocuments2 = PersonalDocuments; 730 - 731 - static recurringTasks2 = RecurringTasks; 732 - 733 - static tasks2 = Tasks; 1075 + } 1076 + 1077 + export class VVersionApiVersion19 { 1078 + static user = User; 1079 + } 1080 + 1081 + export class Api19 { 1082 + static vVersionApiVersion = VVersionApiVersion19; 734 1083 } 735 1084 736 1085 export class MapIdentityApi { 737 - public static get api() { 738 - return Api; 739 - } 1086 + static api = Api19; 740 1087 } 741 1088 742 - export class User { 1089 + export class User2 { 743 1090 public static postApiV1UserRegister<ThrowOnError extends boolean = false>(options: Options<PostApiV1UserRegisterData, ThrowOnError>) { 744 1091 return (options.client ?? client).post<PostApiV1UserRegisterResponses, PostApiV1UserRegisterErrors, ThrowOnError>({ 745 1092 url: '/api/v1/user/register', ··· 771 1118 ...options.headers 772 1119 } 773 1120 }); 774 - } 775 - 776 - public static confirmEmail<ThrowOnError extends boolean = false>(options: Options<MapIdentityApiApiVVersionApiVersionUserConfirmEmailData, ThrowOnError>) { 777 - return (options.client ?? client).get<MapIdentityApiApiVVersionApiVersionUserConfirmEmailResponses, unknown, ThrowOnError>({ url: '/api/v1/user/confirmEmail', ...options }); 778 1121 } 779 1122 780 1123 public static postApiV1UserResendConfirmationEmail<ThrowOnError extends boolean = false>(options: Options<PostApiV1UserResendConfirmationEmailData, ThrowOnError>) { ··· 859 1202 } 860 1203 861 1204 static mapIdentityApi = MapIdentityApi; 862 - } 863 - 864 - export class VVersionApiVersion { 865 - static odata = Odata; 866 - 867 - static user = User; 868 - } 869 - 870 - export class Api { 871 - static vVersionApiVersion = VVersionApiVersion; 872 - } 873 - 874 - export class AccountingCompanies { 875 - public static postApiV1AccountingCompanies<ThrowOnError extends boolean = false>(options?: Options<PostApiV1AccountingCompaniesData, ThrowOnError>) { 876 - return (options?.client ?? client).post<PostApiV1AccountingCompaniesResponses, unknown, ThrowOnError>({ 877 - url: '/api/v1/accounting-companies', 878 - ...options, 879 - headers: { 880 - 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 881 - ...options?.headers 882 - } 883 - }); 884 - } 885 - 886 - public static deleteApiV1AccountingCompaniesById<ThrowOnError extends boolean = false>(options: Options<DeleteApiV1AccountingCompaniesByIdData, ThrowOnError>) { 887 - return (options.client ?? client).delete<DeleteApiV1AccountingCompaniesByIdResponses, unknown, ThrowOnError>({ url: '/api/v1/accounting-companies/{id}', ...options }); 888 - } 889 - 890 - public static postApiV1AccountingCompaniesById<ThrowOnError extends boolean = false>(options: Options<PostApiV1AccountingCompaniesByIdData, ThrowOnError>) { 891 - return (options.client ?? client).post<PostApiV1AccountingCompaniesByIdResponses, unknown, ThrowOnError>({ 892 - url: '/api/v1/accounting-companies/{id}', 893 - ...options, 894 - headers: { 895 - 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 896 - ...options.headers 897 - } 898 - }); 899 - } 900 - 901 - public static count<ThrowOnError extends boolean = false>(options?: Options<ApiVVersionApiVersionOdataAccountingCompaniesCountData, ThrowOnError>) { 902 - return (options?.client ?? client).get<ApiVVersionApiVersionOdataAccountingCompaniesCountResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/AccountingCompanies/$count', ...options }); 903 - } 904 - 905 - static api = Api; 906 - } 907 - 908 - export class BusinessesMemberships { 909 - public static putApiV1BusinessMemberships<ThrowOnError extends boolean = false>(options?: Options<PutApiV1BusinessMembershipsData, ThrowOnError>) { 910 - return (options?.client ?? client).put<PutApiV1BusinessMembershipsResponses, unknown, ThrowOnError>({ 911 - url: '/api/v1/business-memberships', 912 - ...options, 913 - headers: { 914 - 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 915 - ...options?.headers 916 - } 917 - }); 918 - } 919 - } 920 - 921 - export class Dev { 922 - public static getApiDev<ThrowOnError extends boolean = false>(options?: Options<GetApiDevData, ThrowOnError>) { 923 - return (options?.client ?? client).get<GetApiDevResponses, unknown, ThrowOnError>({ url: '/api/dev', ...options }); 924 - } 925 - 926 - public static getApiDevReseedDb<ThrowOnError extends boolean = false>(options?: Options<GetApiDevReseedDbData, ThrowOnError>) { 927 - return (options?.client ?? client).get<GetApiDevReseedDbResponses, unknown, ThrowOnError>({ url: '/api/dev/reseed-db', ...options }); 928 - } 929 - 930 - public static getApiDevDbReset<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbResetData, ThrowOnError>) { 931 - return (options?.client ?? client).get<GetApiDevDbResetResponses, unknown, ThrowOnError>({ url: '/api/dev/db-reset', ...options }); 932 - } 933 - 934 - public static getApiDevDbReset2<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbReset2Data, ThrowOnError>) { 935 - return (options?.client ?? client).get<GetApiDevDbReset2Responses, unknown, ThrowOnError>({ url: '/api/dev/db/reset', ...options }); 936 - } 937 - 938 - public static getApiDevDbCreate<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbCreateData, ThrowOnError>) { 939 - return (options?.client ?? client).get<GetApiDevDbCreateResponses, unknown, ThrowOnError>({ url: '/api/dev/db/create', ...options }); 940 - } 941 - 942 - public static getApiDevDbTouch<ThrowOnError extends boolean = false>(options?: Options<GetApiDevDbTouchData, ThrowOnError>) { 943 - return (options?.client ?? client).get<GetApiDevDbTouchResponses, unknown, ThrowOnError>({ url: '/api/dev/db/touch', ...options }); 944 - } 945 - 946 - public static getApiDevSeed<ThrowOnError extends boolean = false>(options?: Options<GetApiDevSeedData, ThrowOnError>) { 947 - return (options?.client ?? client).get<GetApiDevSeedResponses, unknown, ThrowOnError>({ url: '/api/dev/seed', ...options }); 948 - } 949 - 950 - public static getApiDevSeedAll<ThrowOnError extends boolean = false>(options?: Options<GetApiDevSeedAllData, ThrowOnError>) { 951 - return (options?.client ?? client).get<GetApiDevSeedAllResponses, unknown, ThrowOnError>({ url: '/api/dev/seed-all', ...options }); 952 - } 953 - 954 - public static getApiDevSeedProd<ThrowOnError extends boolean = false>(options?: Options<GetApiDevSeedProdData, ThrowOnError>) { 955 - return (options?.client ?? client).get<GetApiDevSeedProdResponses, unknown, ThrowOnError>({ url: '/api/dev/seed-prod', ...options }); 956 - } 957 - 958 - public static getApiDevJobProcessRecurringTasks<ThrowOnError extends boolean = false>(options?: Options<GetApiDevJobProcessRecurringTasksData, ThrowOnError>) { 959 - return (options?.client ?? client).get<GetApiDevJobProcessRecurringTasksResponses, unknown, ThrowOnError>({ url: '/api/dev/job/process-recurring-tasks', ...options }); 960 - } 961 - 962 - public static getApiDevNotificationsSendTest<ThrowOnError extends boolean = false>(options?: Options<GetApiDevNotificationsSendTestData, ThrowOnError>) { 963 - return (options?.client ?? client).get<GetApiDevNotificationsSendTestResponses, unknown, ThrowOnError>({ url: '/api/dev/notifications/send-test', ...options }); 964 - } 965 - 966 - public static getApiDevConfirmEmail<ThrowOnError extends boolean = false>(options?: Options<GetApiDevConfirmEmailData, ThrowOnError>) { 967 - return (options?.client ?? client).get<GetApiDevConfirmEmailResponses, unknown, ThrowOnError>({ url: '/api/dev/confirm-email', ...options }); 968 - } 969 - 970 - public static getApiDevEmailSendTest<ThrowOnError extends boolean = false>(options?: Options<GetApiDevEmailSendTestData, ThrowOnError>) { 971 - return (options?.client ?? client).get<GetApiDevEmailSendTestResponses, unknown, ThrowOnError>({ url: '/api/dev/email/send-test', ...options }); 972 - } 973 - } 974 - 975 - export class DeviceTokens { 976 - public static deleteApiV1NotificationsDeviceTokens<ThrowOnError extends boolean = false>(options?: Options<DeleteApiV1NotificationsDeviceTokensData, ThrowOnError>) { 977 - return (options?.client ?? client).delete<DeleteApiV1NotificationsDeviceTokensResponses, unknown, ThrowOnError>({ 978 - url: '/api/v1/notifications/device-tokens', 979 - ...options, 980 - headers: { 981 - 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 982 - ...options?.headers 983 - } 984 - }); 985 - } 986 - 987 - public static putApiV1NotificationsDeviceTokens<ThrowOnError extends boolean = false>(options?: Options<PutApiV1NotificationsDeviceTokensData, ThrowOnError>) { 988 - return (options?.client ?? client).put<PutApiV1NotificationsDeviceTokensResponses, unknown, ThrowOnError>({ 989 - url: '/api/v1/notifications/device-tokens', 990 - ...options, 991 - headers: { 992 - 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 993 - ...options?.headers 994 - } 995 - }); 996 - } 997 - } 998 - 999 - export class Feedback { 1000 - public static postApiV1Feedback<ThrowOnError extends boolean = false>(options?: Options<PostApiV1FeedbackData, ThrowOnError>) { 1001 - return (options?.client ?? client).post<PostApiV1FeedbackResponses, unknown, ThrowOnError>({ 1002 - url: '/api/v1/feedback', 1003 - ...options, 1004 - headers: { 1005 - 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 1006 - ...options?.headers 1007 - } 1008 - }); 1009 - } 1010 - } 1011 - 1012 - export class Notifications { 1013 - public static postApiV1NotificationsTest<ThrowOnError extends boolean = false>(options?: Options<PostApiV1NotificationsTestData, ThrowOnError>) { 1014 - return (options?.client ?? client).post<PostApiV1NotificationsTestResponses, unknown, ThrowOnError>({ 1015 - url: '/api/v1/notifications/test', 1016 - ...options, 1017 - headers: { 1018 - 'Content-Type': 'application/json;odata.metadata=minimal;odata.streaming=true', 1019 - ...options?.headers 1020 - } 1021 - }); 1022 - } 1023 - } 1024 - 1025 - export class Root { 1026 - public static get<ThrowOnError extends boolean = false>(options?: Options<GetData, ThrowOnError>) { 1027 - return (options?.client ?? client).get<GetResponses, unknown, ThrowOnError>({ url: '/', ...options }); 1028 - } 1029 1205 } 1030 1206 1031 1207 export class Users {
+60 -51
packages/openapi-ts-tests/sdks/__snapshots__/method-class-conflict/instance/sdk.gen.ts
··· 259 259 return (options?.client ?? this.client).get<ApiVVersionApiVersionOdataTasksKeyResponses, unknown, ThrowOnError>({ url: '/api/v1/odata/Tasks({key})', ...options }); 260 260 } 261 261 262 - private _accountingCompanies2?: AccountingCompanies; 262 + private _accountingCompanies?: AccountingCompanies; 263 263 get accountingCompanies2(): AccountingCompanies { 264 - return this._accountingCompanies2 ??= new AccountingCompanies({ client: this.client }); 264 + return this._accountingCompanies ??= new AccountingCompanies({ client: this.client }); 265 265 } 266 266 267 - private _accountingCompanyMemberships2?: AccountingCompanyMemberships; 267 + private _accountingCompanyMemberships?: AccountingCompanyMemberships; 268 268 get accountingCompanyMemberships2(): AccountingCompanyMemberships { 269 - return this._accountingCompanyMemberships2 ??= new AccountingCompanyMemberships({ client: this.client }); 269 + return this._accountingCompanyMemberships ??= new AccountingCompanyMemberships({ client: this.client }); 270 270 } 271 271 272 - private _bankAccounts2?: BankAccounts; 272 + private _bankAccounts?: BankAccounts; 273 273 get bankAccounts2(): BankAccounts { 274 - return this._bankAccounts2 ??= new BankAccounts({ client: this.client }); 274 + return this._bankAccounts ??= new BankAccounts({ client: this.client }); 275 275 } 276 276 277 - private _businessAccountantAssignments2?: BusinessAccountantAssignments; 277 + private _businessAccountantAssignments?: BusinessAccountantAssignments; 278 278 get businessAccountantAssignments2(): BusinessAccountantAssignments { 279 - return this._businessAccountantAssignments2 ??= new BusinessAccountantAssignments({ client: this.client }); 279 + return this._businessAccountantAssignments ??= new BusinessAccountantAssignments({ client: this.client }); 280 280 } 281 281 282 - private _businessDocumentActivities2?: BusinessDocumentActivities; 282 + private _businessDocumentActivities?: BusinessDocumentActivities; 283 283 get businessDocumentActivities2(): BusinessDocumentActivities { 284 - return this._businessDocumentActivities2 ??= new BusinessDocumentActivities({ client: this.client }); 284 + return this._businessDocumentActivities ??= new BusinessDocumentActivities({ client: this.client }); 285 285 } 286 286 287 - private _businessDocuments2?: BusinessDocuments; 287 + private _businessDocuments?: BusinessDocuments; 288 288 get businessDocuments2(): BusinessDocuments { 289 - return this._businessDocuments2 ??= new BusinessDocuments({ client: this.client }); 289 + return this._businessDocuments ??= new BusinessDocuments({ client: this.client }); 290 290 } 291 291 292 - private _businessDocumentsSummaries2?: BusinessDocumentsSummaries; 292 + private _businessDocumentsSummaries?: BusinessDocumentsSummaries; 293 293 get businessDocumentsSummaries2(): BusinessDocumentsSummaries { 294 - return this._businessDocumentsSummaries2 ??= new BusinessDocumentsSummaries({ client: this.client }); 294 + return this._businessDocumentsSummaries ??= new BusinessDocumentsSummaries({ client: this.client }); 295 295 } 296 296 297 - private _businesses2?: Businesses; 297 + private _businesses?: Businesses; 298 298 get businesses2(): Businesses { 299 - return this._businesses2 ??= new Businesses({ client: this.client }); 299 + return this._businesses ??= new Businesses({ client: this.client }); 300 300 } 301 301 302 - private _businessSummaries2?: BusinessSummaries; 302 + private _businessSummaries?: BusinessSummaries; 303 303 get businessSummaries2(): BusinessSummaries { 304 - return this._businessSummaries2 ??= new BusinessSummaries({ client: this.client }); 304 + return this._businessSummaries ??= new BusinessSummaries({ client: this.client }); 305 305 } 306 306 307 - private _counterparties2?: Counterparties; 307 + private _counterparties?: Counterparties; 308 308 get counterparties2(): Counterparties { 309 - return this._counterparties2 ??= new Counterparties({ client: this.client }); 309 + return this._counterparties ??= new Counterparties({ client: this.client }); 310 310 } 311 311 312 - private _dataBoxCredentials2?: DataBoxCredentials; 312 + private _dataBoxCredentials?: DataBoxCredentials; 313 313 get dataBoxCredentials2(): DataBoxCredentials { 314 - return this._dataBoxCredentials2 ??= new DataBoxCredentials({ client: this.client }); 314 + return this._dataBoxCredentials ??= new DataBoxCredentials({ client: this.client }); 315 315 } 316 316 317 - private _documentTypes2?: DocumentTypes; 317 + private _documentTypes?: DocumentTypes; 318 318 get documentTypes2(): DocumentTypes { 319 - return this._documentTypes2 ??= new DocumentTypes({ client: this.client }); 319 + return this._documentTypes ??= new DocumentTypes({ client: this.client }); 320 320 } 321 321 322 - private _invitations2?: Invitations; 322 + private _invitations?: Invitations; 323 323 get invitations2(): Invitations { 324 - return this._invitations2 ??= new Invitations({ client: this.client }); 324 + return this._invitations ??= new Invitations({ client: this.client }); 325 325 } 326 326 327 - private _invoices2?: Invoices; 327 + private _invoices?: Invoices; 328 328 get invoices2(): Invoices { 329 - return this._invoices2 ??= new Invoices({ client: this.client }); 329 + return this._invoices ??= new Invoices({ client: this.client }); 330 330 } 331 331 332 - private _invoiceSettings2?: InvoiceSettings; 332 + private _invoiceSettings?: InvoiceSettings; 333 333 get invoiceSettings2(): InvoiceSettings { 334 - return this._invoiceSettings2 ??= new InvoiceSettings({ client: this.client }); 334 + return this._invoiceSettings ??= new InvoiceSettings({ client: this.client }); 335 335 } 336 336 337 - private _licenses2?: Licenses; 337 + private _licenses?: Licenses; 338 338 get licenses2(): Licenses { 339 - return this._licenses2 ??= new Licenses({ client: this.client }); 339 + return this._licenses ??= new Licenses({ client: this.client }); 340 340 } 341 341 342 - private _personalDocuments2?: PersonalDocuments; 342 + private _personalDocuments?: PersonalDocuments; 343 343 get personalDocuments2(): PersonalDocuments { 344 - return this._personalDocuments2 ??= new PersonalDocuments({ client: this.client }); 344 + return this._personalDocuments ??= new PersonalDocuments({ client: this.client }); 345 345 } 346 346 347 - private _recurringTasks2?: RecurringTasks; 347 + private _recurringTasks?: RecurringTasks; 348 348 get recurringTasks2(): RecurringTasks { 349 - return this._recurringTasks2 ??= new RecurringTasks({ client: this.client }); 349 + return this._recurringTasks ??= new RecurringTasks({ client: this.client }); 350 350 } 351 351 352 - private _tasks2?: Tasks; 352 + private _tasks?: Tasks; 353 353 get tasks2(): Tasks { 354 - return this._tasks2 ??= new Tasks({ client: this.client }); 354 + return this._tasks ??= new Tasks({ client: this.client }); 355 + } 356 + } 357 + 358 + export class VVersionApiVersion extends HeyApiClient { 359 + private _odata?: Odata; 360 + get odata(): Odata { 361 + return this._odata ??= new Odata({ client: this.client }); 362 + } 363 + } 364 + 365 + export class Api extends HeyApiClient { 366 + private _vVersionApiVersion?: VVersionApiVersion; 367 + get vVersionApiVersion(): VVersionApiVersion { 368 + return this._vVersionApiVersion ??= new VVersionApiVersion({ client: this.client }); 355 369 } 356 370 } 357 371 ··· 361 375 } 362 376 } 363 377 364 - export class VVersionApiVersion extends HeyApiClient { 365 - private _odata?: Odata; 366 - get odata(): Odata { 367 - return this._odata ??= new Odata({ client: this.client }); 368 - } 369 - 378 + export class VVersionApiVersion2 extends HeyApiClient { 370 379 private _user?: User; 371 380 get user(): User { 372 381 return this._user ??= new User({ client: this.client }); 373 382 } 374 383 } 375 384 376 - export class Api extends HeyApiClient { 377 - private _vVersionApiVersion?: VVersionApiVersion; 378 - get vVersionApiVersion(): VVersionApiVersion { 379 - return this._vVersionApiVersion ??= new VVersionApiVersion({ client: this.client }); 385 + export class Api2 extends HeyApiClient { 386 + private _vVersionApiVersion?: VVersionApiVersion2; 387 + get vVersionApiVersion(): VVersionApiVersion2 { 388 + return this._vVersionApiVersion ??= new VVersionApiVersion2({ client: this.client }); 380 389 } 381 390 } 382 391 383 392 export class MapIdentityApi extends HeyApiClient { 384 - private _api?: Api; 385 - get api(): Api { 386 - return this._api ??= new Api({ client: this.client }); 393 + private _api?: Api2; 394 + get api(): Api2 { 395 + return this._api ??= new Api2({ client: this.client }); 387 396 } 388 397 } 389 398
+5 -13
packages/openapi-ts/src/__tests__/internal.test.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 3 3 describe('internal entry index', () => { 4 - it('getSpec should be exported', async () => { 5 - const { getSpec } = await import('../internal'); 6 - expect(getSpec).toBeDefined(); 7 - }); 8 - 9 - it('initConfigs should be exported', async () => { 10 - const { initConfigs } = await import('../internal'); 11 - expect(initConfigs).toBeDefined(); 12 - }); 13 - 14 - it('parseOpenApiSpec should be exported', async () => { 15 - const { parseOpenApiSpec } = await import('../internal'); 16 - expect(parseOpenApiSpec).toBeDefined(); 4 + it('should be exported', async () => { 5 + const internal = await import('../internal'); 6 + expect(internal.getSpec).toBeDefined(); 7 + expect(internal.initConfigs).toBeDefined(); 8 + expect(internal.parseOpenApiSpec).toBeDefined(); 17 9 }); 18 10 });
+1
packages/openapi-ts/src/config/output.ts
··· 21 21 suffix: '.gen', 22 22 }, 23 23 format: null, 24 + header: '// This file is auto-generated by @hey-api/openapi-ts', 24 25 indexFile: true, 25 26 lint: null, 26 27 path: '',
+11 -8
packages/openapi-ts/src/config/utils/config.ts
··· 3 3 ? Record<string, any> 4 4 : Extract<T, Record<string, any>>; 5 5 6 - type NotArray<T> = T extends any[] ? never : T; 7 - type NotFunction<T> = T extends (...args: any[]) => any ? never : T; 6 + type NotArray<T> = T extends Array<any> ? never : T; 7 + type NotFunction<T> = T extends (...args: Array<any>) => any ? never : T; 8 8 type PlainObject<T> = T extends object 9 9 ? NotFunction<T> extends never 10 10 ? never ··· 17 17 boolean: T extends boolean 18 18 ? (value: boolean) => Partial<ObjectType<T>> 19 19 : never; 20 - function: T extends (...args: any[]) => any 21 - ? (value: (...args: any[]) => any) => Partial<ObjectType<T>> 20 + function: T extends (...args: Array<any>) => any 21 + ? (value: (...args: Array<any>) => any) => Partial<ObjectType<T>> 22 22 : never; 23 23 number: T extends number ? (value: number) => Partial<ObjectType<T>> : never; 24 24 object?: PlainObject<T> extends never ··· 35 35 type IsObjectOnly<T> = T extends Record<string, any> | undefined 36 36 ? Extract< 37 37 T, 38 - string | boolean | number | ((...args: any[]) => any) 38 + string | boolean | number | ((...args: Array<any>) => any) 39 39 > extends never 40 40 ? true 41 41 : false ··· 47 47 | string 48 48 | boolean 49 49 | number 50 - | ((...args: any[]) => any) 50 + | ((...args: Array<any>) => any) 51 51 | Record<string, any>, 52 52 >( 53 53 args: { ··· 99 99 case 'function': 100 100 if (mappers && 'function' in mappers) { 101 101 const mapper = mappers.function as ( 102 - value: (...args: any[]) => any, 102 + value: (...args: Array<any>) => any, 103 103 ) => Record<string, any>; 104 - result = mergeResult(result, mapper(value as (...args: any[]) => any)); 104 + result = mergeResult( 105 + result, 106 + mapper(value as (...args: Array<any>) => any), 107 + ); 105 108 } 106 109 break; 107 110 case 'number':
+1
packages/openapi-ts/src/index.ts
··· 88 88 export { defaultPaginationKeywords } from '~/config/parser'; 89 89 export { defaultPlugins } from '~/config/plugins'; 90 90 export type { IR } from '~/ir/types'; 91 + export { OperationPath, OperationStrategy } from '~/openApi/shared/locations'; 91 92 export type { 92 93 OpenApi, 93 94 OpenApiMetaObject,
+3 -5
packages/openapi-ts/src/ir/context.ts
··· 3 3 import type { Package } from '~/config/utils/package'; 4 4 import { packageFactory } from '~/config/utils/package'; 5 5 import type { Graph } from '~/graph'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { PluginConfigMap } from '~/plugins/config'; 8 7 import { PluginInstance } from '~/plugins/shared/utils/instance'; 9 8 import type { PluginNames } from '~/plugins/types'; 10 9 import { TypeScriptRenderer } from '~/ts-dsl'; 11 10 import type { Config } from '~/types/config'; 12 11 import type { Logger } from '~/utils/logger'; 12 + import { applyNaming } from '~/utils/naming'; 13 13 import { resolveRef } from '~/utils/ref'; 14 14 15 15 import type { IR } from './types'; ··· 72 72 this.gen = new Project({ 73 73 defaultFileName: 'index', 74 74 fileName: (base) => { 75 - const name = buildName({ 76 - config: config.output.fileName, 77 - name: base, 78 - }); 75 + const name = applyNaming(base, config.output.fileName); 79 76 const { suffix } = config.output.fileName; 80 77 if (!suffix) { 81 78 return name; ··· 91 88 : undefined, 92 89 renderers: [ 93 90 new TypeScriptRenderer({ 91 + header: config.output.header, 94 92 preferExportAll: config.output.preferExportAll, 95 93 preferFileExtension: config.output.importFileExtension || undefined, 96 94 resolveModuleName: config.output.resolveModuleName,
+1 -1
packages/openapi-ts/src/openApi/common/parser/__tests__/sanitize.test.ts packages/openapi-ts/src/openApi/shared/utils/__tests__/sanitize.test.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 3 - import { sanitizeNamespaceIdentifier } from '../sanitize'; 3 + import { sanitizeNamespaceIdentifier } from '../operation'; 4 4 5 5 describe('sanitizeNamespaceIdentifier', () => { 6 6 it.each([
-21
packages/openapi-ts/src/openApi/common/parser/sanitize.ts
··· 1 - /** 2 - * Sanitizes namespace identifiers so they are valid TypeScript identifiers of a certain form. 3 - * 4 - * 1: Remove any leading characters that are illegal as starting character of a typescript identifier. 5 - * 2: Replace illegal characters in remaining part of type name with hyphen (-). 6 - * 7 - * Step 1 should perhaps instead also replace illegal characters with underscore, or prefix with it, like sanitizeEnumName 8 - * does. The way this is now one could perhaps end up removing all characters, if all are illegal start characters. It 9 - * would be sort of a breaking change to do so, though, previously generated code might change then. 10 - * 11 - * JavaScript identifier regexp pattern retrieved from https://developer.mozilla.org/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers 12 - * 13 - * The output of this is expected to be converted to PascalCase 14 - * 15 - * @deprecated 16 - */ 17 - export const sanitizeNamespaceIdentifier = (name: string) => 18 - name 19 - .replace(/^[^\p{ID_Start}]+/u, '') 20 - .replace(/[^$\u200c\u200d\p{ID_Continue}]/gu, '-') 21 - .replace(/[$+]/g, '-');
+6
packages/openapi-ts/src/openApi/shared/locations/index.ts
··· 1 + export type { 2 + OperationPathStrategy, 3 + OperationsStrategy, 4 + OperationStructureStrategy, 5 + } from './operation'; 6 + export { OperationPath, OperationStrategy } from './operation';
+213
packages/openapi-ts/src/openApi/shared/locations/operation.ts
··· 1 + import type { StructureLocation } from '@hey-api/codegen-core'; 2 + 3 + import type { IR } from '~/ir/types'; 4 + 5 + /** 6 + * A function that derives path segments from an operation. 7 + * 8 + * Used by location strategies to build paths within containers. 9 + */ 10 + export type OperationPathStrategy = ( 11 + operation: IR.OperationObject, 12 + ) => ReadonlyArray<string>; 13 + 14 + /** 15 + * A function that determines where an operation appears in the structure. 16 + * 17 + * Returns one or more locations, each with a full path. 18 + */ 19 + export type OperationStructureStrategy = ( 20 + operation: IR.OperationObject, 21 + ) => ReadonlyArray<StructureLocation['path']>; 22 + 23 + export type OperationsStrategy = 24 + | 'byTags' 25 + | 'flat' 26 + | 'single' 27 + | OperationStructureStrategy; 28 + 29 + /** 30 + * Built-in strategies for operations. 31 + */ 32 + export const OperationStrategy = { 33 + /** 34 + * Creates one root container per operation tag. 35 + * 36 + * Operations with multiple tags appear in multiple root containers. 37 + * Operations without tags use the fallback root container. 38 + * 39 + * @example 40 + * // Operation with tags: ['users', 'admin'] 41 + * // Path function returns: ['list'] 42 + * // Result: [{ path: ['users', 'list'], shell }, { path: ['admin', 'list'], shell }] 43 + */ 44 + byTags: 45 + (config: { 46 + /** 47 + * Root name for operations without tags. 48 + */ 49 + fallback: string; 50 + /** 51 + * Derives path segments from the operation. 52 + * 53 + * @default OperationPath.id() 54 + */ 55 + path?: OperationPathStrategy; 56 + }): OperationStructureStrategy => 57 + (operation) => { 58 + const tags = 59 + operation.tags && operation.tags.length > 0 60 + ? operation.tags 61 + : [config.fallback]; 62 + const path = config.path ?? OperationPath.id(); 63 + const pathSegments = path(operation); 64 + return tags.map((tag) => [tag, ...pathSegments]); 65 + }, 66 + 67 + /** 68 + * Creates flat functions without any container. 69 + * 70 + * Each operation becomes a standalone function at the root level. 71 + * No shell is applied. 72 + * 73 + * @example 74 + * // Operation id: 'getUsers' 75 + * // Result: [{ path: ['getUsers'] }] 76 + */ 77 + flat: 78 + (config?: { 79 + /** 80 + * Derives path segments within the root from the operation. 81 + * 82 + * @default OperationPath.id() 83 + */ 84 + path?: OperationPathStrategy; 85 + }): OperationStructureStrategy => 86 + (operation) => { 87 + const path = config?.path ?? OperationPath.id(); 88 + const pathSegments = path(operation); 89 + return [[pathSegments[pathSegments.length - 1]!]]; 90 + }, 91 + 92 + /** 93 + * Places all operations under a single root container. 94 + * 95 + * @example 96 + * // Root: 'Sdk', path function returns: ['users', 'list'] 97 + * // Result: [{ path: ['Sdk', 'users', 'list'], shell }] 98 + */ 99 + single: 100 + (config: { 101 + /** 102 + * Derives path segments within the root from the operation. 103 + * 104 + * @default OperationPath.id() 105 + */ 106 + path?: OperationPathStrategy; 107 + /** 108 + * Name of the container. 109 + */ 110 + root: string; 111 + }): OperationStructureStrategy => 112 + (operation) => { 113 + const path = config.path ?? OperationPath.id(); 114 + const pathSegments = path(operation); 115 + return [[config.root, ...pathSegments]]; 116 + }, 117 + }; 118 + 119 + /** 120 + * Built-in path derivation helpers for operations. 121 + */ 122 + export const OperationPath = { 123 + /** 124 + * Splits operationId by delimiters to create nested paths. 125 + * 126 + * @example 127 + * // operationId: 'users.accounts.list' 128 + * // Result: ['users', 'accounts', 'list'] 129 + * 130 + * @example 131 + * // operationId: 'users/accounts/getAll' 132 + * // Result: ['users', 'accounts', 'getAll'] 133 + */ 134 + fromOperationId: 135 + (config?: { 136 + /** 137 + * Pattern to split operationId. 138 + * 139 + * @default /[./]/ 140 + */ 141 + delimiters?: RegExp; 142 + /** 143 + * Fallback strategy if operationId is missing. 144 + * 145 + * @default OperationPath.id() 146 + */ 147 + fallback?: OperationPathStrategy; 148 + }): OperationPathStrategy => 149 + (operation) => { 150 + const fallback = config?.fallback ?? OperationPath.id(); 151 + if (!operation.operationId) return fallback(operation); 152 + const delimiters = config?.delimiters ?? /[./]/; 153 + const segments = operation.operationId.split(delimiters).filter(Boolean); 154 + return segments.length === 0 ? fallback(operation) : segments; 155 + }, 156 + 157 + /** 158 + * Splits path by delimiters to create nested paths. 159 + * 160 + * Can include the method as a prefix or suffix segment. 161 + * 162 + * @example 163 + * // path: '/users/{id}/accounts', method: 'get', delimiters: /[\/{}]+/, methodPosition: 'none' 164 + * // Result: ['users', 'id', 'accounts'] 165 + * 166 + * @example 167 + * // path: '/users/{id}/accounts', method: 'get', delimiters: /[\/{}]+/, methodPosition: 'prefix' 168 + * // Result: ['get', 'users', 'id', 'accounts'] 169 + * 170 + * @example 171 + * // path: '/users/{id}/accounts', method: 'get', delimiters: /[\/{}]+/, methodPosition: 'suffix' 172 + * // Result: ['users', 'id', 'accounts', 'get'] 173 + */ 174 + fromPath: 175 + (config?: { 176 + /** 177 + * Pattern to split the path. 178 + * 179 + * @default /[./]/ 180 + */ 181 + delimiters?: RegExp; 182 + /** 183 + * Position of the method segment. 184 + * 185 + * @default 'none' 186 + */ 187 + methodPosition?: 'prefix' | 'suffix'; 188 + }): OperationPathStrategy => 189 + (operation) => { 190 + const delimiters = config?.delimiters ?? /[./]/; 191 + const segments = operation.path.split(delimiters).filter(Boolean); 192 + switch (config?.methodPosition) { 193 + case 'prefix': 194 + segments.unshift(operation.method.toLowerCase()); 195 + break; 196 + case 'suffix': 197 + segments.push(operation.method.toLowerCase()); 198 + break; 199 + default: 200 + break; 201 + } 202 + return segments; 203 + }, 204 + 205 + /** 206 + * Uses operation.id as a single path segment. 207 + * 208 + * @example 209 + * // operation.id: 'getUserById' 210 + * // Result: ['getUserById'] 211 + */ 212 + id: (): OperationPathStrategy => (operation) => [operation.id], 213 + };
+7 -8
packages/openapi-ts/src/openApi/shared/transforms/enums.ts
··· 1 + import { applyNaming } from '~/utils/naming'; 1 2 import { jsonPointerToPath } from '~/utils/ref'; 2 3 3 4 import type { Config } from '../../../types/config'; 4 - import { buildName } from '../utils/name'; 5 5 import { deepClone } from '../utils/schema'; 6 6 import { childSchemaRelationships } from '../utils/schemaChildRelationships'; 7 7 import { getSchemasObject } from '../utils/transforms'; ··· 215 215 } 216 216 217 217 // Generate a unique name for the new root enum using config 218 - const base = buildName({ 219 - config, 220 - name: 221 - typeof node === 'object' && 218 + const base = applyNaming( 219 + typeof node === 'object' && 222 220 node && 223 221 'title' in node && 224 222 typeof node.title === 'string' 225 - ? node.title 226 - : String(key), 227 - }); 223 + ? node.title 224 + : String(key), 225 + config, 226 + ); 228 227 const name = getUniqueComponentName({ 229 228 base, 230 229 components: schemasObj,
+3 -9
packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts
··· 1 1 import type { Graph } from '~/graph'; 2 2 import type { Logger } from '~/utils/logger'; 3 + import { applyNaming } from '~/utils/naming'; 3 4 import { jsonPointerToPath } from '~/utils/ref'; 4 5 5 6 import type { Config } from '../../../types/config'; 6 7 import deepEqual from '../utils/deepEqual'; 7 8 import { buildGraph, type Scope } from '../utils/graph'; 8 - import { buildName } from '../utils/name'; 9 9 import { deepClone } from '../utils/schema'; 10 10 import { childSchemaRelationships } from '../utils/schemaChildRelationships'; 11 11 import { ··· 404 404 // read variant 405 405 const readSchema = deepClone<unknown>(nodeInfo.node); 406 406 pruneSchemaByScope(graph, readSchema, 'writeOnly'); 407 - const readBase = buildName({ 408 - config: config.responses, 409 - name, 410 - }); 407 + const readBase = applyNaming(name, config.responses); 411 408 const readName = 412 409 readBase === name 413 410 ? readBase ··· 448 445 ) { 449 446 continue; 450 447 } 451 - const writeBase = buildName({ 452 - config: config.requests, 453 - name, 454 - }); 448 + const writeBase = applyNaming(name, config.requests); 455 449 const writeName = 456 450 writeBase === name && writeBase !== readName 457 451 ? writeBase
-23
packages/openapi-ts/src/openApi/shared/utils/name.ts
··· 1 - import { stringCase } from '~/utils/stringCase'; 2 - 3 - import type { StringCase, StringName } from '../../../types/case'; 4 - 5 - export const buildName = ({ 6 - config, 7 - name, 8 - }: { 9 - config: { 10 - case: StringCase; 11 - name?: StringName; 12 - }; 13 - name: string; 14 - }): string => { 15 - if (typeof config.name === 'function') { 16 - name = config.name(name); 17 - } else if (config.name) { 18 - const separator = config.case === 'preserve' ? '' : '-'; 19 - name = config.name.replace('{{name}}', `${separator}${name}${separator}`); 20 - } 21 - 22 - return stringCase({ case: config.case, value: name }); 23 - };
+35 -11
packages/openapi-ts/src/openApi/shared/utils/operation.ts
··· 1 1 import type { Context } from '~/ir/context'; 2 2 import { createOperationKey } from '~/ir/operation'; 3 - import { sanitizeNamespaceIdentifier } from '~/openApi/common/parser/sanitize'; 4 - import { stringCase } from '~/utils/stringCase'; 3 + import { toCase } from '~/utils/naming'; 5 4 6 5 import type { State } from '../types/state'; 7 6 ··· 17 16 ] as const; 18 17 19 18 /** 19 + * Sanitizes namespace identifiers so they are valid TypeScript identifiers of a certain form. 20 + * 21 + * 1: Remove any leading characters that are illegal as starting character of a typescript identifier. 22 + * 2: Replace illegal characters in remaining part of type name with hyphen (-). 23 + * 24 + * Step 1 should perhaps instead also replace illegal characters with underscore, or prefix with it, like sanitizeEnumName 25 + * does. The way this is now one could perhaps end up removing all characters, if all are illegal start characters. It 26 + * would be sort of a breaking change to do so, though, previously generated code might change then. 27 + * 28 + * JavaScript identifier regexp pattern retrieved from https://developer.mozilla.org/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers 29 + * 30 + * The output of this is expected to be converted to PascalCase 31 + * 32 + * @deprecated 33 + */ 34 + export const sanitizeNamespaceIdentifier = (name: string) => 35 + name 36 + .replace(/^[^\p{ID_Start}]+/u, '') 37 + .replace(/[^$\u200c\u200d\p{ID_Continue}]/gu, '-') 38 + .replace(/[$+]/g, '-'); 39 + 40 + /** 20 41 * Returns an operation ID to use across the application. By default, we try 21 42 * to use the provided ID. If it's not provided or the SDK is configured 22 43 * to exclude it, we generate operation ID from its location. 44 + * 45 + * @deprecated 23 46 */ 24 47 export const operationToId = ({ 25 48 context, ··· 47 70 if ( 48 71 id && 49 72 (!context.config.plugins['@hey-api/sdk'] || 50 - context.config.plugins['@hey-api/sdk'].config.operationId) 73 + // TODO: needs to be refactored... 74 + (context.config.plugins['@hey-api/sdk'].config.operations && 75 + typeof context.config.plugins['@hey-api/sdk'].config.operations !== 76 + 'function' && 77 + typeof context.config.plugins['@hey-api/sdk'].config.operations === 78 + 'object' && 79 + context.config.plugins['@hey-api/sdk'].config.operations.nesting === 80 + 'operationId')) 51 81 ) { 52 - result = stringCase({ 53 - case: targetCase, 54 - value: sanitizeNamespaceIdentifier(id), 55 - }); 82 + result = toCase(sanitizeNamespaceIdentifier(id), targetCase); 56 83 } else { 57 84 const pathWithoutPlaceholders = path 58 85 .replace(/{(.*?)}/g, 'by-$1') 59 86 // replace slashes with hyphens for camelcase method at the end 60 87 .replace(/[/:+]/g, '-'); 61 88 62 - result = stringCase({ 63 - case: targetCase, 64 - value: `${method}-${pathWithoutPlaceholders}`, 65 - }); 89 + result = toCase(`${method}-${pathWithoutPlaceholders}`, targetCase); 66 90 } 67 91 68 92 if (count > 1) {
+4 -35
packages/openapi-ts/src/plugins/@angular/common/config.ts
··· 1 1 import { definePluginConfig } from '~/plugins/shared/utils/config'; 2 2 3 + import { resolveHttpRequests } from './httpRequests'; 4 + import { resolveHttpResources } from './httpResources'; 3 5 import { handler } from './plugin'; 4 6 import type { AngularCommonPlugin } from './types'; 5 7 ··· 11 13 handler, 12 14 name: '@angular/common', 13 15 resolveConfig: (plugin, context) => { 14 - plugin.config.httpRequests = context.valueToObject({ 15 - defaultValue: { 16 - asClass: false, 17 - classNameBuilder: '{{name}}Requests', 18 - enabled: true, 19 - }, 20 - mappers: { 21 - boolean: (enabled) => ({ enabled }), 22 - }, 23 - value: plugin.config.httpRequests, 24 - }); 25 - 26 - if (!plugin.config.httpRequests.methodNameBuilder) { 27 - const { asClass } = plugin.config.httpRequests; 28 - plugin.config.httpRequests.methodNameBuilder = (operation) => 29 - asClass ? String(operation.id) : `${String(operation.id)}Request`; 30 - } 31 - 32 - plugin.config.httpResources = context.valueToObject({ 33 - defaultValue: { 34 - asClass: false, 35 - classNameBuilder: '{{name}}Resources', 36 - enabled: true, 37 - }, 38 - mappers: { 39 - boolean: (enabled) => ({ enabled }), 40 - }, 41 - value: plugin.config.httpResources, 42 - }); 43 - 44 - if (!plugin.config.httpResources.methodNameBuilder) { 45 - const { asClass } = plugin.config.httpResources; 46 - plugin.config.httpResources.methodNameBuilder = (operation) => 47 - asClass ? String(operation.id) : `${String(operation.id)}Resource`; 48 - } 16 + plugin.config.httpRequests = resolveHttpRequests(plugin.config, context); 17 + plugin.config.httpResources = resolveHttpResources(plugin.config, context); 49 18 }, 50 19 }; 51 20
-344
packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts
··· 1 - import type { Symbol } from '@hey-api/codegen-core'; 2 - 3 - import type { IR } from '~/ir/types'; 4 - import { buildName } from '~/openApi/shared/utils/name'; 5 - import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 6 - import { 7 - createOperationComment, 8 - isOperationOptionsRequired, 9 - } from '~/plugins/shared/utils/operation'; 10 - import { $ } from '~/ts-dsl'; 11 - import { stringCase } from '~/utils/stringCase'; 12 - 13 - import type { AngularCommonPlugin } from './types'; 14 - 15 - interface AngularRequestClassEntry { 16 - className: string; 17 - classes: Set<string>; 18 - methods: Set<string>; 19 - nodes: Array<ReturnType<typeof $.method | typeof $.field | typeof $.newline>>; 20 - root: boolean; 21 - } 22 - 23 - const generateAngularClassRequests = ({ 24 - plugin, 25 - }: { 26 - plugin: AngularCommonPlugin['Instance']; 27 - }) => { 28 - const requestClasses = new Map<string, AngularRequestClassEntry>(); 29 - const generatedClasses = new Set<string>(); 30 - 31 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 32 - 33 - plugin.forEach( 34 - 'operation', 35 - ({ operation }) => { 36 - const isRequiredOptions = isOperationOptionsRequired({ 37 - context: plugin.context, 38 - operation, 39 - }); 40 - 41 - const classes = operationClasses({ operation, plugin: sdkPlugin }); 42 - 43 - for (const entry of classes.values()) { 44 - entry.path.forEach((currentClassName, index) => { 45 - if (!requestClasses.has(currentClassName)) { 46 - requestClasses.set(currentClassName, { 47 - className: currentClassName, 48 - classes: new Set(), 49 - methods: new Set(), 50 - nodes: [], 51 - root: !index, 52 - }); 53 - } 54 - 55 - const parentClassName = entry.path[index - 1]; 56 - if (parentClassName && parentClassName !== currentClassName) { 57 - const parentClass = requestClasses.get(parentClassName)!; 58 - parentClass.classes.add(currentClassName); 59 - requestClasses.set(parentClassName, parentClass); 60 - } 61 - 62 - const isLast = entry.path.length === index + 1; 63 - if (!isLast) { 64 - return; 65 - } 66 - 67 - const currentClass = requestClasses.get(currentClassName)!; 68 - 69 - const requestMethodName = 70 - plugin.config.httpRequests.methodNameBuilder(operation); 71 - 72 - if (currentClass.methods.has(requestMethodName)) { 73 - return; 74 - } 75 - 76 - const methodNode = generateAngularRequestMethod({ 77 - isRequiredOptions, 78 - methodName: requestMethodName, 79 - operation, 80 - plugin, 81 - }); 82 - 83 - if (!currentClass.nodes.length) { 84 - currentClass.nodes.push(methodNode); 85 - } else { 86 - currentClass.nodes.push($.newline(), methodNode); 87 - } 88 - 89 - currentClass.methods.add(requestMethodName); 90 - requestClasses.set(currentClassName, currentClass); 91 - }); 92 - } 93 - }, 94 - { 95 - order: 'declarations', 96 - }, 97 - ); 98 - 99 - const generateClass = (currentClass: AngularRequestClassEntry) => { 100 - if (generatedClasses.has(currentClass.className)) { 101 - return; 102 - } 103 - 104 - if (currentClass.classes.size) { 105 - for (const childClassName of currentClass.classes) { 106 - const childClass = requestClasses.get(childClassName)!; 107 - generateClass(childClass); 108 - 109 - currentClass.nodes.push( 110 - $.field( 111 - stringCase({ case: 'camelCase', value: childClass.className }), 112 - ).assign( 113 - $.new( 114 - buildName({ 115 - config: { 116 - case: 'preserve', 117 - name: plugin.config.httpRequests.classNameBuilder, 118 - }, 119 - name: childClass.className, 120 - }), 121 - ), 122 - ), 123 - ); 124 - } 125 - } 126 - 127 - const symbolInjectable = plugin.referenceSymbol({ 128 - category: 'external', 129 - resource: '@angular/core.Injectable', 130 - }); 131 - const symbolClass = plugin.registerSymbol({ 132 - meta: { 133 - category: 'utility', 134 - resource: 'class', 135 - resourceId: currentClass.className, 136 - tool: 'angular', 137 - }, 138 - name: buildName({ 139 - config: { 140 - case: 'preserve', 141 - name: plugin.config.httpRequests.classNameBuilder, 142 - }, 143 - name: currentClass.className, 144 - }), 145 - }); 146 - const node = $.class(symbolClass) 147 - .export() 148 - .$if(currentClass.root, (c) => 149 - c.decorator( 150 - symbolInjectable, 151 - $.object().prop('providedIn', $.literal('root')), 152 - ), 153 - ) 154 - .do(...currentClass.nodes); 155 - plugin.node(node); 156 - 157 - generatedClasses.add(currentClass.className); 158 - }; 159 - 160 - for (const requestClass of requestClasses.values()) { 161 - generateClass(requestClass); 162 - } 163 - }; 164 - 165 - const generateAngularFunctionRequests = ({ 166 - plugin, 167 - }: { 168 - plugin: AngularCommonPlugin['Instance']; 169 - }) => { 170 - plugin.forEach( 171 - 'operation', 172 - ({ operation }) => { 173 - const isRequiredOptions = isOperationOptionsRequired({ 174 - context: plugin.context, 175 - operation, 176 - }); 177 - 178 - const symbol = plugin.registerSymbol({ 179 - meta: { 180 - category: 'utility', 181 - resource: 'operation', 182 - resourceId: operation.id, 183 - role: 'data', 184 - tool: 'angular', 185 - }, 186 - name: plugin.config.httpRequests.methodNameBuilder(operation), 187 - }); 188 - const node = generateAngularRequestFunction({ 189 - isRequiredOptions, 190 - operation, 191 - plugin, 192 - symbol, 193 - }); 194 - plugin.node(node); 195 - }, 196 - { 197 - order: 'declarations', 198 - }, 199 - ); 200 - }; 201 - 202 - const generateRequestCallExpression = ({ 203 - operation, 204 - plugin, 205 - }: { 206 - operation: IR.OperationObject; 207 - plugin: AngularCommonPlugin['Instance']; 208 - }) => { 209 - const symbolClient = plugin.getSymbol({ 210 - category: 'client', 211 - }); 212 - 213 - const optionsClient = $('options') 214 - .attr('client') 215 - .optional() 216 - .$if(symbolClient, (c, s) => c.coalesce(s)); 217 - 218 - return optionsClient 219 - .attr('requestOptions') 220 - .call( 221 - $.object() 222 - .prop('responseStyle', $.literal('data')) 223 - .prop('method', $.literal(operation.method.toUpperCase())) 224 - .prop('url', $.literal(operation.path)) 225 - .spread('options'), 226 - ); 227 - }; 228 - 229 - const generateAngularRequestMethod = ({ 230 - isRequiredOptions, 231 - methodName, 232 - operation, 233 - plugin, 234 - }: { 235 - isRequiredOptions: boolean; 236 - methodName: string; 237 - operation: IR.OperationObject; 238 - plugin: AngularCommonPlugin['Instance']; 239 - }) => { 240 - const symbolHttpRequest = plugin.referenceSymbol({ 241 - category: 'external', 242 - resource: '@angular/common/http.HttpRequest', 243 - }); 244 - 245 - const symbolOptions = plugin.referenceSymbol({ 246 - category: 'type', 247 - resource: 'client-options', 248 - tool: 'sdk', 249 - }); 250 - 251 - const symbolDataType = plugin.querySymbol({ 252 - category: 'type', 253 - resource: 'operation', 254 - resourceId: operation.id, 255 - role: 'data', 256 - tool: 'typescript', 257 - }); 258 - 259 - return $.method(methodName) 260 - .public() 261 - .$if(createOperationComment(operation), (c, v) => c.doc(v)) 262 - .param('options', (p) => 263 - p.required(isRequiredOptions).type( 264 - $.type(symbolOptions) 265 - .generic(symbolDataType ?? 'unknown') 266 - .generic('ThrowOnError'), 267 - ), 268 - ) 269 - .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 270 - .returns($.type(symbolHttpRequest).generic('unknown')) 271 - .do( 272 - $.return( 273 - generateRequestCallExpression({ 274 - operation, 275 - plugin, 276 - }), 277 - ), 278 - ); 279 - }; 280 - 281 - const generateAngularRequestFunction = ({ 282 - isRequiredOptions, 283 - operation, 284 - plugin, 285 - symbol, 286 - }: { 287 - isRequiredOptions: boolean; 288 - operation: IR.OperationObject; 289 - plugin: AngularCommonPlugin['Instance']; 290 - symbol: Symbol; 291 - }) => { 292 - const symbolHttpRequest = plugin.referenceSymbol({ 293 - category: 'external', 294 - resource: '@angular/common/http.HttpRequest', 295 - }); 296 - 297 - const symbolOptions = plugin.referenceSymbol({ 298 - category: 'type', 299 - resource: 'client-options', 300 - tool: 'sdk', 301 - }); 302 - 303 - const symbolDataType = plugin.querySymbol({ 304 - category: 'type', 305 - resource: 'operation', 306 - resourceId: operation.id, 307 - role: 'data', 308 - tool: 'typescript', 309 - }); 310 - 311 - return $.const(symbol) 312 - .export() 313 - .$if(createOperationComment(operation), (c, v) => c.doc(v)) 314 - .assign( 315 - $.func() 316 - .param('options', (p) => 317 - p.required(isRequiredOptions).type( 318 - $.type(symbolOptions) 319 - .generic(symbolDataType ?? 'unknown') 320 - .generic('ThrowOnError'), 321 - ), 322 - ) 323 - .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 324 - .returns($.type(symbolHttpRequest).generic('unknown')) 325 - .do( 326 - $.return( 327 - generateRequestCallExpression({ 328 - operation, 329 - plugin, 330 - }), 331 - ), 332 - ), 333 - ); 334 - }; 335 - 336 - export const createHttpRequests: AngularCommonPlugin['Handler'] = ({ 337 - plugin, 338 - }) => { 339 - if (plugin.config.httpRequests.asClass) { 340 - generateAngularClassRequests({ plugin }); 341 - } else { 342 - generateAngularFunctionRequests({ plugin }); 343 - } 344 - };
+68
packages/openapi-ts/src/plugins/@angular/common/httpRequests/config.ts
··· 1 + import type { PluginContext } from '~/plugins/types'; 2 + 3 + import type { UserConfig } from '../types'; 4 + import type { HttpRequestsConfig, UserHttpRequestsConfig } from './types'; 5 + 6 + type Config = Omit<UserConfig, 'name'>; 7 + 8 + export function resolveHttpRequests( 9 + config: Config, 10 + context: PluginContext, 11 + ): HttpRequestsConfig { 12 + let input = config.httpRequests; 13 + if (typeof input === 'string' || typeof input === 'function') { 14 + input = { strategy: input }; 15 + } else if (typeof input === 'boolean' || !input) { 16 + input = { enabled: Boolean(input) }; 17 + } 18 + 19 + const strategy = input.strategy ?? 'flat'; 20 + 21 + return context.valueToObject({ 22 + defaultValue: { 23 + container: 'class', 24 + enabled: true, 25 + methods: 'instance', 26 + nesting: 'operationId', 27 + nestingDelimiters: /[./]/, 28 + strategy, 29 + strategyDefaultTag: 'default', 30 + }, 31 + mappers: { 32 + object(value) { 33 + value.containerName = context.valueToObject({ 34 + defaultValue: 35 + strategy === 'single' 36 + ? { casing: 'PascalCase', name: 'HttpRequests' } 37 + : { casing: 'PascalCase' }, 38 + mappers: { 39 + function: (name) => ({ name }), 40 + string: (name) => ({ name }), 41 + }, 42 + value: value.containerName, 43 + }); 44 + value.methodName = context.valueToObject({ 45 + defaultValue: 46 + strategy === 'flat' 47 + ? { casing: 'camelCase', name: '{{name}}Request' } 48 + : { casing: 'camelCase' }, 49 + mappers: { 50 + function: (name) => ({ name }), 51 + string: (name) => ({ name }), 52 + }, 53 + value: value.methodName, 54 + }); 55 + value.segmentName = context.valueToObject({ 56 + defaultValue: { casing: 'PascalCase', name: '{{name}}Requests' }, 57 + mappers: { 58 + function: (name) => ({ name }), 59 + string: (name) => ({ name }), 60 + }, 61 + value: value.segmentName, 62 + }); 63 + return value; 64 + }, 65 + }, 66 + value: input as UserHttpRequestsConfig, 67 + }) as HttpRequestsConfig; 68 + }
+3
packages/openapi-ts/src/plugins/@angular/common/httpRequests/index.ts
··· 1 + export { resolveHttpRequests } from './config'; 2 + export { resolveHttpRequestsStrategy } from './resolve'; 3 + export type { HttpRequestsConfig, UserHttpRequestsConfig } from './types';
+51
packages/openapi-ts/src/plugins/@angular/common/httpRequests/resolve.ts
··· 1 + import type { 2 + OperationPathStrategy, 3 + OperationStructureStrategy, 4 + } from '~/openApi/shared/locations'; 5 + import { OperationPath, OperationStrategy } from '~/openApi/shared/locations'; 6 + 7 + import type { AngularCommonPlugin } from '../types'; 8 + 9 + function resolvePath( 10 + plugin: AngularCommonPlugin['Instance'], 11 + ): OperationPathStrategy { 12 + if (plugin.config.httpRequests.nesting === 'id') { 13 + return OperationPath.id(); 14 + } 15 + 16 + if (plugin.config.httpRequests.nesting === 'operationId') { 17 + return OperationPath.fromOperationId({ 18 + delimiters: plugin.config.httpRequests.nestingDelimiters, 19 + fallback: OperationPath.id(), 20 + }); 21 + } 22 + 23 + return plugin.config.httpRequests.nesting; 24 + } 25 + 26 + export function resolveHttpRequestsStrategy( 27 + plugin: AngularCommonPlugin['Instance'], 28 + ): OperationStructureStrategy { 29 + if (plugin.config.httpRequests.strategy === 'flat') { 30 + return OperationStrategy.flat({ 31 + path: (operation) => [resolvePath(plugin)(operation).join('.')], 32 + }); 33 + } 34 + 35 + if (plugin.config.httpRequests.strategy === 'single') { 36 + const root = plugin.config.httpRequests.containerName; 37 + return OperationStrategy.single({ 38 + path: resolvePath(plugin), 39 + root: typeof root.name === 'string' ? root.name : (root.name?.('') ?? ''), 40 + }); 41 + } 42 + 43 + if (plugin.config.httpRequests.strategy === 'byTags') { 44 + return OperationStrategy.byTags({ 45 + fallback: plugin.config.httpRequests.strategyDefaultTag, 46 + path: resolvePath(plugin), 47 + }); 48 + } 49 + 50 + return plugin.config.httpRequests.strategy; 51 + }
+189
packages/openapi-ts/src/plugins/@angular/common/httpRequests/types.d.ts
··· 1 + import type { 2 + OperationPathStrategy, 3 + OperationsStrategy, 4 + } from '~/openApi/shared/locations'; 5 + import type { NamingConfig, NamingRule } from '~/utils/naming'; 6 + 7 + export interface UserHttpRequestsConfig { 8 + /** 9 + * Type of container for grouped operations. 10 + * 11 + * Ignored when `strategy` is `'flat'`. 12 + * 13 + * - `'class'` - Class with methods 14 + * 15 + * @default 'class' 16 + */ 17 + container?: 'class'; 18 + /** 19 + * Customize container names. 20 + * 21 + * For `'single'` strategy, this sets the root container name. 22 + * For `'byTags'` strategy, this transforms tag names. 23 + * 24 + * @default 'Sdk' for `'single'` strategy 25 + * 26 + * @example 27 + * // Set root name for single strategy 28 + * containerName: 'MyApi' 29 + * 30 + * @example 31 + * // Transform tag names with suffix 32 + * containerName: '{{name}}Service' 33 + * 34 + * @example 35 + * // With casing 36 + * containerName: { name: '{{name}}Service', case: 'PascalCase' } 37 + */ 38 + containerName?: NamingRule; 39 + /** 40 + * Whether or not to create HTTP Request instances. 41 + * 42 + * @default true 43 + */ 44 + enabled?: boolean; 45 + /** 46 + * Customize method/function names. 47 + * 48 + * Applied to the final segment of the path (the method name). 49 + */ 50 + methodName?: NamingRule; 51 + /** 52 + * How methods are attached to class containers. 53 + * 54 + * Only applies when `container` is `'class'`. 55 + * 56 + * - `'instance'` - Instance methods, requires `new ClassName(config)` 57 + * 58 + * @default 'instance' 59 + */ 60 + methods?: 'instance'; 61 + /** 62 + * How to derive nesting structure from operations. 63 + * 64 + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `Users.list()`) 65 + * - `'id'` - Use operation id as-is, no nesting 66 + * - Custom function for full control 67 + * 68 + * @default 'operationId' 69 + */ 70 + nesting?: 'operationId' | 'id' | OperationPathStrategy; 71 + /** 72 + * Delimiters for splitting operationId. 73 + * 74 + * Only applies when `nesting` is `'operationId'`. 75 + * 76 + * @default /[./]/ 77 + */ 78 + nestingDelimiters?: RegExp; 79 + /** 80 + * Customize nesting segment names. 81 + * 82 + * Applied to intermediate path segments (not the method name). 83 + */ 84 + segmentName?: NamingRule; 85 + /** 86 + * Grouping strategy. 87 + * 88 + * - `'flat'` - Standalone functions, no grouping 89 + * - `'byTags'` - One container per operation tag 90 + * - `'single'` - All operations in one container 91 + * - Custom function for full control 92 + * 93 + * @default 'flat' 94 + */ 95 + strategy?: OperationsStrategy; 96 + /** 97 + * Default container name for operations without tags. 98 + * 99 + * Only applies when `strategy` is `'byTags'`. 100 + * 101 + * @default 'default' 102 + */ 103 + strategyDefaultTag?: string; 104 + } 105 + 106 + export interface HttpRequestsConfig { 107 + /** 108 + * Type of container for grouped operations. 109 + * 110 + * Ignored when `strategy` is `'flat'`. 111 + * 112 + * - `'class'` - Class with methods 113 + */ 114 + container: 'class'; 115 + /** 116 + * Customize container names. 117 + * 118 + * For `'single'` strategy, this sets the root container name. 119 + * For `'byTags'` strategy, this transforms tag names. 120 + * 121 + * @default 'Sdk' for `'single'` strategy 122 + * 123 + * @example 124 + * // Set root name for single strategy 125 + * containerName: 'MyApi' 126 + * 127 + * @example 128 + * // Transform tag names with suffix 129 + * containerName: '{{name}}Service' 130 + * 131 + * @example 132 + * // With casing 133 + * containerName: { name: '{{name}}Service', case: 'PascalCase' } 134 + */ 135 + containerName: NamingConfig; 136 + /** 137 + * Whether or not to create HTTP Request instances. 138 + */ 139 + enabled: boolean; 140 + /** 141 + * Customize method/function names. 142 + * 143 + * Applied to the final segment of the path (the method name). 144 + */ 145 + methodName: NamingConfig; 146 + /** 147 + * How methods are attached to class containers. 148 + * 149 + * Only applies when `container` is `'class'`. 150 + * 151 + * - `'instance'` - Instance methods, requires `new ClassName(config)` 152 + */ 153 + methods: 'instance'; 154 + /** 155 + * How to derive nesting structure from operations. 156 + * 157 + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `Users.list()`) 158 + * - `'id'` - Use operation id as-is, no nesting 159 + * - Custom function for full control 160 + */ 161 + nesting: 'operationId' | 'id' | OperationPathStrategy; 162 + /** 163 + * Delimiters for splitting operationId. 164 + * 165 + * Only applies when `nesting` is `'operationId'`. 166 + */ 167 + nestingDelimiters: RegExp; 168 + /** 169 + * Customize nesting segment names. 170 + * 171 + * Applied to intermediate path segments (not the method name). 172 + */ 173 + segmentName: NamingConfig; 174 + /** 175 + * Grouping strategy. 176 + * 177 + * - `'flat'` - Standalone functions, no grouping 178 + * - `'byTags'` - One container per operation tag 179 + * - `'single'` - All operations in one container 180 + * - Custom function for full control 181 + */ 182 + strategy: OperationsStrategy; 183 + /** 184 + * Default container name for operations without tags. 185 + * 186 + * Only applies when `strategy` is `'byTags'`. 187 + */ 188 + strategyDefaultTag: string; 189 + }
-418
packages/openapi-ts/src/plugins/@angular/common/httpResources.ts
··· 1 - import type { Symbol } from '@hey-api/codegen-core'; 2 - 3 - import type { IR } from '~/ir/types'; 4 - import { buildName } from '~/openApi/shared/utils/name'; 5 - import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 6 - import { 7 - createOperationComment, 8 - isOperationOptionsRequired, 9 - } from '~/plugins/shared/utils/operation'; 10 - import { $ } from '~/ts-dsl'; 11 - import { stringCase } from '~/utils/stringCase'; 12 - 13 - import type { AngularCommonPlugin } from './types'; 14 - 15 - interface AngularServiceClassEntry { 16 - className: string; 17 - classes: Set<string>; 18 - methods: Set<string>; 19 - nodes: Array<ReturnType<typeof $.method | typeof $.field | typeof $.newline>>; 20 - root: boolean; 21 - } 22 - 23 - const generateAngularClassServices = ({ 24 - plugin, 25 - }: { 26 - plugin: AngularCommonPlugin['Instance']; 27 - }) => { 28 - const serviceClasses = new Map<string, AngularServiceClassEntry>(); 29 - const generatedClasses = new Set<string>(); 30 - 31 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 32 - 33 - plugin.forEach( 34 - 'operation', 35 - ({ operation }) => { 36 - const isRequiredOptions = isOperationOptionsRequired({ 37 - context: plugin.context, 38 - operation, 39 - }); 40 - 41 - const classes = operationClasses({ operation, plugin: sdkPlugin }); 42 - 43 - for (const entry of classes.values()) { 44 - entry.path.forEach((currentClassName, index) => { 45 - if (!serviceClasses.has(currentClassName)) { 46 - serviceClasses.set(currentClassName, { 47 - className: currentClassName, 48 - classes: new Set(), 49 - methods: new Set(), 50 - nodes: [], 51 - root: !index, 52 - }); 53 - } 54 - 55 - const parentClassName = entry.path[index - 1]; 56 - if (parentClassName && parentClassName !== currentClassName) { 57 - const parentClass = serviceClasses.get(parentClassName)!; 58 - parentClass.classes.add(currentClassName); 59 - serviceClasses.set(parentClassName, parentClass); 60 - } 61 - 62 - const isLast = entry.path.length === index + 1; 63 - if (!isLast) { 64 - return; 65 - } 66 - 67 - const currentClass = serviceClasses.get(currentClassName)!; 68 - 69 - const resourceMethodName = 70 - plugin.config.httpResources.methodNameBuilder(operation); 71 - 72 - if (currentClass.methods.has(resourceMethodName)) { 73 - return; 74 - } 75 - 76 - const methodNode = generateAngularResourceMethod({ 77 - isRequiredOptions, 78 - methodName: resourceMethodName, 79 - operation, 80 - plugin, 81 - }); 82 - 83 - if (!currentClass.nodes.length) { 84 - currentClass.nodes.push(methodNode); 85 - } else { 86 - currentClass.nodes.push($.newline(), methodNode); 87 - } 88 - 89 - currentClass.methods.add(resourceMethodName); 90 - serviceClasses.set(currentClassName, currentClass); 91 - }); 92 - } 93 - }, 94 - { 95 - order: 'declarations', 96 - }, 97 - ); 98 - 99 - const generateClass = (currentClass: AngularServiceClassEntry) => { 100 - if (generatedClasses.has(currentClass.className)) { 101 - return; 102 - } 103 - 104 - if (currentClass.classes.size) { 105 - for (const childClassName of currentClass.classes) { 106 - const childClass = serviceClasses.get(childClassName)!; 107 - generateClass(childClass); 108 - 109 - currentClass.nodes.push( 110 - $.field( 111 - stringCase({ 112 - case: 'camelCase', 113 - value: childClass.className, 114 - }), 115 - ).assign( 116 - $.new( 117 - buildName({ 118 - config: { 119 - case: 'preserve', 120 - name: plugin.config.httpResources.classNameBuilder, 121 - }, 122 - name: childClass.className, 123 - }), 124 - ), 125 - ), 126 - ); 127 - } 128 - } 129 - 130 - const symbolInjectable = plugin.referenceSymbol({ 131 - category: 'external', 132 - resource: '@angular/core.Injectable', 133 - }); 134 - const symbolClass = plugin.registerSymbol({ 135 - name: buildName({ 136 - config: { 137 - case: 'preserve', 138 - name: plugin.config.httpResources.classNameBuilder, 139 - }, 140 - name: currentClass.className, 141 - }), 142 - }); 143 - const node = $.class(symbolClass) 144 - .export() 145 - .$if(currentClass.root, (c) => 146 - c.decorator( 147 - symbolInjectable, 148 - $.object().prop('providedIn', $.literal('root')), 149 - ), 150 - ) 151 - .do(...currentClass.nodes); 152 - plugin.node(node); 153 - 154 - generatedClasses.add(currentClass.className); 155 - }; 156 - 157 - for (const serviceClass of serviceClasses.values()) { 158 - generateClass(serviceClass); 159 - } 160 - }; 161 - 162 - const generateAngularFunctionServices = ({ 163 - plugin, 164 - }: { 165 - plugin: AngularCommonPlugin['Instance']; 166 - }) => { 167 - plugin.forEach( 168 - 'operation', 169 - ({ operation }) => { 170 - const isRequiredOptions = isOperationOptionsRequired({ 171 - context: plugin.context, 172 - operation, 173 - }); 174 - 175 - const symbol = plugin.registerSymbol({ 176 - name: plugin.config.httpResources.methodNameBuilder(operation), 177 - }); 178 - const node = generateAngularResourceFunction({ 179 - isRequiredOptions, 180 - operation, 181 - plugin, 182 - symbol, 183 - }); 184 - plugin.node(node); 185 - }, 186 - { 187 - order: 'declarations', 188 - }, 189 - ); 190 - }; 191 - 192 - const generateResourceCallExpression = ({ 193 - operation, 194 - plugin, 195 - }: { 196 - operation: IR.OperationObject; 197 - plugin: AngularCommonPlugin['Instance']; 198 - }) => { 199 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 200 - 201 - const symbolHttpResource = plugin.referenceSymbol({ 202 - category: 'external', 203 - resource: '@angular/common/http.httpResource', 204 - }); 205 - 206 - const symbolResponseType = plugin.querySymbol({ 207 - category: 'type', 208 - resource: 'operation', 209 - resourceId: operation.id, 210 - role: 'response', 211 - }); 212 - 213 - if (plugin.config.httpRequests.asClass) { 214 - // For class-based request methods, use inject and class hierarchy 215 - const classes = operationClasses({ operation, plugin: sdkPlugin }); 216 - 217 - const firstEntry = Array.from(classes.values())[0]; 218 - if (firstEntry) { 219 - // Import the root class from HTTP requests 220 - const rootClassName = firstEntry.path[0]!; 221 - const symbolClass = plugin.referenceSymbol({ 222 - category: 'utility', 223 - resource: 'class', 224 - resourceId: rootClassName, 225 - tool: 'angular', 226 - }); 227 - 228 - // Build the method access path using inject 229 - const symbolInject = plugin.referenceSymbol({ 230 - category: 'external', 231 - resource: '@angular/core.inject', 232 - }); 233 - let methodAccess: ReturnType<typeof $.attr | typeof $.call> = 234 - $(symbolInject).call(symbolClass); 235 - 236 - // Navigate through the class hierarchy 237 - for (let i = 1; i < firstEntry.path.length; i++) { 238 - const className = firstEntry.path[i]; 239 - if (className) { 240 - methodAccess = methodAccess.attr( 241 - stringCase({ 242 - case: 'camelCase', 243 - value: className, 244 - }), 245 - ); 246 - } 247 - } 248 - 249 - methodAccess = methodAccess.attr( 250 - plugin.config.httpRequests.methodNameBuilder(operation), 251 - ); 252 - 253 - return $(symbolHttpResource) 254 - .call( 255 - $.func().do( 256 - $.const('opts').assign( 257 - $.ternary('options') 258 - .do($('options').call()) 259 - .otherwise($.id('undefined')), 260 - ), 261 - $.return( 262 - $.ternary('opts') 263 - .do(methodAccess.call('opts')) 264 - .otherwise($.id('undefined')), 265 - ), 266 - ), 267 - ) 268 - .generic(symbolResponseType ?? 'unknown'); 269 - } 270 - } else { 271 - const symbolHttpRequest = plugin.referenceSymbol({ 272 - category: 'utility', 273 - resource: 'operation', 274 - resourceId: operation.id, 275 - role: 'data', 276 - tool: 'angular', 277 - }); 278 - 279 - return $(symbolHttpResource) 280 - .call( 281 - $.func().do( 282 - $.const('opts').assign( 283 - $.ternary('options') 284 - .do($('options').call()) 285 - .otherwise($.id('undefined')), 286 - ), 287 - $.return( 288 - $.ternary('opts') 289 - .do($(symbolHttpRequest).call('opts')) 290 - .otherwise($.id('undefined')), 291 - ), 292 - ), 293 - ) 294 - .generic(symbolResponseType ?? 'unknown'); 295 - } 296 - 297 - // Fallback return (should not reach here) 298 - return $(symbolHttpResource).call( 299 - $.func() 300 - .do($.return($.id('undefined'))) 301 - .generic(symbolResponseType ?? 'unknown'), 302 - ); 303 - }; 304 - 305 - const generateAngularResourceMethod = ({ 306 - isRequiredOptions, 307 - methodName, 308 - operation, 309 - plugin, 310 - }: { 311 - isRequiredOptions: boolean; 312 - methodName: string; 313 - operation: IR.OperationObject; 314 - plugin: AngularCommonPlugin['Instance']; 315 - }) => { 316 - const symbolOptions = plugin.referenceSymbol({ 317 - category: 'type', 318 - resource: 'client-options', 319 - tool: 'sdk', 320 - }); 321 - 322 - const symbolDataType = plugin.querySymbol({ 323 - category: 'type', 324 - resource: 'operation', 325 - resourceId: operation.id, 326 - role: 'data', 327 - tool: 'typescript', 328 - }); 329 - 330 - return $.method(methodName) 331 - .public() 332 - .$if(createOperationComment(operation), (c, v) => c.doc(v)) 333 - .param('options', (p) => 334 - p.required(isRequiredOptions).type( 335 - $.type.func().returns( 336 - $.type.or( 337 - $.type(symbolOptions) 338 - .generic(symbolDataType ?? 'unknown') 339 - .generic('ThrowOnError'), 340 - $.type('undefined'), 341 - ), 342 - ), 343 - ), 344 - ) 345 - .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 346 - .do( 347 - $.return( 348 - generateResourceCallExpression({ 349 - operation, 350 - plugin, 351 - }), 352 - ), 353 - ); 354 - }; 355 - 356 - const generateAngularResourceFunction = ({ 357 - isRequiredOptions, 358 - operation, 359 - plugin, 360 - symbol, 361 - }: { 362 - isRequiredOptions: boolean; 363 - operation: IR.OperationObject; 364 - plugin: AngularCommonPlugin['Instance']; 365 - symbol: Symbol; 366 - }) => { 367 - const symbolOptions = plugin.referenceSymbol({ 368 - category: 'type', 369 - resource: 'client-options', 370 - tool: 'sdk', 371 - }); 372 - 373 - const symbolDataType = plugin.querySymbol({ 374 - category: 'type', 375 - resource: 'operation', 376 - resourceId: operation.id, 377 - role: 'data', 378 - tool: 'typescript', 379 - }); 380 - 381 - return $.const(symbol) 382 - .export() 383 - .$if(createOperationComment(operation), (c, v) => c.doc(v)) 384 - .assign( 385 - $.func() 386 - .param('options', (p) => 387 - p.required(isRequiredOptions).type( 388 - $.type.func().returns( 389 - $.type.or( 390 - $.type(symbolOptions) 391 - .generic(symbolDataType ?? 'unknown') 392 - .generic('ThrowOnError'), 393 - $.type('undefined'), 394 - ), 395 - ), 396 - ), 397 - ) 398 - .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 399 - .do( 400 - $.return( 401 - generateResourceCallExpression({ 402 - operation, 403 - plugin, 404 - }), 405 - ), 406 - ), 407 - ); 408 - }; 409 - 410 - export const createHttpResources: AngularCommonPlugin['Handler'] = ({ 411 - plugin, 412 - }) => { 413 - if (plugin.config.httpResources.asClass) { 414 - generateAngularClassServices({ plugin }); 415 - } else { 416 - generateAngularFunctionServices({ plugin }); 417 - } 418 - };
+68
packages/openapi-ts/src/plugins/@angular/common/httpResources/config.ts
··· 1 + import type { PluginContext } from '~/plugins/types'; 2 + 3 + import type { UserConfig } from '../types'; 4 + import type { HttpResourcesConfig, UserHttpResourcesConfig } from './types'; 5 + 6 + type Config = Omit<UserConfig, 'name'>; 7 + 8 + export function resolveHttpResources( 9 + config: Config, 10 + context: PluginContext, 11 + ): HttpResourcesConfig { 12 + let input = config.httpResources; 13 + if (typeof input === 'string' || typeof input === 'function') { 14 + input = { strategy: input }; 15 + } else if (typeof input === 'boolean' || !input) { 16 + input = { enabled: Boolean(input) }; 17 + } 18 + 19 + const strategy = input.strategy ?? 'flat'; 20 + 21 + return context.valueToObject({ 22 + defaultValue: { 23 + container: 'class', 24 + enabled: true, 25 + methods: 'instance', 26 + nesting: 'operationId', 27 + nestingDelimiters: /[./]/, 28 + strategy, 29 + strategyDefaultTag: 'default', 30 + }, 31 + mappers: { 32 + object(value) { 33 + value.containerName = context.valueToObject({ 34 + defaultValue: 35 + strategy === 'single' 36 + ? { casing: 'PascalCase', name: 'HttpResources' } 37 + : { casing: 'PascalCase' }, 38 + mappers: { 39 + function: (name) => ({ name }), 40 + string: (name) => ({ name }), 41 + }, 42 + value: value.containerName, 43 + }); 44 + value.methodName = context.valueToObject({ 45 + defaultValue: 46 + strategy === 'flat' 47 + ? { casing: 'camelCase', name: '{{name}}Resource' } 48 + : { casing: 'camelCase' }, 49 + mappers: { 50 + function: (name) => ({ name }), 51 + string: (name) => ({ name }), 52 + }, 53 + value: value.methodName, 54 + }); 55 + value.segmentName = context.valueToObject({ 56 + defaultValue: { casing: 'PascalCase', name: '{{name}}Resources' }, 57 + mappers: { 58 + function: (name) => ({ name }), 59 + string: (name) => ({ name }), 60 + }, 61 + value: value.segmentName, 62 + }); 63 + return value; 64 + }, 65 + }, 66 + value: input as UserHttpResourcesConfig, 67 + }) as HttpResourcesConfig; 68 + }
+3
packages/openapi-ts/src/plugins/@angular/common/httpResources/index.ts
··· 1 + export { resolveHttpResources } from './config'; 2 + export { resolveHttpResourcesStrategy } from './resolve'; 3 + export type { HttpResourcesConfig, UserHttpResourcesConfig } from './types';
+51
packages/openapi-ts/src/plugins/@angular/common/httpResources/resolve.ts
··· 1 + import type { 2 + OperationPathStrategy, 3 + OperationStructureStrategy, 4 + } from '~/openApi/shared/locations'; 5 + import { OperationPath, OperationStrategy } from '~/openApi/shared/locations'; 6 + 7 + import type { AngularCommonPlugin } from '../types'; 8 + 9 + function resolvePath( 10 + plugin: AngularCommonPlugin['Instance'], 11 + ): OperationPathStrategy { 12 + if (plugin.config.httpResources.nesting === 'id') { 13 + return OperationPath.id(); 14 + } 15 + 16 + if (plugin.config.httpResources.nesting === 'operationId') { 17 + return OperationPath.fromOperationId({ 18 + delimiters: plugin.config.httpResources.nestingDelimiters, 19 + fallback: OperationPath.id(), 20 + }); 21 + } 22 + 23 + return plugin.config.httpResources.nesting; 24 + } 25 + 26 + export function resolveHttpResourcesStrategy( 27 + plugin: AngularCommonPlugin['Instance'], 28 + ): OperationStructureStrategy { 29 + if (plugin.config.httpResources.strategy === 'flat') { 30 + return OperationStrategy.flat({ 31 + path: (operation) => [resolvePath(plugin)(operation).join('.')], 32 + }); 33 + } 34 + 35 + if (plugin.config.httpResources.strategy === 'single') { 36 + const root = plugin.config.httpResources.containerName; 37 + return OperationStrategy.single({ 38 + path: resolvePath(plugin), 39 + root: typeof root.name === 'string' ? root.name : (root.name?.('') ?? ''), 40 + }); 41 + } 42 + 43 + if (plugin.config.httpResources.strategy === 'byTags') { 44 + return OperationStrategy.byTags({ 45 + fallback: plugin.config.httpResources.strategyDefaultTag, 46 + path: resolvePath(plugin), 47 + }); 48 + } 49 + 50 + return plugin.config.httpResources.strategy; 51 + }
+189
packages/openapi-ts/src/plugins/@angular/common/httpResources/types.d.ts
··· 1 + import type { 2 + OperationPathStrategy, 3 + OperationsStrategy, 4 + } from '~/openApi/shared/locations'; 5 + import type { NamingConfig, NamingRule } from '~/utils/naming'; 6 + 7 + export interface UserHttpResourcesConfig { 8 + /** 9 + * Type of container for grouped operations. 10 + * 11 + * Ignored when `strategy` is `'flat'`. 12 + * 13 + * - `'class'` - Class with methods 14 + * 15 + * @default 'class' 16 + */ 17 + container?: 'class'; 18 + /** 19 + * Customize container names. 20 + * 21 + * For `'single'` strategy, this sets the root container name. 22 + * For `'byTags'` strategy, this transforms tag names. 23 + * 24 + * @default 'Sdk' for `'single'` strategy 25 + * 26 + * @example 27 + * // Set root name for single strategy 28 + * containerName: 'MyApi' 29 + * 30 + * @example 31 + * // Transform tag names with suffix 32 + * containerName: '{{name}}Service' 33 + * 34 + * @example 35 + * // With casing 36 + * containerName: { name: '{{name}}Service', case: 'PascalCase' } 37 + */ 38 + containerName?: NamingRule; 39 + /** 40 + * Whether or not to create HTTP resource APIs. 41 + * 42 + * @default true 43 + */ 44 + enabled?: boolean; 45 + /** 46 + * Customize method/function names. 47 + * 48 + * Applied to the final segment of the path (the method name). 49 + */ 50 + methodName?: NamingRule; 51 + /** 52 + * How methods are attached to class containers. 53 + * 54 + * Only applies when `container` is `'class'`. 55 + * 56 + * - `'instance'` - Instance methods, requires `new ClassName(config)` 57 + * 58 + * @default 'instance' 59 + */ 60 + methods?: 'instance'; 61 + /** 62 + * How to derive nesting structure from operations. 63 + * 64 + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `Users.list()`) 65 + * - `'id'` - Use operation id as-is, no nesting 66 + * - Custom function for full control 67 + * 68 + * @default 'operationId' 69 + */ 70 + nesting?: 'operationId' | 'id' | OperationPathStrategy; 71 + /** 72 + * Delimiters for splitting operationId. 73 + * 74 + * Only applies when `nesting` is `'operationId'`. 75 + * 76 + * @default /[./]/ 77 + */ 78 + nestingDelimiters?: RegExp; 79 + /** 80 + * Customize nesting segment names. 81 + * 82 + * Applied to intermediate path segments (not the method name). 83 + */ 84 + segmentName?: NamingRule; 85 + /** 86 + * Grouping strategy. 87 + * 88 + * - `'flat'` - Standalone functions, no grouping 89 + * - `'byTags'` - One container per operation tag 90 + * - `'single'` - All operations in one container 91 + * - Custom function for full control 92 + * 93 + * @default 'flat' 94 + */ 95 + strategy?: OperationsStrategy; 96 + /** 97 + * Default container name for operations without tags. 98 + * 99 + * Only applies when `strategy` is `'byTags'`. 100 + * 101 + * @default 'default' 102 + */ 103 + strategyDefaultTag?: string; 104 + } 105 + 106 + export interface HttpResourcesConfig { 107 + /** 108 + * Type of container for grouped operations. 109 + * 110 + * Ignored when `strategy` is `'flat'`. 111 + * 112 + * - `'class'` - Class with methods 113 + */ 114 + container: 'class'; 115 + /** 116 + * Customize container names. 117 + * 118 + * For `'single'` strategy, this sets the root container name. 119 + * For `'byTags'` strategy, this transforms tag names. 120 + * 121 + * @default 'Sdk' for `'single'` strategy 122 + * 123 + * @example 124 + * // Set root name for single strategy 125 + * containerName: 'MyApi' 126 + * 127 + * @example 128 + * // Transform tag names with suffix 129 + * containerName: '{{name}}Service' 130 + * 131 + * @example 132 + * // With casing 133 + * containerName: { name: '{{name}}Service', case: 'PascalCase' } 134 + */ 135 + containerName: NamingConfig; 136 + /** 137 + * Whether or not to create HTTP resource APIs. 138 + */ 139 + enabled: boolean; 140 + /** 141 + * Customize method/function names. 142 + * 143 + * Applied to the final segment of the path (the method name). 144 + */ 145 + methodName: NamingConfig; 146 + /** 147 + * How methods are attached to class containers. 148 + * 149 + * Only applies when `container` is `'class'`. 150 + * 151 + * - `'instance'` - Instance methods, requires `new ClassName(config)` 152 + */ 153 + methods: 'instance'; 154 + /** 155 + * How to derive nesting structure from operations. 156 + * 157 + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `Users.list()`) 158 + * - `'id'` - Use operation id as-is, no nesting 159 + * - Custom function for full control 160 + */ 161 + nesting: 'operationId' | 'id' | OperationPathStrategy; 162 + /** 163 + * Delimiters for splitting operationId. 164 + * 165 + * Only applies when `nesting` is `'operationId'`. 166 + */ 167 + nestingDelimiters: RegExp; 168 + /** 169 + * Customize nesting segment names. 170 + * 171 + * Applied to intermediate path segments (not the method name). 172 + */ 173 + segmentName: NamingConfig; 174 + /** 175 + * Grouping strategy. 176 + * 177 + * - `'flat'` - Standalone functions, no grouping 178 + * - `'byTags'` - One container per operation tag 179 + * - `'single'` - All operations in one container 180 + * - Custom function for full control 181 + */ 182 + strategy: OperationsStrategy; 183 + /** 184 + * Default container name for operations without tags. 185 + * 186 + * Only applies when `strategy` is `'byTags'`. 187 + */ 188 + strategyDefaultTag: string; 189 + }
+82 -12
packages/openapi-ts/src/plugins/@angular/common/plugin.ts
··· 1 - import { createHttpRequests } from './httpRequests'; 2 - import { createHttpResources } from './httpResources'; 1 + import { StructureModel } from '@hey-api/codegen-core'; 2 + 3 + import type { $ } from '~/ts-dsl'; 4 + 5 + import { resolveHttpRequestsStrategy } from './httpRequests'; 6 + import { resolveHttpResourcesStrategy } from './httpResources'; 7 + import type { OperationItem } from './shared/node'; 8 + import { 9 + createHttpRequestShell, 10 + createHttpResourceShell, 11 + source, 12 + toHttpRequestNode, 13 + toHttpResourceNode, 14 + } from './shared/node'; 3 15 import type { AngularCommonPlugin } from './types'; 4 16 5 17 export const handler: AngularCommonPlugin['Handler'] = ({ plugin }) => { 6 - plugin.registerSymbol({ 18 + plugin.symbol('HttpRequest', { 7 19 external: '@angular/common/http', 8 20 kind: 'type', 9 21 meta: { 10 22 category: 'external', 11 23 resource: '@angular/common/http.HttpRequest', 12 24 }, 13 - name: 'HttpRequest', 14 25 }); 15 - plugin.registerSymbol({ 26 + plugin.symbol('inject', { 16 27 external: '@angular/core', 17 28 meta: { 18 29 category: 'external', 19 30 resource: '@angular/core.inject', 20 31 }, 21 - name: 'inject', 22 32 }); 23 - plugin.registerSymbol({ 33 + plugin.symbol('Injectable', { 24 34 external: '@angular/core', 25 35 meta: { 26 36 category: 'external', 27 37 resource: '@angular/core.Injectable', 28 38 }, 29 - name: 'Injectable', 30 39 }); 31 - plugin.registerSymbol({ 40 + plugin.symbol('httpResource', { 32 41 external: '@angular/common/http', 33 42 meta: { 34 43 category: 'external', 35 44 resource: '@angular/common/http.httpResource', 36 45 }, 37 - name: 'httpResource', 38 46 }); 39 47 48 + const httpRequestStructure = new StructureModel(); 49 + const httpResourceStructure = new StructureModel(); 50 + 40 51 if (plugin.config.httpRequests.enabled) { 41 - createHttpRequests({ plugin }); 52 + const shell = createHttpRequestShell(plugin); 53 + const strategy = resolveHttpRequestsStrategy(plugin); 54 + 55 + plugin.forEach( 56 + 'operation', 57 + ({ operation }) => { 58 + httpRequestStructure.insert({ 59 + data: { 60 + operation, 61 + } satisfies OperationItem, 62 + locations: strategy(operation).map((path) => ({ path, shell })), 63 + source, 64 + }); 65 + }, 66 + { order: 'declarations' }, 67 + ); 42 68 } 43 69 44 70 if (plugin.config.httpResources.enabled) { 45 - createHttpResources({ plugin }); 71 + const shell = createHttpResourceShell(plugin); 72 + const strategy = resolveHttpResourcesStrategy(plugin); 73 + 74 + plugin.forEach( 75 + 'operation', 76 + ({ operation }) => { 77 + httpResourceStructure.insert({ 78 + data: { 79 + operation, 80 + } satisfies OperationItem, 81 + locations: strategy(operation).map((path) => ({ path, shell })), 82 + source, 83 + }); 84 + }, 85 + { order: 'declarations' }, 86 + ); 87 + } 88 + 89 + const allDependencies: Array<ReturnType<typeof $.class | typeof $.var>> = []; 90 + const allNodes: Array<ReturnType<typeof $.class | typeof $.var>> = []; 91 + 92 + for (const node of httpRequestStructure.walk()) { 93 + const { dependencies, nodes } = toHttpRequestNode(node, plugin); 94 + allDependencies.push(...(dependencies ?? [])); 95 + allNodes.push(...nodes); 96 + } 97 + for (const node of httpResourceStructure.walk()) { 98 + const { dependencies, nodes } = toHttpResourceNode(node, plugin); 99 + allDependencies.push(...(dependencies ?? [])); 100 + allNodes.push(...nodes); 101 + } 102 + 103 + const uniqueDependencies = new Map< 104 + number, 105 + ReturnType<typeof $.class | typeof $.var> 106 + >(); 107 + for (const dep of allDependencies) { 108 + if (dep.symbol) uniqueDependencies.set(dep.symbol.id, dep); 109 + } 110 + for (const dep of uniqueDependencies.values()) { 111 + plugin.node(dep); 112 + } 113 + 114 + for (const node of allNodes) { 115 + plugin.node(node); 46 116 } 47 117 };
+499
packages/openapi-ts/src/plugins/@angular/common/shared/node.ts
··· 1 + import type { 2 + StructureItem, 3 + StructureNode, 4 + StructureShell, 5 + Symbol, 6 + SymbolMeta, 7 + } from '@hey-api/codegen-core'; 8 + 9 + import type { IR } from '~/ir/types'; 10 + import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 11 + import { 12 + createOperationComment, 13 + isOperationOptionsRequired, 14 + } from '~/plugins/shared/utils/operation'; 15 + import { $ } from '~/ts-dsl'; 16 + import { applyNaming, toCase } from '~/utils/naming'; 17 + 18 + import type { AngularCommonPlugin } from '../types'; 19 + 20 + export interface OperationItem { 21 + operation: IR.OperationObject; 22 + } 23 + 24 + export const source = globalThis.Symbol('@angular/common'); 25 + 26 + function attachComment< 27 + T extends ReturnType<typeof $.var | typeof $.method>, 28 + >(args: { node: T; operation: IR.OperationObject }): T { 29 + const { node, operation } = args; 30 + return node.$if(createOperationComment(operation), (n, v) => n.doc(v)) as T; 31 + } 32 + 33 + function createHttpRequestFnMeta(operation: IR.OperationObject): SymbolMeta { 34 + return { 35 + category: 'utility', 36 + resource: 'operation', 37 + resourceId: operation.id, 38 + role: 'request', 39 + tool: 'angular', 40 + }; 41 + } 42 + 43 + function createHttpRequestShellMeta(node: StructureNode): SymbolMeta { 44 + return { 45 + category: 'utility', 46 + resource: 'shell', 47 + resourceId: node.getPath().join('.'), 48 + role: 'request', 49 + tool: 'angular', 50 + }; 51 + } 52 + 53 + function createHttpResourceFnMeta(operation: IR.OperationObject): SymbolMeta { 54 + return { 55 + category: 'utility', 56 + resource: 'operation', 57 + resourceId: operation.id, 58 + role: 'resource', 59 + tool: 'angular', 60 + }; 61 + } 62 + 63 + function createHttpResourceShellMeta(node: StructureNode): SymbolMeta { 64 + return { 65 + category: 'utility', 66 + resource: 'shell', 67 + resourceId: node.getPath().join('.'), 68 + role: 'resource', 69 + tool: 'angular', 70 + }; 71 + } 72 + 73 + function createHttpRequestFnSymbol( 74 + plugin: AngularCommonPlugin['Instance'], 75 + item: StructureItem & { data: OperationItem }, 76 + ): Symbol { 77 + const { operation } = item.data; 78 + const name = item.location[item.location.length - 1]!; 79 + return plugin.symbol( 80 + applyNaming(name, plugin.config.httpRequests.methodName), 81 + { 82 + meta: createHttpRequestFnMeta(operation), 83 + }, 84 + ); 85 + } 86 + 87 + function createHttpResourceFnSymbol( 88 + plugin: AngularCommonPlugin['Instance'], 89 + item: StructureItem & { data: OperationItem }, 90 + ): Symbol { 91 + const { operation } = item.data; 92 + const name = item.location[item.location.length - 1]!; 93 + return plugin.symbol( 94 + applyNaming(name, plugin.config.httpResources.methodName), 95 + { 96 + meta: createHttpResourceFnMeta(operation), 97 + }, 98 + ); 99 + } 100 + 101 + function childToHttpRequestNode( 102 + resource: StructureNode, 103 + plugin: AngularCommonPlugin['Instance'], 104 + ): ReadonlyArray<ReturnType<typeof $.field | typeof $.getter>> { 105 + const refChild = plugin.referenceSymbol(createHttpRequestShellMeta(resource)); 106 + const memberNameStr = toCase(refChild.name, 'camelCase'); 107 + const memberName = plugin.symbol(memberNameStr); 108 + const privateName = plugin.symbol(`_${memberNameStr}`); 109 + return [ 110 + $.field(privateName, (f) => f.private().optional().type(refChild)), 111 + $.getter(memberName, (g) => 112 + g 113 + .returns(refChild) 114 + .do( 115 + $('this') 116 + .attr(privateName) 117 + .nullishAssign($.new(refChild).args()) 118 + .return(), 119 + ), 120 + ), 121 + ]; 122 + } 123 + 124 + function childToHttpResourceNode( 125 + resource: StructureNode, 126 + plugin: AngularCommonPlugin['Instance'], 127 + ): ReadonlyArray<ReturnType<typeof $.field | typeof $.getter>> { 128 + const refChild = plugin.referenceSymbol( 129 + createHttpResourceShellMeta(resource), 130 + ); 131 + const memberNameStr = toCase(refChild.name, 'camelCase'); 132 + const memberName = plugin.symbol(memberNameStr); 133 + const privateName = plugin.symbol(`_${memberNameStr}`); 134 + return [ 135 + $.field(privateName, (f) => f.private().optional().type(refChild)), 136 + $.getter(memberName, (g) => 137 + g 138 + .returns(refChild) 139 + .do( 140 + $('this') 141 + .attr(privateName) 142 + .nullishAssign($.new(refChild).args()) 143 + .return(), 144 + ), 145 + ), 146 + ]; 147 + } 148 + 149 + export function createHttpRequestShell( 150 + plugin: AngularCommonPlugin['Instance'], 151 + ): StructureShell { 152 + const client = getClientPlugin(plugin.context.config); 153 + const isAngularClient = client.name === '@hey-api/client-angular'; 154 + 155 + const symbolInjectable = plugin.external('@angular/core.Injectable'); 156 + 157 + return { 158 + define: (node) => { 159 + const symbol = plugin.symbol( 160 + applyNaming( 161 + node.name, 162 + node.isRoot 163 + ? plugin.config.httpRequests.containerName 164 + : plugin.config.httpRequests.segmentName, 165 + ), 166 + { 167 + meta: createHttpRequestShellMeta(node), 168 + }, 169 + ); 170 + 171 + const c = $.class(symbol) 172 + .export() 173 + .$if(isAngularClient && node.isRoot, (c) => 174 + c.decorator( 175 + symbolInjectable, 176 + $.object().prop('providedIn', $.literal('root')), 177 + ), 178 + ); 179 + 180 + return { dependencies: [], node: c }; 181 + }, 182 + }; 183 + } 184 + 185 + export function createHttpResourceShell( 186 + plugin: AngularCommonPlugin['Instance'], 187 + ): StructureShell { 188 + const client = getClientPlugin(plugin.context.config); 189 + const isAngularClient = client.name === '@hey-api/client-angular'; 190 + 191 + const symbolInjectable = plugin.external('@angular/core.Injectable'); 192 + 193 + return { 194 + define: (node) => { 195 + const symbol = plugin.symbol( 196 + applyNaming( 197 + node.name, 198 + node.isRoot 199 + ? plugin.config.httpResources.containerName 200 + : plugin.config.httpResources.segmentName, 201 + ), 202 + { 203 + meta: createHttpResourceShellMeta(node), 204 + }, 205 + ); 206 + 207 + const c = $.class(symbol) 208 + .export() 209 + .$if(isAngularClient && node.isRoot, (c) => 210 + c.decorator( 211 + symbolInjectable, 212 + $.object().prop('providedIn', $.literal('root')), 213 + ), 214 + ); 215 + 216 + return { dependencies: [], node: c }; 217 + }, 218 + }; 219 + } 220 + 221 + function implementHttpRequestFn< 222 + T extends ReturnType<typeof $.func | typeof $.method>, 223 + >(args: { 224 + node: T; 225 + operation: IR.OperationObject; 226 + plugin: AngularCommonPlugin['Instance']; 227 + }): T { 228 + const { node, operation, plugin } = args; 229 + const isRequiredOptions = isOperationOptionsRequired({ 230 + context: plugin.context, 231 + operation, 232 + }); 233 + 234 + const symbolHttpRequest = plugin.external('@angular/common/http.HttpRequest'); 235 + const symbolClient = plugin.getSymbol({ category: 'client' }); 236 + const symbolOptions = plugin.referenceSymbol({ 237 + category: 'type', 238 + resource: 'client-options', 239 + tool: 'sdk', 240 + }); 241 + const symbolDataType = plugin.querySymbol({ 242 + category: 'type', 243 + resource: 'operation', 244 + resourceId: operation.id, 245 + role: 'data', 246 + tool: 'typescript', 247 + }); 248 + 249 + return node 250 + .param('options', (p) => 251 + p.required(isRequiredOptions).type( 252 + $.type(symbolOptions) 253 + .generic(symbolDataType ?? 'unknown') 254 + .generic('ThrowOnError'), 255 + ), 256 + ) 257 + .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 258 + .returns($.type(symbolHttpRequest).generic('unknown')) 259 + .do( 260 + $.return( 261 + $('options') 262 + .attr('client') 263 + .optional() 264 + .$if(symbolClient, (c, s) => c.coalesce(s)) 265 + .attr('requestOptions') 266 + .call( 267 + $.object() 268 + .prop('responseStyle', $.literal('data')) 269 + .prop('method', $.literal(operation.method.toUpperCase())) 270 + .prop('url', $.literal(operation.path)) 271 + .spread('options'), 272 + ), 273 + ), 274 + ) as T; 275 + } 276 + 277 + function implementHttpResourceFn< 278 + T extends ReturnType<typeof $.func | typeof $.method>, 279 + >(args: { 280 + node: T; 281 + operation: IR.OperationObject; 282 + plugin: AngularCommonPlugin['Instance']; 283 + }): T { 284 + const { node, operation, plugin } = args; 285 + const isRequiredOptions = isOperationOptionsRequired({ 286 + context: plugin.context, 287 + operation, 288 + }); 289 + 290 + const symbolHttpResource = plugin.external( 291 + '@angular/common/http.httpResource', 292 + ); 293 + const symbolInject = plugin.external('@angular/core.inject'); 294 + const symbolOptions = plugin.referenceSymbol({ 295 + category: 'type', 296 + resource: 'client-options', 297 + tool: 'sdk', 298 + }); 299 + const symbolDataType = plugin.querySymbol({ 300 + category: 'type', 301 + resource: 'operation', 302 + resourceId: operation.id, 303 + role: 'data', 304 + tool: 'typescript', 305 + }); 306 + const symbolResponseType = plugin.querySymbol({ 307 + category: 'type', 308 + resource: 'operation', 309 + resourceId: operation.id, 310 + role: 'response', 311 + }); 312 + 313 + return node 314 + .param('options', (p) => 315 + p.required(isRequiredOptions).type( 316 + $.type.func().returns( 317 + $.type.or( 318 + $.type(symbolOptions) 319 + .generic(symbolDataType ?? 'unknown') 320 + .generic('ThrowOnError'), 321 + $.type('undefined'), 322 + ), 323 + ), 324 + ), 325 + ) 326 + .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 327 + .do( 328 + $.return( 329 + $(symbolHttpResource) 330 + .call( 331 + $.func().do( 332 + $.const('opts').assign( 333 + $.ternary('options') 334 + .do($('options').call()) 335 + .otherwise($.id('undefined')), 336 + ), 337 + $.return( 338 + $.ternary('opts') 339 + .do( 340 + $.lazy((ctx) => 341 + ctx 342 + .access( 343 + plugin.referenceSymbol( 344 + createHttpRequestFnMeta(operation), 345 + ), 346 + { 347 + transform: (node, index) => 348 + index === 0 349 + ? node['~dsl'] === 'ClassTsDsl' 350 + ? $(symbolInject).call($(node.name)) 351 + : $(node.name) 352 + : node, 353 + }, 354 + ) 355 + .call('opts'), 356 + ), 357 + ) 358 + .otherwise($.id('undefined')), 359 + ), 360 + ), 361 + ) 362 + .generic(symbolResponseType ?? 'unknown'), 363 + ), 364 + ) as T; 365 + } 366 + 367 + export function toHttpRequestNode( 368 + model: StructureNode, 369 + plugin: AngularCommonPlugin['Instance'], 370 + ): { 371 + dependencies?: Array<ReturnType<typeof $.class | typeof $.var>>; 372 + nodes: ReadonlyArray<ReturnType<typeof $.class | typeof $.var>>; 373 + } { 374 + if (model.virtual) { 375 + const nodes: Array<ReturnType<typeof $.var>> = []; 376 + for (const item of model.itemsFrom<OperationItem>(source)) { 377 + const { operation } = item.data; 378 + let node = $.const(createHttpRequestFnSymbol(plugin, item)) 379 + .export() 380 + .assign( 381 + implementHttpRequestFn({ 382 + node: $.func(), 383 + operation, 384 + plugin, 385 + }), 386 + ); 387 + node = attachComment({ node, operation }); 388 + nodes.push(node); 389 + } 390 + return { nodes }; 391 + } 392 + 393 + if (!model.shell) { 394 + return { nodes: [] }; 395 + } 396 + 397 + const nodes: Array<ReturnType<typeof $.class>> = []; 398 + 399 + const shell = model.shell.define(model); 400 + const node = shell.node as ReturnType<typeof $.class>; 401 + 402 + let index = 0; 403 + for (const item of model.itemsFrom<OperationItem>(source)) { 404 + const { operation } = item.data; 405 + if (index > 0 || node.hasBody) node.newline(); 406 + node.do( 407 + implementHttpRequestFn({ 408 + node: $.method(createHttpRequestFnSymbol(plugin, item), (m) => 409 + attachComment({ 410 + node: m, 411 + operation, 412 + }).public(), 413 + ), 414 + operation, 415 + plugin, 416 + }), 417 + ); 418 + index += 1; 419 + } 420 + 421 + for (const child of model.children.values()) { 422 + if (node.hasBody) node.newline(); 423 + node.do(...childToHttpRequestNode(child, plugin)); 424 + } 425 + 426 + nodes.push(node); 427 + 428 + return { 429 + dependencies: shell.dependencies as Array<ReturnType<typeof $.class>>, 430 + nodes, 431 + }; 432 + } 433 + 434 + export function toHttpResourceNode( 435 + model: StructureNode, 436 + plugin: AngularCommonPlugin['Instance'], 437 + ): { 438 + dependencies?: Array<ReturnType<typeof $.class | typeof $.var>>; 439 + nodes: ReadonlyArray<ReturnType<typeof $.class | typeof $.var>>; 440 + } { 441 + if (model.virtual) { 442 + const nodes: Array<ReturnType<typeof $.var>> = []; 443 + for (const item of model.itemsFrom<OperationItem>(source)) { 444 + const { operation } = item.data; 445 + let node = $.const(createHttpResourceFnSymbol(plugin, item)) 446 + .export() 447 + .assign( 448 + implementHttpResourceFn({ 449 + node: $.func(), 450 + operation, 451 + plugin, 452 + }), 453 + ); 454 + node = attachComment({ node, operation }); 455 + nodes.push(node); 456 + } 457 + return { nodes }; 458 + } 459 + 460 + if (!model.shell) { 461 + return { nodes: [] }; 462 + } 463 + 464 + const nodes: Array<ReturnType<typeof $.class>> = []; 465 + 466 + const shell = model.shell.define(model); 467 + const node = shell.node as ReturnType<typeof $.class>; 468 + 469 + let index = 0; 470 + for (const item of model.itemsFrom<OperationItem>(source)) { 471 + const { operation } = item.data; 472 + if (index > 0 || node.hasBody) node.newline(); 473 + node.do( 474 + implementHttpResourceFn({ 475 + node: $.method(createHttpResourceFnSymbol(plugin, item), (m) => 476 + attachComment({ 477 + node: m, 478 + operation, 479 + }).public(), 480 + ), 481 + operation, 482 + plugin, 483 + }), 484 + ); 485 + index += 1; 486 + } 487 + 488 + for (const child of model.children.values()) { 489 + if (node.hasBody) node.newline(); 490 + node.do(...childToHttpResourceNode(child, plugin)); 491 + } 492 + 493 + nodes.push(node); 494 + 495 + return { 496 + dependencies: shell.dependencies as Array<ReturnType<typeof $.class>>, 497 + nodes, 498 + }; 499 + }
+16 -104
packages/openapi-ts/src/plugins/@angular/common/types.d.ts
··· 1 + import type { OperationsStrategy } from '~/openApi/shared/locations'; 1 2 import type { DefinePlugin, Plugin } from '~/plugins'; 2 - import type { StringName } from '~/types/case'; 3 + 4 + import type { 5 + HttpRequestsConfig, 6 + UserHttpRequestsConfig, 7 + } from './httpRequests'; 8 + import type { 9 + HttpResourcesConfig, 10 + UserHttpResourcesConfig, 11 + } from './httpResources'; 3 12 4 13 export type UserConfig = Plugin.Name<'@angular/common'> & 5 14 Plugin.Hooks & { ··· 13 22 /** 14 23 * Options for generating HTTP Request instances. 15 24 * 16 - * @default true 25 + * @default 'flat' 17 26 */ 18 - httpRequests?: 19 - | boolean 20 - | { 21 - /** 22 - * Whether to generate the resource as a class. 23 - * 24 - * @default false 25 - */ 26 - asClass?: boolean; 27 - /** 28 - * Builds the class name for the generated resource. 29 - * By default, the class name is suffixed with "Resources". 30 - */ 31 - classNameBuilder?: StringName; 32 - /** 33 - * Whether or not to create HTTP Request instances. 34 - * 35 - * @default true 36 - */ 37 - enabled?: boolean; 38 - /** 39 - * Builds the method name for the generated resource. 40 - * 41 - * By default, the operation id is used, if `asClass` is false, the method is also suffixed with "Resource". 42 - */ 43 - methodNameBuilder?: (operation: IR.OperationObject) => string; 44 - }; 27 + httpRequests?: boolean | OperationsStrategy | UserHttpRequestsConfig; 45 28 /** 46 29 * Options for generating HTTP resource APIs. 47 30 * 48 - * @default true 31 + * @default 'flat' 49 32 */ 50 - httpResources?: 51 - | boolean 52 - | { 53 - /** 54 - * Whether to generate the resource as a class. 55 - * @default false 56 - */ 57 - asClass?: boolean; 58 - /** 59 - * Builds the class name for the generated resource. 60 - * By default, the class name is suffixed with "Resources". 61 - */ 62 - classNameBuilder?: StringName; 63 - /** 64 - * Whether or not to create HTTP resource APIs. 65 - * 66 - * @default true 67 - */ 68 - enabled?: boolean; 69 - /** 70 - * Builds the method name for the generated resource. 71 - * 72 - * By default, the operation id is used, if `asClass` is false, the method is also suffixed with "Resource". 73 - */ 74 - methodNameBuilder?: (operation: IR.OperationObject) => string; 75 - }; 33 + httpResources?: boolean | OperationsStrategy | UserHttpResourcesConfig; 76 34 }; 77 35 78 36 export type Config = Plugin.Name<'@angular/common'> & ··· 87 45 /** 88 46 * Options for generating HTTP Request instances. 89 47 */ 90 - httpRequests: { 91 - /** 92 - * Whether to generate the resource as a class. 93 - * 94 - * @default false 95 - */ 96 - asClass: boolean; 97 - /** 98 - * Builds the class name for the generated resource. 99 - * By default, the class name is suffixed with "Resources". 100 - */ 101 - classNameBuilder: StringName; 102 - /** 103 - * Whether or not to create HTTP Request instances. 104 - * 105 - * @default true 106 - */ 107 - enabled: boolean; 108 - /** 109 - * Builds the method name for the generated resource. 110 - * By default, the operation id is used, if `asClass` is false, the method is also suffixed with "Resource". 111 - */ 112 - methodNameBuilder: (operation: IR.OperationObject) => string; 113 - }; 48 + httpRequests: HttpRequestsConfig; 114 49 /** 115 50 * Options for generating HTTP resource APIs. 116 51 */ 117 - httpResources: { 118 - /** 119 - * Whether to generate the resource as a class. 120 - * 121 - * @default false 122 - */ 123 - asClass: boolean; 124 - /** 125 - * Builds the class name for the generated resource. 126 - * By default, the class name is suffixed with "Resources". 127 - */ 128 - classNameBuilder: StringName; 129 - /** 130 - * Whether or not to create HTTP resource APIs. 131 - * 132 - * @default true 133 - */ 134 - enabled: boolean; 135 - /** 136 - * Builds the method name for the generated resource. 137 - * By default, the operation id is used, if `asClass` is false, the method is also suffixed with "Resource". 138 - */ 139 - methodNameBuilder: (operation: IR.OperationObject) => string; 140 - }; 52 + httpResources: HttpResourcesConfig; 141 53 }; 142 54 143 55 export type AngularCommonPlugin = DefinePlugin<UserConfig, Config>;
+4 -8
packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts
··· 29 29 30 30 export const createClient: PluginHandler = ({ plugin }) => { 31 31 const clientModule = clientFolderAbsolutePath(plugin.context.config); 32 - const symbolCreateClient = plugin.registerSymbol({ 32 + const symbolCreateClient = plugin.symbol('createClient', { 33 33 external: clientModule, 34 - name: 'createClient', 35 34 }); 36 - const symbolCreateConfig = plugin.registerSymbol({ 35 + const symbolCreateConfig = plugin.symbol('createConfig', { 37 36 external: clientModule, 38 - name: 'createConfig', 39 37 }); 40 38 const symbolClientOptions = plugin.referenceSymbol({ 41 39 category: 'type', ··· 45 43 46 44 const { runtimeConfigPath } = plugin.config; 47 45 const symbolCreateClientConfig = runtimeConfigPath 48 - ? plugin.registerSymbol({ 46 + ? plugin.symbol('createClientConfig', { 49 47 external: runtimeConfigPath, 50 - name: 'createClientConfig', 51 48 }) 52 49 : undefined; 53 50 ··· 84 81 .generic(symbolClientOptions), 85 82 ]; 86 83 87 - const symbolClient = plugin.registerSymbol({ 84 + const symbolClient = plugin.symbol('client', { 88 85 meta: { 89 86 category: 'client', 90 87 }, 91 - name: 'client', 92 88 }); 93 89 const statement = $.const(symbolClient) 94 90 .export()
+3 -7
packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts
··· 12 12 resource: 'client', 13 13 role: 'options', 14 14 }); 15 - const symbolConfig = plugin.registerSymbol({ 15 + const symbolConfig = plugin.symbol('Config', { 16 16 external: clientModule, 17 17 kind: 'type', 18 - name: 'Config', 19 18 }); 20 - const symbolDefaultClientOptions = plugin.registerSymbol({ 19 + const symbolDefaultClientOptions = plugin.symbol('ClientOptions', { 21 20 external: clientModule, 22 21 kind: 'type', 23 - name: 'ClientOptions', 24 22 }); 25 - const symbolCreateClientConfig = plugin.registerSymbol({ 26 - name: 'CreateClientConfig', 27 - }); 23 + const symbolCreateClientConfig = plugin.symbol('CreateClientConfig'); 28 24 29 25 const typeCreateClientConfig = $.type 30 26 .alias(symbolCreateClientConfig)
+3 -6
packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts
··· 367 367 368 368 for (const name in context.spec.definitions) { 369 369 const schema = context.spec.definitions[name]!; 370 - const symbol = plugin.registerSymbol({ 370 + const symbol = plugin.symbol(schemaName({ name, plugin, schema }), { 371 371 meta: { 372 372 category: 'schema', 373 373 resource: 'definition', 374 374 resourceId: name, 375 375 tool: 'json-schema', 376 376 }, 377 - name: schemaName({ name, plugin, schema }), 378 377 }); 379 378 const obj = schemaToJsonSchemaDraft_04({ 380 379 context, ··· 407 406 408 407 for (const name in context.spec.components.schemas) { 409 408 const schema = context.spec.components.schemas[name]!; 410 - const symbol = plugin.registerSymbol({ 409 + const symbol = plugin.symbol(schemaName({ name, plugin, schema }), { 411 410 meta: { 412 411 category: 'schema', 413 412 resource: 'definition', 414 413 resourceId: name, 415 414 tool: 'json-schema', 416 415 }, 417 - name: schemaName({ name, plugin, schema }), 418 416 }); 419 417 const obj = schemaToJsonSchemaDraft_05({ 420 418 context, ··· 447 445 448 446 for (const name in context.spec.components.schemas) { 449 447 const schema = context.spec.components.schemas[name]!; 450 - const symbol = plugin.registerSymbol({ 448 + const symbol = plugin.symbol(schemaName({ name, plugin, schema }), { 451 449 meta: { 452 450 category: 'schema', 453 451 resource: 'definition', 454 452 resourceId: name, 455 453 tool: 'json-schema', 456 454 }, 457 - name: schemaName({ name, plugin, schema }), 458 455 }); 459 456 const obj = schemaToJsonSchema2020_12({ 460 457 context,
+6 -22
packages/openapi-ts/src/plugins/@hey-api/sdk/config.ts
··· 1 1 import { definePluginConfig } from '~/plugins/shared/utils/config'; 2 2 3 + import { resolveOperations } from './operations'; 3 4 import { handler } from './plugin'; 4 5 import type { HeyApiSdkPlugin } from './types'; 5 6 6 7 export const defaultConfig: HeyApiSdkPlugin['Config'] = { 7 8 config: { 8 - asClass: false, 9 9 auth: true, 10 - classNameBuilder: '{{name}}', 11 - classStructure: 'auto', 12 10 client: true, 13 11 exportFromIndex: true, 14 - instance: '', 15 - operationId: true, 16 12 paramsStructure: 'grouped', 17 - response: 'body', 18 13 responseStyle: 'fields', 19 14 transformer: false, 20 15 validator: false, 16 + 17 + // Deprecated - kept for backward compatibility 18 + // eslint-disable-next-line sort-keys-fix/sort-keys-fix 19 + response: 'body', 21 20 }, 22 21 dependencies: ['@hey-api/typescript'], 23 22 handler, ··· 72 71 plugin.config.validator.response = false; 73 72 } 74 73 75 - if (plugin.config.instance) { 76 - if (typeof plugin.config.instance !== 'string') { 77 - plugin.config.instance = 'Sdk'; 78 - } 79 - 80 - plugin.config.asClass = true; 81 - } else { 82 - plugin.config.instance = ''; 83 - } 84 - 85 - // Set default classNameBuilder based on client type 86 - if (plugin.config.classNameBuilder === '{{name}}') { 87 - if (plugin.config.client === '@hey-api/client-angular') { 88 - plugin.config.classNameBuilder = '{{name}}Service'; 89 - } 90 - } 74 + plugin.config.operations = resolveOperations(plugin.config, context); 91 75 }, 92 76 }; 93 77
+202
packages/openapi-ts/src/plugins/@hey-api/sdk/operations/config.ts
··· 1 + import { log } from '@hey-api/codegen-core'; 2 + 3 + import type { OperationsStrategy } from '~/openApi/shared/locations'; 4 + import type { PluginContext } from '~/plugins/types'; 5 + 6 + import type { UserConfig } from '../types'; 7 + import type { OperationsConfig, UserOperationsConfig } from './types'; 8 + 9 + type Config = Omit<UserConfig, 'name'>; 10 + 11 + export function resolveOperations( 12 + config: Config, 13 + context: PluginContext, 14 + ): OperationsConfig { 15 + if (config.asClass !== undefined) { 16 + log.warnDeprecated({ 17 + context: '@hey-api/sdk', 18 + field: 'asClass', 19 + replacement: [ 20 + 'operations: { strategy: "byTags" }', 21 + 'operations: { strategy: "single" }', 22 + ], 23 + }); 24 + } 25 + 26 + if (config.classNameBuilder !== undefined) { 27 + log.warnDeprecated({ 28 + context: '@hey-api/sdk', 29 + field: 'classNameBuilder', 30 + replacement: 'operations: { containerName: "..." }', 31 + }); 32 + } 33 + 34 + if (config.classStructure !== undefined) { 35 + log.warnDeprecated({ 36 + context: '@hey-api/sdk', 37 + field: 'classStructure', 38 + replacement: [ 39 + 'operations: { nesting: "operationId" }', 40 + 'operations: { nesting: "id" }', 41 + ], 42 + }); 43 + } 44 + 45 + if (config.instance !== undefined) { 46 + log.warnDeprecated({ 47 + context: '@hey-api/sdk', 48 + field: 'instance', 49 + replacement: `operations: { strategy: "single", containerName: "${config.instance || 'Name'}", methods: "instance" }`, 50 + }); 51 + } 52 + 53 + if (config.methodNameBuilder !== undefined) { 54 + log.warnDeprecated({ 55 + context: '@hey-api/sdk', 56 + field: 'methodNameBuilder', 57 + replacement: 'operations: { methodName: "..." }', 58 + }); 59 + } 60 + 61 + if (config.operationId !== undefined) { 62 + log.warnDeprecated({ 63 + context: '@hey-api/sdk', 64 + field: 'operationId', 65 + replacement: [ 66 + 'operations: { nesting: "operationId" }', 67 + 'operations: { nesting: "id" }', 68 + ], 69 + }); 70 + } 71 + 72 + const legacy = mapLegacyToConfig(config); 73 + return normalizeConfig(config.operations, legacy, context); 74 + } 75 + 76 + function normalizeConfig( 77 + input: OperationsStrategy | UserOperationsConfig | undefined, 78 + legacy: Partial<OperationsConfig>, 79 + context: PluginContext, 80 + ): OperationsConfig { 81 + if (!input || typeof input === 'string' || typeof input === 'function') { 82 + input = { strategy: input }; 83 + } 84 + 85 + const strategy = legacy.strategy ?? input.strategy ?? 'flat'; 86 + const methods: OperationsConfig['methods'] = 87 + strategy === 'single' ? 'instance' : 'static'; 88 + 89 + return context.valueToObject({ 90 + defaultValue: { 91 + container: 'class', 92 + methods, 93 + nesting: 'operationId', 94 + nestingDelimiters: /[./]/, 95 + strategy, 96 + strategyDefaultTag: 'default', 97 + }, 98 + mappers: { 99 + object(value) { 100 + value.containerName = context.valueToObject({ 101 + defaultValue: 102 + strategy === 'single' 103 + ? { casing: 'PascalCase', name: 'Sdk' } 104 + : { casing: 'PascalCase' }, 105 + mappers: { 106 + function: (name) => ({ name }), 107 + string: (name) => ({ name }), 108 + }, 109 + value: value.containerName, 110 + }); 111 + value.methodName = context.valueToObject({ 112 + defaultValue: { casing: 'camelCase' }, 113 + mappers: { 114 + function: (name) => ({ name }), 115 + string: (name) => ({ name }), 116 + }, 117 + value: value.methodName, 118 + }); 119 + value.segmentName = context.valueToObject({ 120 + defaultValue: { casing: 'PascalCase' }, 121 + mappers: { 122 + function: (name) => ({ name }), 123 + string: (name) => ({ name }), 124 + }, 125 + value: value.segmentName, 126 + }); 127 + return value; 128 + }, 129 + }, 130 + value: { 131 + ...legacy, 132 + ...input, 133 + } as UserOperationsConfig, 134 + }) as OperationsConfig; 135 + } 136 + 137 + function mapLegacyToConfig(config: Config): Partial<OperationsConfig> { 138 + let strategy: OperationsConfig['strategy'] | undefined; 139 + if (config.instance) { 140 + strategy = 'single'; 141 + } else if (config.asClass) { 142 + strategy = 'byTags'; 143 + } else if (config.instance === false || config.asClass === false) { 144 + strategy = 'flat'; 145 + } 146 + 147 + let containerName: OperationsConfig['containerName'] | undefined; 148 + let segmentName: OperationsConfig['segmentName'] | undefined; 149 + if (config.instance) { 150 + let name = typeof config.instance === 'string' ? config.instance : 'Sdk'; 151 + segmentName = { casing: 'PascalCase' }; 152 + if (config.classNameBuilder) { 153 + segmentName.name = config.classNameBuilder; 154 + if (typeof config.classNameBuilder === 'string') { 155 + name = config.classNameBuilder.replace('{{name}}', name); 156 + } else { 157 + name = config.classNameBuilder(name); 158 + } 159 + } 160 + containerName = { casing: 'PascalCase', name }; 161 + } else if (config.classNameBuilder) { 162 + containerName = { 163 + casing: 'PascalCase', 164 + name: config.classNameBuilder, 165 + }; 166 + segmentName = { ...containerName }; 167 + } else if (config.asClass) { 168 + containerName = { casing: 'PascalCase' }; 169 + segmentName = { ...containerName }; 170 + } 171 + 172 + let methods: OperationsConfig['methods'] | undefined; 173 + if (config.instance) { 174 + methods = 'instance'; 175 + } else if (config.asClass) { 176 + methods = 'static'; 177 + } 178 + 179 + let nesting: OperationsConfig['nesting'] | undefined; 180 + if (config.classStructure === 'off' || config.operationId === false) { 181 + nesting = 'id'; 182 + } else if (config.classStructure === 'auto') { 183 + nesting = 'operationId'; 184 + } 185 + 186 + let methodName: OperationsConfig['methodName'] | undefined; 187 + if (config.methodNameBuilder) { 188 + methodName = { 189 + casing: 'camelCase', 190 + name: config.methodNameBuilder, 191 + }; 192 + } 193 + 194 + return { 195 + containerName, 196 + methodName, 197 + methods, 198 + nesting, 199 + segmentName, 200 + strategy, 201 + }; 202 + }
+3
packages/openapi-ts/src/plugins/@hey-api/sdk/operations/index.ts
··· 1 + export { resolveOperations } from './config'; 2 + export { resolveStrategy } from './resolve'; 3 + export type { OperationsConfig, UserOperationsConfig } from './types';
+51
packages/openapi-ts/src/plugins/@hey-api/sdk/operations/resolve.ts
··· 1 + import type { 2 + OperationPathStrategy, 3 + OperationStructureStrategy, 4 + } from '~/openApi/shared/locations'; 5 + import { OperationPath, OperationStrategy } from '~/openApi/shared/locations'; 6 + 7 + import type { HeyApiSdkPlugin } from '../types'; 8 + 9 + function resolvePath( 10 + plugin: HeyApiSdkPlugin['Instance'], 11 + ): OperationPathStrategy { 12 + if (plugin.config.operations.nesting === 'id') { 13 + return OperationPath.id(); 14 + } 15 + 16 + if (plugin.config.operations.nesting === 'operationId') { 17 + return OperationPath.fromOperationId({ 18 + delimiters: plugin.config.operations.nestingDelimiters, 19 + fallback: OperationPath.id(), 20 + }); 21 + } 22 + 23 + return plugin.config.operations.nesting; 24 + } 25 + 26 + export function resolveStrategy( 27 + plugin: HeyApiSdkPlugin['Instance'], 28 + ): OperationStructureStrategy { 29 + if (plugin.config.operations.strategy === 'flat') { 30 + return OperationStrategy.flat({ 31 + path: (operation) => [resolvePath(plugin)(operation).join('.')], 32 + }); 33 + } 34 + 35 + if (plugin.config.operations.strategy === 'single') { 36 + const root = plugin.config.operations.containerName; 37 + return OperationStrategy.single({ 38 + path: resolvePath(plugin), 39 + root: typeof root.name === 'string' ? root.name : (root.name?.('') ?? ''), 40 + }); 41 + } 42 + 43 + if (plugin.config.operations.strategy === 'byTags') { 44 + return OperationStrategy.byTags({ 45 + fallback: plugin.config.operations.strategyDefaultTag, 46 + path: resolvePath(plugin), 47 + }); 48 + } 49 + 50 + return plugin.config.operations.strategy; 51 + }
+185
packages/openapi-ts/src/plugins/@hey-api/sdk/operations/types.d.ts
··· 1 + import type { 2 + OperationPathStrategy, 3 + OperationsStrategy, 4 + } from '~/openApi/shared/locations'; 5 + import type { NamingConfig, NamingRule } from '~/utils/naming'; 6 + 7 + export interface UserOperationsConfig { 8 + /** 9 + * Type of container for grouped operations. 10 + * 11 + * Ignored when `strategy` is `'flat'`. 12 + * 13 + * - `'class'` - Class with methods 14 + * 15 + * @default 'class' 16 + */ 17 + container?: 'class'; 18 + // * - `'object'` - Plain object literal 19 + // container?: 'class' | 'object'; 20 + /** 21 + * Customize container names. 22 + * 23 + * For `'single'` strategy, this sets the root container name. 24 + * For `'byTags'` strategy, this transforms tag names. 25 + * 26 + * @default 'Sdk' for `'single'` strategy 27 + * 28 + * @example 29 + * // Set root name for single strategy 30 + * containerName: 'MyApi' 31 + * 32 + * @example 33 + * // Transform tag names with suffix 34 + * containerName: '{{name}}Service' 35 + * 36 + * @example 37 + * // With casing 38 + * containerName: { name: '{{name}}Service', casing: 'PascalCase' } 39 + */ 40 + containerName?: NamingRule; 41 + /** 42 + * Customize method/function names. 43 + * 44 + * Applied to the final segment of the path (the method name). 45 + */ 46 + methodName?: NamingRule; 47 + /** 48 + * How methods are attached to class containers. 49 + * 50 + * Only applies when `container` is `'class'`. 51 + * 52 + * - `'static'` - Static methods, no instantiation required 53 + * - `'instance'` - Instance methods, requires `new ClassName(config)` 54 + * 55 + * @default 'instance' 56 + */ 57 + methods?: 'instance' | 'static'; 58 + /** 59 + * How to derive nesting structure from operations. 60 + * 61 + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `Users.list()`) 62 + * - `'id'` - Use operation id as-is, no nesting 63 + * - Custom function for full control 64 + * 65 + * @default 'operationId' 66 + */ 67 + nesting?: 'operationId' | 'id' | OperationPathStrategy; 68 + /** 69 + * Delimiters for splitting operationId. 70 + * 71 + * Only applies when `nesting` is `'operationId'`. 72 + * 73 + * @default /[./]/ 74 + */ 75 + nestingDelimiters?: RegExp; 76 + /** 77 + * Customize nesting segment names. 78 + * 79 + * Applied to intermediate path segments (not the method name). 80 + */ 81 + segmentName?: NamingRule; 82 + /** 83 + * Grouping strategy. 84 + * 85 + * - `'flat'` - Standalone functions, no grouping 86 + * - `'byTags'` - One container per operation tag 87 + * - `'single'` - All operations in one container 88 + * - Custom function for full control 89 + * 90 + * @default 'flat' 91 + */ 92 + strategy?: OperationsStrategy; 93 + /** 94 + * Default container name for operations without tags. 95 + * 96 + * Only applies when `strategy` is `'byTags'`. 97 + * 98 + * @default 'default' 99 + */ 100 + strategyDefaultTag?: string; 101 + } 102 + 103 + export interface OperationsConfig { 104 + /** 105 + * Type of container for grouped operations. 106 + * 107 + * Ignored when `strategy` is `'flat'`. 108 + * 109 + * - `'class'` - Class with methods 110 + */ 111 + container: 'class'; 112 + // * - `'object'` - Plain object literal 113 + // container: 'class' | 'object'; 114 + /** 115 + * Customize container names. 116 + * 117 + * For `'single'` strategy, this sets the root container name. 118 + * For `'byTags'` strategy, this transforms tag names. 119 + * 120 + * @default 'Sdk' for `'single'` strategy 121 + * 122 + * @example 123 + * // Set root name for single strategy 124 + * containerName: 'MyApi' 125 + * 126 + * @example 127 + * // Transform tag names with suffix 128 + * containerName: '{{name}}Service' 129 + * 130 + * @example 131 + * // With casing 132 + * containerName: { name: '{{name}}Service', case: 'PascalCase' } 133 + */ 134 + containerName: NamingConfig; 135 + /** 136 + * Customize method/function names. 137 + * 138 + * Applied to the final segment of the path (the method name). 139 + */ 140 + methodName: NamingConfig; 141 + /** 142 + * How methods are attached to class containers. 143 + * 144 + * Only applies when `container` is `'class'`. 145 + * 146 + * - `'static'` - Static methods, no instantiation required 147 + * - `'instance'` - Instance methods, requires `new ClassName(config)` 148 + */ 149 + methods: 'instance' | 'static'; 150 + /** 151 + * How to derive nesting structure from operations. 152 + * 153 + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `Users.list()`) 154 + * - `'id'` - Use operation id as-is, no nesting 155 + * - Custom function for full control 156 + */ 157 + nesting: 'operationId' | 'id' | OperationPathStrategy; 158 + /** 159 + * Delimiters for splitting operationId. 160 + * 161 + * Only applies when `nesting` is `'operationId'`. 162 + */ 163 + nestingDelimiters: RegExp; 164 + /** 165 + * Customize nesting segment names. 166 + * 167 + * Applied to intermediate path segments (not the method name). 168 + */ 169 + segmentName: NamingConfig; 170 + /** 171 + * Grouping strategy. 172 + * 173 + * - `'flat'` - Standalone functions, no grouping 174 + * - `'byTags'` - One container per operation tag 175 + * - `'single'` - All operations in one container 176 + * - Custom function for full control 177 + */ 178 + strategy: OperationsStrategy; 179 + /** 180 + * Default container name for operations without tags. 181 + * 182 + * Only applies when `strategy` is `'byTags'`. 183 + */ 184 + strategyDefaultTag: string; 185 + }
+5 -372
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/class.ts
··· 1 1 import type { Symbol } from '@hey-api/codegen-core'; 2 2 3 - import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 4 - import { 5 - createOperationComment, 6 - isOperationOptionsRequired, 7 - } from '~/plugins/shared/utils/operation'; 8 - import type { TsDsl } from '~/ts-dsl'; 9 3 import { $ } from '~/ts-dsl'; 10 - import { stringCase } from '~/utils/stringCase'; 11 4 12 5 import type { HeyApiSdkPlugin } from '../types'; 13 - import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; 14 - import { 15 - operationClasses, 16 - operationParameters, 17 - operationStatements, 18 - } from './operation'; 19 6 20 - type SdkClassEntry = { 21 - /** 22 - * Name of the class. 23 - */ 24 - className: string; 25 - /** 26 - * Class names for child classes located inside this class. 27 - */ 28 - classes: Set<string>; 29 - /** 30 - * Symbol ID for the class. 31 - */ 32 - id: number; 33 - /** 34 - * Track unique added method nodes. 35 - */ 36 - methods: Set<string>; 37 - /** 38 - * List of class nodes containing methods. 39 - */ 40 - nodes: Array<TsDsl>; 41 - /** 42 - * Is this a root class? 43 - */ 44 - root: boolean; 45 - }; 46 - 47 - export const registryName = '__registry'; 48 - 49 - const createRegistryClass = ({ 7 + export const createRegistryClass = ({ 50 8 plugin, 51 9 sdkSymbol, 52 10 symbol, ··· 54 12 plugin: HeyApiSdkPlugin['Instance']; 55 13 sdkSymbol: Symbol; 56 14 symbol: Symbol; 57 - }): TsDsl => { 15 + }): ReturnType<typeof $.class> => { 58 16 const symbolDefaultKey = plugin.symbol('defaultKey'); 59 17 const symbolInstances = plugin.symbol('instances'); 60 18 return $.class(symbol) ··· 107 65 ); 108 66 }; 109 67 110 - const createClientClass = ({ 68 + export const createClientClass = ({ 111 69 plugin, 112 70 symbol, 113 71 }: { 114 72 plugin: HeyApiSdkPlugin['Instance']; 115 73 symbol: Symbol; 116 - }): TsDsl => { 117 - const symClient = plugin.getSymbol({ 118 - category: 'client', 119 - }); 74 + }): ReturnType<typeof $.class> => { 75 + const symClient = plugin.getSymbol({ category: 'client' }); 120 76 const optionalClient = Boolean(plugin.config.client && symClient); 121 77 const symbolClient = plugin.referenceSymbol({ 122 78 category: 'external', ··· 150 106 ), 151 107 ); 152 108 }; 153 - 154 - export const generateClassSdk = ({ 155 - plugin, 156 - }: { 157 - plugin: HeyApiSdkPlugin['Instance']; 158 - }): void => { 159 - const client = getClientPlugin(plugin.context.config); 160 - const isAngularClient = client.name === '@hey-api/client-angular'; 161 - const isNuxtClient = client.name === '@hey-api/client-nuxt'; 162 - const sdkClasses = new Map<string, SdkClassEntry>(); 163 - /** 164 - * Track unique added classes. 165 - */ 166 - const generatedClasses = new Set<string>(); 167 - 168 - plugin.forEach( 169 - 'operation', 170 - ({ operation }) => { 171 - const isRequiredOptions = isOperationOptionsRequired({ 172 - context: plugin.context, 173 - operation, 174 - }); 175 - const symbolResponse = isNuxtClient 176 - ? plugin.querySymbol({ 177 - category: 'type', 178 - resource: 'operation', 179 - resourceId: operation.id, 180 - role: 'response', 181 - }) 182 - : undefined; 183 - 184 - const classes = operationClasses({ operation, plugin }); 185 - 186 - for (const entry of classes.values()) { 187 - entry.path.forEach((currentClassName, index) => { 188 - const symbolCurrentClass = plugin.referenceSymbol({ 189 - category: 'utility', 190 - resource: 'class', 191 - resourceId: currentClassName, 192 - tool: 'sdk', 193 - }); 194 - if (!sdkClasses.has(symbolCurrentClass.meta!.resourceId!)) { 195 - sdkClasses.set(symbolCurrentClass.meta!.resourceId!, { 196 - className: symbolCurrentClass.meta!.resourceId!, 197 - classes: new Set(), 198 - id: symbolCurrentClass.id, 199 - methods: new Set(), 200 - nodes: [], 201 - root: !index, 202 - }); 203 - } 204 - 205 - const parentClassName = entry.path[index - 1]; 206 - if (parentClassName) { 207 - const symbolParentClass = plugin.referenceSymbol({ 208 - category: 'utility', 209 - resource: 'class', 210 - resourceId: parentClassName, 211 - tool: 'sdk', 212 - }); 213 - if ( 214 - symbolParentClass.meta?.resourceId !== 215 - symbolCurrentClass.meta?.resourceId 216 - ) { 217 - const parentClass = sdkClasses.get( 218 - symbolParentClass.meta!.resourceId!, 219 - )!; 220 - parentClass.classes.add(symbolCurrentClass.meta!.resourceId!); 221 - sdkClasses.set(symbolParentClass.meta!.resourceId!, parentClass); 222 - } 223 - } 224 - 225 - const isLast = entry.path.length === index + 1; 226 - // add methods only to the last class 227 - if (!isLast) { 228 - return; 229 - } 230 - 231 - const currentClass = sdkClasses.get( 232 - symbolCurrentClass.meta!.resourceId!, 233 - )!; 234 - 235 - const methodName = entry.methodName; 236 - if (currentClass.methods.has(methodName)) return; 237 - currentClass.methods.add(methodName); 238 - 239 - const opParameters = operationParameters({ 240 - isRequiredOptions, 241 - operation, 242 - plugin, 243 - }); 244 - const statements = operationStatements({ 245 - isRequiredOptions, 246 - opParameters, 247 - operation, 248 - plugin, 249 - }); 250 - const functionNode = $.method(methodName, (m) => 251 - m 252 - .$if(createOperationComment(operation), (m, v) => m.doc(v)) 253 - .public() 254 - .static(!isAngularClient && !plugin.config.instance) 255 - .$if( 256 - isNuxtClient, 257 - (m) => 258 - m 259 - .generic(nuxtTypeComposable, (t) => 260 - t 261 - .extends( 262 - plugin.referenceSymbol({ 263 - category: 'external', 264 - resource: 'client.Composable', 265 - }), 266 - ) 267 - .default($.type.literal('$fetch')), 268 - ) 269 - .generic(nuxtTypeDefault, (t) => 270 - t.$if(symbolResponse, (t, s) => t.extends(s).default(s)), 271 - ), 272 - (m) => 273 - m.generic('ThrowOnError', (t) => 274 - t 275 - .extends('boolean') 276 - .default( 277 - ('throwOnError' in client.config 278 - ? client.config.throwOnError 279 - : false) ?? false, 280 - ), 281 - ), 282 - ) 283 - .params(...opParameters.parameters) 284 - .do(...statements), 285 - ); 286 - 287 - if (!currentClass.nodes.length) { 288 - currentClass.nodes.push(functionNode); 289 - } else { 290 - currentClass.nodes.push($.newline(), functionNode); 291 - } 292 - 293 - sdkClasses.set(symbolCurrentClass.meta!.resourceId!, currentClass); 294 - }); 295 - } 296 - }, 297 - { 298 - order: 'declarations', 299 - }, 300 - ); 301 - 302 - const clientIndex = plugin.config.instance ? plugin.node(null) : undefined; 303 - const symbolClient = 304 - clientIndex !== undefined 305 - ? plugin.symbol('HeyApiClient', { 306 - meta: { 307 - category: 'utility', 308 - resource: 'class', 309 - resourceId: 'HeyApiClient', 310 - tool: 'sdk', 311 - }, 312 - }) 313 - : undefined; 314 - const registryIndex = plugin.config.instance ? plugin.node(null) : undefined; 315 - 316 - const generateClass = (currentClass: SdkClassEntry) => { 317 - const resourceId = currentClass.className; 318 - 319 - if (generatedClasses.has(resourceId)) return; 320 - generatedClasses.add(resourceId); 321 - 322 - if (clientIndex !== undefined && symbolClient && !symbolClient.node) { 323 - const node = createClientClass({ plugin, symbol: symbolClient }); 324 - plugin.node(node, clientIndex); 325 - } 326 - 327 - for (const childClassName of currentClass.classes) { 328 - const childClass = sdkClasses.get(childClassName)!; 329 - generateClass(childClass); 330 - 331 - const refChildClass = plugin.referenceSymbol({ 332 - category: 'utility', 333 - resource: 'class', 334 - resourceId: childClass.className, 335 - tool: 'sdk', 336 - }); 337 - 338 - const originalMemberName = stringCase({ 339 - case: 'camelCase', 340 - value: refChildClass.meta!.resourceId!, 341 - }); 342 - // avoid collisions with existing method names 343 - let memberName = originalMemberName; 344 - if (currentClass.methods.has(memberName)) { 345 - let index = 2; 346 - let attempt = `${memberName}${index}`; 347 - while (currentClass.methods.has(attempt)) { 348 - attempt = `${memberName}${index++}`; 349 - } 350 - memberName = attempt; 351 - } 352 - currentClass.methods.add(memberName); 353 - 354 - if (currentClass.nodes.length > 0) { 355 - currentClass.nodes.push($.newline()); 356 - } 357 - 358 - if (plugin.config.instance) { 359 - const privateName = plugin.symbol(`_${memberName}`); 360 - const privateNode = $.field(privateName, (f) => 361 - f.private().optional().type(refChildClass), 362 - ); 363 - currentClass.nodes.push(privateNode); 364 - const getterNode = $.getter(memberName, (g) => 365 - g.returns(refChildClass).do( 366 - $('this') 367 - .attr(privateName) 368 - .nullishAssign( 369 - $.new(refChildClass).args( 370 - $.object().prop('client', $('this').attr('client')), 371 - ), 372 - ) 373 - .return(), 374 - ), 375 - ); 376 - currentClass.nodes.push(getterNode); 377 - } else { 378 - const subClassReferenceNode = plugin.isSymbolRegistered( 379 - refChildClass.id, 380 - ) 381 - ? $.field(memberName, (f) => f.static().assign($(refChildClass))) 382 - : $.getter(memberName, (g) => 383 - g.public().static().do($.return(refChildClass)), 384 - ); 385 - currentClass.nodes.push(subClassReferenceNode); 386 - } 387 - } 388 - 389 - const symbol = plugin.symbol(resourceId, { 390 - meta: { 391 - category: 'utility', 392 - resource: 'class', 393 - resourceId, 394 - tool: 'sdk', 395 - }, 396 - }); 397 - 398 - if (currentClass.root && registryIndex !== undefined) { 399 - const symClient = plugin.getSymbol({ category: 'client' }); 400 - const isClientRequired = !plugin.config.client || !symClient; 401 - const symbolClient = plugin.referenceSymbol({ 402 - category: 'external', 403 - resource: 'client.Client', 404 - }); 405 - const ctor = $.init((i) => 406 - i 407 - .param('args', (p) => 408 - p.required(isClientRequired).type( 409 - $.type 410 - .object() 411 - .prop('client', (p) => 412 - p.required(isClientRequired).type(symbolClient), 413 - ) 414 - .prop('key', (p) => p.optional().type('string')), 415 - ), 416 - ) 417 - .do( 418 - $('super').call('args'), 419 - $(symbol) 420 - .attr(registryName) 421 - .attr('set') 422 - .call('this', $('args').attr('key').required(isClientRequired)), 423 - ), 424 - ); 425 - 426 - if (!currentClass.nodes.length) { 427 - currentClass.nodes.unshift(ctor); 428 - } else { 429 - currentClass.nodes.unshift(ctor, $.newline()); 430 - } 431 - 432 - const symbolRegistry = plugin.symbol('HeyApiRegistry', { 433 - meta: { 434 - category: 'utility', 435 - resource: 'class', 436 - resourceId: 'HeyApiRegistry', 437 - tool: 'sdk', 438 - }, 439 - }); 440 - const node = createRegistryClass({ 441 - plugin, 442 - sdkSymbol: symbol, 443 - symbol: symbolRegistry, 444 - }); 445 - plugin.node(node, registryIndex); 446 - const registryNode = $.field(registryName, (f) => 447 - f 448 - .public() 449 - .static() 450 - .readonly() 451 - .assign($.new(symbolRegistry).generic(symbol)), 452 - ); 453 - currentClass.nodes.unshift(registryNode, $.newline()); 454 - } 455 - 456 - const node = $.class(symbol) 457 - .export() 458 - .extends(symbolClient) 459 - .$if(isAngularClient && currentClass.root, (c) => 460 - c.decorator( 461 - plugin.referenceSymbol({ 462 - category: 'external', 463 - resource: '@angular/core.Injectable', 464 - }), 465 - $.object().prop('providedIn', $.literal('root')), 466 - ), 467 - ) 468 - .do(...currentClass.nodes); 469 - plugin.node(node); 470 - }; 471 - 472 - for (const sdkClass of sdkClasses.values()) { 473 - generateClass(sdkClass); 474 - } 475 - };
-107
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/functions.ts
··· 1 - import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 2 - import { 3 - createOperationComment, 4 - isOperationOptionsRequired, 5 - } from '~/plugins/shared/utils/operation'; 6 - import { $ } from '~/ts-dsl'; 7 - 8 - import type { HeyApiSdkPlugin } from '../types'; 9 - import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; 10 - import { 11 - operationMethodName, 12 - operationParameters, 13 - operationStatements, 14 - } from './operation'; 15 - 16 - export const generateFlatSdk = ({ 17 - plugin, 18 - }: { 19 - plugin: HeyApiSdkPlugin['Instance']; 20 - }): void => { 21 - const client = getClientPlugin(plugin.context.config); 22 - const isNuxtClient = client.name === '@hey-api/client-nuxt'; 23 - 24 - plugin.forEach( 25 - 'operation', 26 - (event) => { 27 - const { operation } = event; 28 - const isRequiredOptions = isOperationOptionsRequired({ 29 - context: plugin.context, 30 - operation, 31 - }); 32 - const symbolResponse = isNuxtClient 33 - ? plugin.querySymbol({ 34 - category: 'type', 35 - resource: 'operation', 36 - resourceId: operation.id, 37 - role: 'response', 38 - }) 39 - : undefined; 40 - const opParameters = operationParameters({ 41 - isRequiredOptions, 42 - operation, 43 - plugin, 44 - }); 45 - const statements = operationStatements({ 46 - isRequiredOptions, 47 - opParameters, 48 - operation, 49 - plugin, 50 - }); 51 - const symbol = plugin.symbol(operationMethodName({ operation, plugin }), { 52 - meta: { 53 - category: 'sdk', 54 - path: event._path, 55 - resource: 'operation', 56 - resourceId: operation.id, 57 - tags: event.tags, 58 - tool: 'sdk', 59 - }, 60 - }); 61 - const node = $.const(symbol) 62 - .export() 63 - .$if(createOperationComment(operation), (c, v) => c.doc(v)) 64 - .assign( 65 - $.func() 66 - .params(...opParameters.parameters) 67 - .$if( 68 - isNuxtClient, 69 - (f) => 70 - f 71 - .generic(nuxtTypeComposable, (g) => 72 - g 73 - .extends( 74 - plugin.referenceSymbol({ 75 - category: 'external', 76 - resource: 'client.Composable', 77 - }), 78 - ) 79 - .default($.type.literal('$fetch')), 80 - ) 81 - .generic(nuxtTypeDefault, (g) => 82 - g.$if( 83 - symbolResponse, 84 - (t, s) => t.extends(s).default(s), 85 - (t) => t.default('undefined'), 86 - ), 87 - ), 88 - (f) => 89 - f.generic('ThrowOnError', (g) => 90 - g 91 - .extends('boolean') 92 - .default( 93 - ('throwOnError' in client.config 94 - ? client.config.throwOnError 95 - : false) ?? false, 96 - ), 97 - ), 98 - ) 99 - .do(...statements), 100 - ); 101 - plugin.node(node); 102 - }, 103 - { 104 - order: 'declarations', 105 - }, 106 - ); 107 - };
+2 -96
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/operation.ts
··· 3 3 4 4 import { statusCodeToGroup } from '~/ir/operation'; 5 5 import type { IR } from '~/ir/types'; 6 - import { sanitizeNamespaceIdentifier } from '~/openApi/common/parser/sanitize'; 7 6 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 8 7 import { $ } from '~/ts-dsl'; 9 - import { stringCase } from '~/utils/stringCase'; 10 8 11 9 import type { Field, Fields } from '../../client-core/bundle/params'; 12 10 import type { HeyApiSdkPlugin } from '../types'; 11 + import { isInstance } from '../v1/node'; 13 12 import { operationAuth } from './auth'; 14 13 import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; 15 14 import { getSignatureParameters } from './signature'; 16 15 import { createRequestValidator, createResponseValidator } from './validator'; 17 - 18 - interface ClassNameEntry { 19 - /** 20 - * Name of the class where this function appears. 21 - */ 22 - className: string; 23 - /** 24 - * Name of the function within the class. 25 - */ 26 - methodName: string; 27 - /** 28 - * JSONPath-like array to class location. 29 - */ 30 - path: ReadonlyArray<string>; 31 - } 32 - 33 - const operationClassName = ({ 34 - plugin, 35 - value, 36 - }: { 37 - plugin: HeyApiSdkPlugin['Instance']; 38 - value: string; 39 - }) => { 40 - const name = stringCase({ case: 'PascalCase', value }); 41 - return ( 42 - (typeof plugin.config.classNameBuilder === 'string' 43 - ? plugin.config.classNameBuilder.replace('{{name}}', name) 44 - : plugin.config.classNameBuilder(name)) || name 45 - ); 46 - }; 47 - 48 - export const operationMethodName = ({ 49 - operation, 50 - plugin, 51 - }: { 52 - operation: IR.OperationObject; 53 - plugin: HeyApiSdkPlugin['Instance']; 54 - }) => plugin.config.methodNameBuilder?.(operation) || operation.id; 55 - 56 - /** 57 - * Returns a list of classes where this operation appears in the generated SDK. 58 - */ 59 - export const operationClasses = ({ 60 - operation, 61 - plugin, 62 - }: { 63 - operation: IR.OperationObject; 64 - plugin: HeyApiSdkPlugin['Instance']; 65 - }): Map<string, ClassNameEntry> => { 66 - const classNames = new Map<string, ClassNameEntry>(); 67 - 68 - let className: string | undefined; 69 - let methodName: string | undefined; 70 - let classCandidates: Array<string> = []; 71 - 72 - if (plugin.config.classStructure === 'auto' && operation.operationId) { 73 - classCandidates = operation.operationId.split(/[./]/).filter(Boolean); 74 - if (classCandidates.length >= 2) { 75 - const methodCandidate = classCandidates.pop()!; 76 - methodName = stringCase({ 77 - case: 'camelCase', 78 - value: sanitizeNamespaceIdentifier(methodCandidate), 79 - }); 80 - className = classCandidates.pop()!; 81 - } 82 - } 83 - 84 - const rootClasses = plugin.config.instance 85 - ? [plugin.config.instance] 86 - : (operation.tags ?? ['default']); 87 - 88 - for (const rootClass of rootClasses) { 89 - // Default path 90 - let path = [rootClass]; 91 - if (className) { 92 - // If root class is already within classCandidates or the same as className 93 - // do not add it again as this will cause a recursion issue. 94 - if (classCandidates.includes(rootClass) || rootClass === className) { 95 - path = [...classCandidates, className]; 96 - } else { 97 - path = [rootClass, ...classCandidates, className]; 98 - } 99 - } 100 - 101 - classNames.set(rootClass, { 102 - className: operationClassName({ plugin, value: className || rootClass }), 103 - methodName: methodName || operationMethodName({ operation, plugin }), 104 - path: path.map((value) => operationClassName({ plugin, value })), 105 - }); 106 - } 107 - 108 - return classNames; 109 - }; 110 16 111 17 /** TODO: needs complete refactor */ 112 18 export const operationOptionsType = ({ ··· 564 470 565 471 let clientExpression: ReturnType<typeof $.attr | typeof $.binary>; 566 472 const optionsClient = $('options').attr('client').required(isRequiredOptions); 567 - if (plugin.config.instance) { 473 + if (isInstance(plugin)) { 568 474 clientExpression = optionsClient.coalesce($('this').attr('client')); 569 475 } else if (symbolClient) { 570 476 clientExpression = optionsClient.coalesce(symbolClient);
+3 -3
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/signature.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { PluginInstance } from '~/plugins/shared/utils/instance'; 3 + import { toCase } from '~/utils/naming'; 3 4 import { refToName } from '~/utils/ref'; 4 - import { stringCase } from '~/utils/stringCase'; 5 5 6 6 import type { Field } from '../../client-core/bundle/params'; 7 7 ··· 83 83 } else if (operation.body.schema.$ref) { 84 84 // alias body for more ergonomic naming, e.g. user if the type is User 85 85 const name = refToName(operation.body.schema.$ref); 86 - const key = stringCase({ case: 'camelCase', value: name }); 86 + const key = toCase(name, 'camelCase'); 87 87 addParameter(key, 'body'); 88 88 } else { 89 89 addParameter('body', 'body'); ··· 157 157 } 158 158 } else if (operation.body.schema.$ref) { 159 159 const value = refToName(operation.body.schema.$ref); 160 - const originalName = stringCase({ case: 'camelCase', value }); 160 + const originalName = toCase(value, 'camelCase'); 161 161 const name = conflicts.has(originalName) 162 162 ? `${location}_${originalName}` 163 163 : originalName;
+6 -9
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/typeOptions.ts
··· 3 3 import { $ } from '~/ts-dsl'; 4 4 5 5 import type { HeyApiSdkPlugin } from '../types'; 6 + import { isInstance } from '../v1/node'; 6 7 import { nuxtTypeDefault, nuxtTypeResponse } from './constants'; 7 8 8 9 export const createTypeOptions = ({ ··· 14 15 const client = getClientPlugin(plugin.context.config); 15 16 const isNuxtClient = client.name === '@hey-api/client-nuxt'; 16 17 17 - const symbolTDataShape = plugin.registerSymbol({ 18 + const symbolTDataShape = plugin.symbol('TDataShape', { 18 19 external: clientModule, 19 20 kind: 'type', 20 - name: 'TDataShape', 21 21 }); 22 - const symbolClient = plugin.registerSymbol({ 22 + const symbolClient = plugin.symbol('Client', { 23 23 external: clientModule, 24 24 kind: 'type', 25 25 meta: { ··· 27 27 resource: 'client.Client', 28 28 tool: client.name, 29 29 }, 30 - name: 'Client', 31 30 }); 32 - const symbolClientOptions = plugin.registerSymbol({ 31 + const symbolClientOptions = plugin.symbol('Options', { 33 32 external: clientModule, 34 33 kind: 'type', 35 - name: 'Options', 36 34 }); 37 - const symbolOptions = plugin.registerSymbol({ 35 + const symbolOptions = plugin.symbol('Options', { 38 36 meta: { 39 37 category: 'type', 40 38 resource: 'client-options', 41 39 tool: 'sdk', 42 40 }, 43 - name: 'Options', 44 41 }); 45 42 46 43 const typeOptions = $.type ··· 95 92 'individual options. This might be also useful if you want to implement a', 96 93 'custom client.', 97 94 ]) 98 - .required(!plugin.config.client && !plugin.config.instance) 95 + .required(!plugin.config.client && !isInstance(plugin)) 99 96 .type(symbolClient), 100 97 ) 101 98 .prop('meta', (p) =>
+66 -120
packages/openapi-ts/src/plugins/@hey-api/sdk/types.d.ts
··· 1 - import type { IR } from '~/ir/types'; 1 + import type { OperationsStrategy } from '~/openApi/shared/locations'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 3 import type { PluginClientNames, PluginValidatorNames } from '~/plugins/types'; 4 - import type { StringName } from '~/types/case'; 4 + import type { NameTransformer } from '~/utils/naming'; 5 + 6 + import type { OperationsConfig, UserOperationsConfig } from './operations'; 5 7 6 8 export type UserConfig = Plugin.Name<'@hey-api/sdk'> & 7 9 Plugin.Hooks & { 8 10 /** 9 - * Group operation methods into classes? When enabled, you can select which 10 - * classes to export with `sdk.include` and/or transform their names with 11 - * `sdk.classNameBuilder`. 12 - * 13 - * Note that by enabling this option, your SDKs will **NOT** 14 - * support {@link https://developer.mozilla.org/docs/Glossary/Tree_shaking tree-shaking}. 15 - * For this reason, it is disabled by default. 16 - * 17 - * @default false 18 - */ 19 - asClass?: boolean; 20 - /** 21 11 * Should the generated functions contain auth mechanisms? You may want to 22 12 * disable this option if you're handling auth yourself or defining it 23 13 * globally on the client and want to reduce the size of generated code. ··· 26 16 */ 27 17 auth?: boolean; 28 18 /** 29 - * Customize the generated class names. The name variable is obtained from 30 - * your OpenAPI specification tags or `instance` value. 31 - * 32 - * This option has no effect if `sdk.asClass` is `false`. 33 - */ 34 - classNameBuilder?: StringName; 35 - /** 36 - * How should we structure your SDK? By default, we try to infer the ideal 37 - * structure using `operationId` keywords. If you prefer a flatter structure, 38 - * you can set `classStructure` to `off` to disable this behavior. 39 - * 40 - * @default 'auto' 41 - */ 42 - classStructure?: 'auto' | 'off'; 43 - /** 44 19 * Use an internal client instance to send HTTP requests? This is useful if 45 20 * you don't want to manually pass the client to each SDK function. 46 21 * ··· 60 35 */ 61 36 exportFromIndex?: boolean; 62 37 /** 63 - * Include only service classes with names matching regular expression 38 + * Define the structure of generated SDK operations. 64 39 * 65 - * This option has no effect if `sdk.asClass` is `false`. 66 - */ 67 - include?: string; 68 - /** 69 - * Set `instance` to create an instantiable SDK. Using `true` will use the 70 - * default instance name; in practice, you want to define your own by passing 71 - * a string value. 40 + * String shorthand: 41 + * - `'byTags'` – one container per operation tag 42 + * - `'flat'` – standalone functions, no container 43 + * - `'single'` – all operations in a single container 44 + * - custom function for full control 72 45 * 73 - * @default false 74 - */ 75 - instance?: string | boolean; 76 - /** 77 - * Customise the name of methods within the service. By default, 78 - * {@link IR.OperationObject.id} is used. 79 - */ 80 - methodNameBuilder?: (operation: IR.OperationObject) => string; 81 - // TODO: parser - rename operationId option to something like inferId?: boolean 82 - /** 83 - * Use operation ID to generate operation names? 46 + * Use the object form for advanced configuration. 84 47 * 85 - * @default true 48 + * @default 'flat' 86 49 */ 87 - operationId?: boolean; 50 + operations?: OperationsStrategy | UserOperationsConfig; 88 51 /** 89 52 * Define how request parameters are structured in generated SDK methods. 90 53 * ··· 161 124 // DEPRECATED OPTIONS BELOW 162 125 163 126 /** 164 - * **This feature works only with the legacy parser** 165 - * 166 - * Filter endpoints to be included in the generated SDK. The provided 167 - * string should be a regular expression where matched results will be 168 - * included in the output. The input pattern this string will be tested 169 - * against is `{method} {path}`. For example, you can match 170 - * `POST /api/v1/foo` with `^POST /api/v1/foo$`. 171 - * 172 - * @deprecated 173 - */ 174 - // eslint-disable-next-line typescript-sort-keys/interface 175 - filter?: string; 176 - /** 177 - * Define shape of returned value from service calls 178 - * 179 - * @deprecated 180 - * @default 'body' 181 - */ 182 - response?: 'body' | 'response'; 183 - }; 184 - 185 - export type Config = Plugin.Name<'@hey-api/sdk'> & 186 - Plugin.Hooks & { 187 - /** 188 127 * Group operation methods into classes? When enabled, you can select which 189 128 * classes to export with `sdk.include` and/or transform their names with 190 129 * `sdk.classNameBuilder`. ··· 193 132 * support {@link https://developer.mozilla.org/docs/Glossary/Tree_shaking tree-shaking}. 194 133 * For this reason, it is disabled by default. 195 134 * 135 + * @deprecated Use `operations: { strategy: "byTags" }` or `operations: { strategy: "single" }` instead. 196 136 * @default false 197 137 */ 198 - asClass: boolean; 199 - /** 200 - * Should the generated functions contain auth mechanisms? You may want to 201 - * disable this option if you're handling auth yourself or defining it 202 - * globally on the client and want to reduce the size of generated code. 203 - * 204 - * @default true 205 - */ 206 - auth: boolean; 138 + // eslint-disable-next-line typescript-sort-keys/interface 139 + asClass?: boolean; 207 140 /** 208 141 * Customize the generated class names. The name variable is obtained from 209 142 * your OpenAPI specification tags or `instance` value. 210 143 * 211 144 * This option has no effect if `sdk.asClass` is `false`. 145 + * 146 + * @deprecated Use `operations: { containerName: "..." }` instead. 212 147 */ 213 - classNameBuilder: StringName; 148 + classNameBuilder?: NameTransformer; 214 149 /** 215 150 * How should we structure your SDK? By default, we try to infer the ideal 216 151 * structure using `operationId` keywords. If you prefer a flatter structure, 217 152 * you can set `classStructure` to `off` to disable this behavior. 218 153 * 154 + * @deprecated Use `operations: { nesting: "operationId" }` or `operations: { nesting: "id" }` instead. 219 155 * @default 'auto' 220 156 */ 221 - classStructure: 'auto' | 'off'; 157 + classStructure?: 'auto' | 'off'; 158 + /** 159 + * Set `instance` to create an instantiable SDK. Using `true` will use the 160 + * default instance name; in practice, you want to define your own by passing 161 + * a string value. 162 + * 163 + * @deprecated Use `operations: { strategy: "single", containerName: "Name", methods: "instance" }` instead. 164 + * @default false 165 + */ 166 + instance?: string | boolean; 167 + /** 168 + * Customise the name of methods within the service. By default, 169 + * `operation.id` is used. 170 + * 171 + * @deprecated Use `operations: { methodName: "..." }` instead. 172 + */ 173 + methodNameBuilder?: NameTransformer; 174 + /** 175 + * Use operation ID to generate operation names? 176 + * 177 + * @deprecated Use `operations: { nesting: "operationId" }` or `operations: { nesting: "id" }` instead. 178 + * @default true 179 + */ 180 + operationId?: boolean; 181 + /** 182 + * Define shape of returned value from service calls 183 + * 184 + * @deprecated 185 + * @default 'body' 186 + */ 187 + response?: 'body' | 'response'; 188 + }; 189 + 190 + export type Config = Plugin.Name<'@hey-api/sdk'> & 191 + Plugin.Hooks & { 192 + /** 193 + * Should the generated functions contain auth mechanisms? You may want to 194 + * disable this option if you're handling auth yourself or defining it 195 + * globally on the client and want to reduce the size of generated code. 196 + * 197 + * @default true 198 + */ 199 + auth: boolean; 222 200 /** 223 201 * Use an internal client instance to send HTTP requests? This is useful if 224 202 * you don't want to manually pass the client to each SDK function. ··· 239 217 */ 240 218 exportFromIndex: boolean; 241 219 /** 242 - * Include only service classes with names matching regular expression 243 - * 244 - * This option has no effect if `sdk.asClass` is `false`. 245 - */ 246 - include: string | undefined; 247 - /** 248 - * Set `instance` to create an instantiable SDK. Using `true` will use the 249 - * default instance name; in practice, you want to define your own by passing 250 - * a string value. 251 - */ 252 - instance: string; 253 - /** 254 - * Customise the name of methods within the service. By default, 255 - * {@link IR.OperationObject.id} is used. 256 - */ 257 - methodNameBuilder?: (operation: IR.OperationObject) => string; 258 - // TODO: parser - rename operationId option to something like inferId?: boolean 259 - /** 260 - * Use operation ID to generate operation names? 261 - * 262 - * @default true 220 + * Define the structure of generated SDK operations. 263 221 */ 264 - operationId: boolean; 222 + operations: OperationsConfig; 265 223 /** 266 224 * Define how request parameters are structured in generated SDK methods. 267 225 * ··· 317 275 // DEPRECATED OPTIONS BELOW 318 276 319 277 /** 320 - * **This feature works only with the legacy parser** 321 - * 322 - * Filter endpoints to be included in the generated SDK. The provided 323 - * string should be a regular expression where matched results will be 324 - * included in the output. The input pattern this string will be tested 325 - * against is `{method} {path}`. For example, you can match 326 - * `POST /api/v1/foo` with `^POST /api/v1/foo$`. 327 - * 328 - * @deprecated 329 - */ 330 - // eslint-disable-next-line typescript-sort-keys/interface 331 - filter?: string; 332 - /** 333 278 * Define shape of returned value from service calls 334 279 * 335 280 * @deprecated 336 281 * @default 'body' 337 282 */ 283 + // eslint-disable-next-line typescript-sort-keys/interface 338 284 response: 'body' | 'response'; 339 285 }; 340 286
+383
packages/openapi-ts/src/plugins/@hey-api/sdk/v1/node.ts
··· 1 + import type { 2 + StructureItem, 3 + StructureNode, 4 + StructureShell, 5 + Symbol, 6 + SymbolMeta, 7 + } from '@hey-api/codegen-core'; 8 + 9 + import type { IR } from '~/ir/types'; 10 + import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 11 + import { 12 + createOperationComment, 13 + isOperationOptionsRequired, 14 + } from '~/plugins/shared/utils/operation'; 15 + import { $ } from '~/ts-dsl'; 16 + import { applyNaming, toCase } from '~/utils/naming'; 17 + 18 + import { createClientClass, createRegistryClass } from '../shared/class'; 19 + import { nuxtTypeComposable, nuxtTypeDefault } from '../shared/constants'; 20 + import { operationParameters, operationStatements } from '../shared/operation'; 21 + import type { HeyApiSdkPlugin } from '../types'; 22 + 23 + export interface OperationItem { 24 + operation: IR.OperationObject; 25 + path: ReadonlyArray<string | number>; 26 + tags: ReadonlyArray<string> | undefined; 27 + } 28 + 29 + export const source = globalThis.Symbol('@hey-api/sdk'); 30 + 31 + export function isInstance(plugin: HeyApiSdkPlugin['Instance']): boolean { 32 + const config = plugin.config.operations; 33 + return ( 34 + config.container === 'class' && 35 + config.methods === 'instance' && 36 + config.strategy !== 'flat' 37 + ); 38 + } 39 + 40 + function attachComment< 41 + T extends ReturnType<typeof $.var | typeof $.method>, 42 + >(args: { node: T; operation: IR.OperationObject }): T { 43 + const { node, operation } = args; 44 + return node.$if(createOperationComment(operation), (n, v) => n.doc(v)) as T; 45 + } 46 + 47 + function createShellMeta(node: StructureNode): SymbolMeta { 48 + return { 49 + category: 'utility', 50 + resource: 'class', 51 + resourceId: node.getPath().join('.'), 52 + tool: 'sdk', 53 + }; 54 + } 55 + 56 + function createFnSymbol( 57 + plugin: HeyApiSdkPlugin['Instance'], 58 + item: StructureItem & { data: OperationItem }, 59 + ): Symbol { 60 + const { operation, path, tags } = item.data; 61 + const name = item.location[item.location.length - 1]!; 62 + return plugin.symbol(applyNaming(name, plugin.config.operations.methodName), { 63 + meta: { 64 + category: 'sdk', 65 + path, 66 + resource: 'operation', 67 + resourceId: operation.id, 68 + tags, 69 + tool: 'sdk', 70 + }, 71 + }); 72 + } 73 + 74 + function childToNode( 75 + resource: StructureNode, 76 + plugin: HeyApiSdkPlugin['Instance'], 77 + ): ReadonlyArray<ReturnType<typeof $.field | typeof $.getter>> { 78 + const refChild = plugin.referenceSymbol(createShellMeta(resource)); 79 + const memberNameStr = toCase( 80 + refChild.name, 81 + plugin.config.operations.methodName.casing ?? 'camelCase', 82 + ); 83 + const memberName = plugin.symbol(memberNameStr); 84 + if (isInstance(plugin)) { 85 + const privateName = plugin.symbol(`_${memberNameStr}`); 86 + return [ 87 + $.field(privateName, (f) => f.private().optional().type(refChild)), 88 + $.getter(memberName, (g) => 89 + g.returns(refChild).do( 90 + $('this') 91 + .attr(privateName) 92 + .nullishAssign( 93 + $.new(refChild).args( 94 + $.object().prop('client', $('this').attr('client')), 95 + ), 96 + ) 97 + .return(), 98 + ), 99 + ), 100 + ]; 101 + } 102 + if (plugin.isSymbolRegistered(refChild.id)) { 103 + return [$.field(memberName, (f) => f.static().assign($(refChild)))]; 104 + } 105 + return [ 106 + $.getter(memberName, (g) => g.public().static().do($.return(refChild))), 107 + ]; 108 + } 109 + 110 + export function createShell( 111 + plugin: HeyApiSdkPlugin['Instance'], 112 + ): StructureShell { 113 + const client = getClientPlugin(plugin.context.config); 114 + const isAngularClient = client.name === '@hey-api/client-angular'; 115 + return { 116 + define: (node) => { 117 + const symbol = plugin.symbol( 118 + applyNaming( 119 + node.name, 120 + node.isRoot 121 + ? plugin.config.operations.containerName 122 + : plugin.config.operations.segmentName, 123 + ), 124 + { 125 + meta: createShellMeta(node), 126 + }, 127 + ); 128 + 129 + // if (plugin.config.operations.container === 'object') { 130 + // const o = $.const(symbol).export().assign($.object()); 131 + // return { dependencies: [], node: o }; 132 + // } 133 + 134 + const c = $.class(symbol) 135 + .export() 136 + .$if(isInstance(plugin), (c) => 137 + c.extends( 138 + plugin.referenceSymbol({ 139 + category: 'utility', 140 + resource: 'class', 141 + resourceId: 'HeyApiClient', 142 + tool: 'sdk', 143 + }), 144 + ), 145 + ) 146 + .$if(isAngularClient && node.isRoot, (c) => 147 + c.decorator( 148 + plugin.external('@angular/core.Injectable'), 149 + $.object().prop('providedIn', $.literal('root')), 150 + ), 151 + ); 152 + 153 + const dependencies: Array<ReturnType<typeof $.class>> = []; 154 + 155 + if (node.isRoot && isInstance(plugin)) { 156 + enrichRootClass({ 157 + dependencies, 158 + node: c, 159 + plugin, 160 + symbol, 161 + }); 162 + } 163 + 164 + return { dependencies, node: c }; 165 + }, 166 + }; 167 + } 168 + 169 + function enrichRootClass(args: { 170 + dependencies: Array<ReturnType<typeof $.class>>; 171 + node: ReturnType<typeof $.class>; 172 + plugin: HeyApiSdkPlugin['Instance']; 173 + symbol: Symbol; 174 + }): void { 175 + const { dependencies, node, plugin, symbol } = args; 176 + const symbolClient = plugin.symbol('HeyApiClient', { 177 + meta: { 178 + category: 'utility', 179 + resource: 'class', 180 + resourceId: 'HeyApiClient', 181 + tool: 'sdk', 182 + }, 183 + }); 184 + dependencies.push(createClientClass({ plugin, symbol: symbolClient })); 185 + const symbolRegistry = plugin.symbol('HeyApiRegistry', { 186 + meta: { 187 + category: 'utility', 188 + resource: 'class', 189 + resourceId: 'HeyApiRegistry', 190 + tool: 'sdk', 191 + }, 192 + }); 193 + dependencies.push( 194 + createRegistryClass({ 195 + plugin, 196 + sdkSymbol: symbol, 197 + symbol: symbolRegistry, 198 + }), 199 + ); 200 + const isClientRequired = 201 + !plugin.config.client || !plugin.getSymbol({ category: 'client' }); 202 + const registry = plugin.symbol('__registry'); 203 + node.toAccessNode = (node, options) => { 204 + if (options.context) return; 205 + return $(node.name).attr(registry).attr('get').call(); 206 + }; 207 + node.do( 208 + $.field(registry, (f) => 209 + f 210 + .public() 211 + .static() 212 + .readonly() 213 + .assign($.new(symbolRegistry).generic(symbol)), 214 + ), 215 + $.newline(), 216 + $.init((i) => 217 + i 218 + .param('args', (p) => 219 + p.required(isClientRequired).type( 220 + $.type 221 + .object() 222 + .prop('client', (p) => 223 + p 224 + .required(isClientRequired) 225 + .type(plugin.external('client.Client')), 226 + ) 227 + .prop('key', (p) => p.optional().type('string')), 228 + ), 229 + ) 230 + .do( 231 + $('super').call('args'), 232 + $(symbol) 233 + .attr(registry) 234 + .attr('set') 235 + .call('this', $('args').attr('key').required(isClientRequired)), 236 + ), 237 + ), 238 + ); 239 + } 240 + 241 + function implementFn< 242 + T extends ReturnType<typeof $.func | typeof $.method>, 243 + >(args: { 244 + node: T; 245 + operation: IR.OperationObject; 246 + plugin: HeyApiSdkPlugin['Instance']; 247 + }): T { 248 + const { node, operation, plugin } = args; 249 + const client = getClientPlugin(plugin.context.config); 250 + const isNuxtClient = client.name === '@hey-api/client-nuxt'; 251 + const isRequiredOptions = isOperationOptionsRequired({ 252 + context: plugin.context, 253 + operation, 254 + }); 255 + const opParameters = operationParameters({ 256 + isRequiredOptions, 257 + operation, 258 + plugin, 259 + }); 260 + const statements = operationStatements({ 261 + isRequiredOptions, 262 + opParameters, 263 + operation, 264 + plugin, 265 + }); 266 + return node 267 + .$if( 268 + isNuxtClient, 269 + (m) => 270 + m 271 + .generic(nuxtTypeComposable, (t) => 272 + t 273 + .extends(plugin.external('client.Composable')) 274 + .default($.type.literal('$fetch')), 275 + ) 276 + .generic(nuxtTypeDefault, (t) => 277 + t.$if( 278 + plugin.querySymbol({ 279 + category: 'type', 280 + resource: 'operation', 281 + resourceId: operation.id, 282 + role: 'response', 283 + }), 284 + (t, s) => t.extends(s).default(s), 285 + (t) => t.default('undefined'), 286 + ), 287 + ), 288 + (m) => 289 + m.generic('ThrowOnError', (t) => 290 + t 291 + .extends('boolean') 292 + .default( 293 + ('throwOnError' in client.config 294 + ? client.config.throwOnError 295 + : false) ?? false, 296 + ), 297 + ), 298 + ) 299 + .params(...opParameters.parameters) 300 + .do(...statements) as T; 301 + } 302 + 303 + export function toNode( 304 + model: StructureNode, 305 + plugin: HeyApiSdkPlugin['Instance'], 306 + ): { 307 + dependencies?: Array<ReturnType<typeof $.class | typeof $.var>>; 308 + nodes: ReadonlyArray<ReturnType<typeof $.class | typeof $.var>>; 309 + } { 310 + if (model.virtual) { 311 + const nodes: Array<ReturnType<typeof $.var>> = []; 312 + for (const item of model.itemsFrom<OperationItem>(source)) { 313 + const { operation } = item.data; 314 + let node = $.const(createFnSymbol(plugin, item)) 315 + .export() 316 + .assign( 317 + implementFn({ 318 + node: $.func(), 319 + operation, 320 + plugin, 321 + }), 322 + ); 323 + node = attachComment({ node, operation }); 324 + nodes.push(node); 325 + } 326 + return { nodes }; 327 + } 328 + 329 + if (!model.shell) { 330 + return { nodes: [] }; 331 + } 332 + 333 + const nodes: Array<ReturnType<typeof $.class | typeof $.var>> = []; 334 + 335 + const client = getClientPlugin(plugin.context.config); 336 + const isAngularClient = client.name === '@hey-api/client-angular'; 337 + 338 + const shell = model.shell.define(model); 339 + const node = shell.node as ReturnType<typeof $.class | typeof $.var>; 340 + 341 + let index = 0; 342 + for (const item of model.itemsFrom<OperationItem>(source)) { 343 + const { operation } = item.data; 344 + if (node['~dsl'] === 'VarTsDsl') { 345 + // TODO: object 346 + } else { 347 + if (index > 0 || node.hasBody) node.newline(); 348 + node.do( 349 + implementFn({ 350 + node: $.method(createFnSymbol(plugin, item), (m) => 351 + attachComment({ 352 + node: m, 353 + operation, 354 + }) 355 + .public() 356 + .static(!isAngularClient && !isInstance(plugin)), 357 + ), 358 + operation, 359 + plugin, 360 + }), 361 + ); 362 + } 363 + index += 1; 364 + } 365 + 366 + for (const child of model.children.values()) { 367 + if (node['~dsl'] === 'VarTsDsl') { 368 + // TODO: object 369 + } else { 370 + if (node.hasBody) node.newline(); 371 + node.do(...childToNode(child, plugin)); 372 + } 373 + } 374 + 375 + nodes.push(node); 376 + 377 + return { 378 + dependencies: shell.dependencies as Array< 379 + ReturnType<typeof $.class | typeof $.var> 380 + >, 381 + nodes, 382 + }; 383 + }
+54 -17
packages/openapi-ts/src/plugins/@hey-api/sdk/v1/plugin.ts
··· 1 + import { StructureModel } from '@hey-api/codegen-core'; 2 + 1 3 import { clientFolderAbsolutePath } from '~/generate/client'; 2 4 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 5 + import type { $ } from '~/ts-dsl'; 3 6 4 - import { generateClassSdk } from '../shared/class'; 5 - import { generateFlatSdk } from '../shared/functions'; 7 + import { resolveStrategy } from '../operations'; 6 8 import { createTypeOptions } from '../shared/typeOptions'; 7 9 import type { HeyApiSdkPlugin } from '../types'; 10 + import type { OperationItem } from './node'; 11 + import { createShell, source, toNode } from './node'; 8 12 9 13 export const handlerV1: HeyApiSdkPlugin['Handler'] = ({ plugin }) => { 10 14 const clientModule = clientFolderAbsolutePath(plugin.context.config); ··· 12 16 const isAngularClient = client.name === '@hey-api/client-angular'; 13 17 const isNuxtClient = client.name === '@hey-api/client-nuxt'; 14 18 15 - plugin.registerSymbol({ 19 + plugin.symbol('formDataBodySerializer', { 16 20 external: clientModule, 17 21 meta: { 18 22 category: 'external', 19 23 resource: 'client.formDataBodySerializer', 20 24 tool: client.name, 21 25 }, 22 - name: 'formDataBodySerializer', 23 26 }); 24 - plugin.registerSymbol({ 27 + plugin.symbol('urlSearchParamsBodySerializer', { 25 28 external: clientModule, 26 29 meta: { 27 30 category: 'external', 28 31 resource: 'client.urlSearchParamsBodySerializer', 29 32 tool: client.name, 30 33 }, 31 - name: 'urlSearchParamsBodySerializer', 32 34 }); 33 - plugin.registerSymbol({ 35 + plugin.symbol('buildClientParams', { 34 36 external: clientModule, 35 37 meta: { 36 38 category: 'external', 37 39 resource: 'client.buildClientParams', 38 40 tool: client.name, 39 41 }, 40 - name: 'buildClientParams', 41 42 }); 42 43 if (isNuxtClient) { 43 - plugin.registerSymbol({ 44 + plugin.symbol('Composable', { 44 45 external: clientModule, 45 46 kind: 'type', 46 47 meta: { ··· 48 49 resource: 'client.Composable', 49 50 tool: client.name, 50 51 }, 51 - name: 'Composable', 52 52 }); 53 53 } 54 - if (isAngularClient && plugin.config.asClass) { 55 - plugin.registerSymbol({ 54 + if (isAngularClient) { 55 + plugin.symbol('Injectable', { 56 56 external: '@angular/core', 57 57 meta: { 58 58 category: 'external', 59 59 resource: '@angular/core.Injectable', 60 60 }, 61 - name: 'Injectable', 62 61 }); 63 62 } 64 63 65 64 createTypeOptions({ plugin }); 66 65 67 - if (plugin.config.asClass) { 68 - generateClassSdk({ plugin }); 69 - } else { 70 - generateFlatSdk({ plugin }); 66 + const structure = new StructureModel(); 67 + const shell = createShell(plugin); 68 + const strategy = resolveStrategy(plugin); 69 + 70 + plugin.forEach( 71 + 'operation', 72 + (event) => { 73 + structure.insert({ 74 + data: { 75 + operation: event.operation, 76 + path: event._path, 77 + tags: event.tags, 78 + } satisfies OperationItem, 79 + locations: strategy(event.operation).map((path) => ({ path, shell })), 80 + source, 81 + }); 82 + }, 83 + { order: 'declarations' }, 84 + ); 85 + 86 + const allDependencies: Array<ReturnType<typeof $.class | typeof $.var>> = []; 87 + const allNodes: Array<ReturnType<typeof $.class | typeof $.var>> = []; 88 + 89 + for (const node of structure.walk()) { 90 + const { dependencies, nodes } = toNode(node, plugin); 91 + allDependencies.push(...(dependencies ?? [])); 92 + allNodes.push(...nodes); 93 + } 94 + 95 + const uniqueDependencies = new Map< 96 + number, 97 + ReturnType<typeof $.class | typeof $.var> 98 + >(); 99 + for (const dep of allDependencies) { 100 + if (dep.symbol) uniqueDependencies.set(dep.symbol.id, dep); 101 + } 102 + for (const dep of uniqueDependencies.values()) { 103 + plugin.node(dep); 104 + } 105 + 106 + for (const node of allNodes) { 107 + plugin.node(node); 71 108 } 72 109 };
+17 -21
packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts
··· 3 3 4 4 import { createOperationKey, operationResponsesMap } from '~/ir/operation'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import { $ } from '~/ts-dsl'; 7 + import { applyNaming } from '~/utils/naming'; 8 8 import { refToName } from '~/utils/ref'; 9 9 10 10 import type { HeyApiTransformersPlugin } from './types'; ··· 62 62 const symbol = 63 63 plugin.getSymbol(query) ?? 64 64 plugin.symbol( 65 - buildName({ 66 - config: { 67 - case: 'camelCase', 68 - name: '{{name}}SchemaResponseTransformer', 69 - }, 70 - name: refToName(schema.$ref), 65 + applyNaming(refToName(schema.$ref), { 66 + case: 'camelCase', 67 + name: '{{name}}SchemaResponseTransformer', 71 68 }), 72 69 { 73 70 meta: query, ··· 311 308 schema: response, 312 309 }); 313 310 if (!nodes.length) return; 314 - const symbol = plugin.registerSymbol({ 315 - meta: { 316 - category: 'transform', 317 - resource: 'operation', 318 - resourceId: operation.id, 319 - role: 'response', 311 + const symbol = plugin.symbol( 312 + applyNaming(operation.id, { 313 + case: 'camelCase', 314 + name: '{{name}}ResponseTransformer', 315 + }), 316 + { 317 + meta: { 318 + category: 'transform', 319 + resource: 'operation', 320 + resourceId: operation.id, 321 + role: 'response', 322 + }, 320 323 }, 321 - name: buildName({ 322 - config: { 323 - case: 'camelCase', 324 - name: '{{name}}ResponseTransformer', 325 - }, 326 - name: operation.id, 327 - }), 328 - }); 324 + ); 329 325 const value = $.const(symbol) 330 326 .export() 331 327 .assign(
+3 -6
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/clientOptions.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import { buildName } from '~/openApi/shared/utils/name'; 3 2 import { 4 3 getClientBaseUrlKey, 5 4 getClientPlugin, 6 5 } from '~/plugins/@hey-api/client-core/utils'; 7 6 import type { TypeTsDsl } from '~/ts-dsl'; 8 7 import { $ } from '~/ts-dsl'; 8 + import { applyNaming } from '~/utils/naming'; 9 9 import { parseUrl } from '~/utils/url'; 10 10 11 11 import type { HeyApiTypeScriptPlugin } from '../types'; ··· 50 50 } 51 51 52 52 const symbol = plugin.symbol( 53 - buildName({ 54 - config: { 55 - case: plugin.config.case, 56 - }, 57 - name: 'ClientOptions', 53 + applyNaming('ClientOptions', { 54 + case: plugin.config.case, 58 55 }), 59 56 { 60 57 meta: {
+50 -57
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/export.ts
··· 1 1 import { fromRef } from '@hey-api/codegen-core'; 2 2 3 3 import type { IR } from '~/ir/types'; 4 - import { buildName } from '~/openApi/shared/utils/name'; 5 4 import { createSchemaComment } from '~/plugins/shared/utils/schema'; 6 5 import type { MaybeTsDsl, TypeTsDsl } from '~/ts-dsl'; 7 6 import { $, regexp } from '~/ts-dsl'; 7 + import { applyNaming, toCase } from '~/utils/naming'; 8 8 import { pathToJsonPointer, refToName } from '~/utils/ref'; 9 - import { stringCase } from '~/utils/stringCase'; 10 9 11 10 import type { HeyApiTypeScriptPlugin } from '../types'; 12 11 import type { IrSchemaToAstOptions } from './types'; ··· 52 51 } 53 52 54 53 if (key) { 55 - key = stringCase({ 56 - case: plugin.config.enums.case, 54 + key = toCase(key, plugin.config.enums.case, { 57 55 stripLeadingSeparators: false, 58 - value: key, 59 56 }); 60 57 61 58 regexp.number.lastIndex = 0; ··· 108 105 ); 109 106 } 110 107 111 - const symbolObject = plugin.registerSymbol({ 112 - meta: { 113 - category: 'utility', 114 - path: fromRef(state.path), 115 - resource: 'definition', 116 - resourceId: $ref, 117 - tags: fromRef(state.tags), 118 - tool: 'typescript', 108 + const symbolObject = plugin.symbol( 109 + applyNaming(refToName($ref), plugin.config.definitions), 110 + { 111 + meta: { 112 + category: 'utility', 113 + path: fromRef(state.path), 114 + resource: 'definition', 115 + resourceId: $ref, 116 + tags: fromRef(state.tags), 117 + tool: 'typescript', 118 + }, 119 119 }, 120 - name: buildName({ 121 - config: plugin.config.definitions, 122 - name: refToName($ref), 123 - }), 124 - }); 120 + ); 125 121 const objectNode = $.const(symbolObject) 126 122 .export() 127 123 .$if(createSchemaComment(schema), (c, v) => c.doc(v)) ··· 136 132 ); 137 133 plugin.node(objectNode); 138 134 139 - const symbol = plugin.registerSymbol({ 140 - meta: { 141 - category: 'type', 142 - path: fromRef(state.path), 143 - resource: 'definition', 144 - resourceId: $ref, 145 - tags: fromRef(state.tags), 146 - tool: 'typescript', 135 + const symbol = plugin.symbol( 136 + applyNaming(refToName($ref), plugin.config.definitions), 137 + { 138 + meta: { 139 + category: 'type', 140 + path: fromRef(state.path), 141 + resource: 'definition', 142 + resourceId: $ref, 143 + tags: fromRef(state.tags), 144 + tool: 'typescript', 145 + }, 147 146 }, 148 - name: buildName({ 149 - config: plugin.config.definitions, 150 - name: refToName($ref), 151 - }), 152 - }); 147 + ); 153 148 const node = $.type 154 149 .alias(symbol) 155 150 .export() ··· 168 163 (type) => type !== 'number' && type !== 'string', 169 164 ); 170 165 if (shouldCreateTypeScriptEnum) { 171 - const symbol = plugin.registerSymbol({ 172 - meta: { 173 - category: 'type', 174 - path: fromRef(state.path), 175 - resource: 'definition', 176 - resourceId: $ref, 177 - tags: fromRef(state.tags), 178 - tool: 'typescript', 166 + const symbol = plugin.symbol( 167 + applyNaming(refToName($ref), plugin.config.definitions), 168 + { 169 + meta: { 170 + category: 'type', 171 + path: fromRef(state.path), 172 + resource: 'definition', 173 + resourceId: $ref, 174 + tags: fromRef(state.tags), 175 + tool: 'typescript', 176 + }, 179 177 }, 180 - name: buildName({ 181 - config: plugin.config.definitions, 182 - name: refToName($ref), 183 - }), 184 - }); 178 + ); 185 179 const enumNode = $.enum(symbol) 186 180 .export() 187 181 .$if(createSchemaComment(schema), (e, v) => e.doc(v)) ··· 199 193 } 200 194 } 201 195 202 - const symbol = plugin.registerSymbol({ 203 - meta: { 204 - category: 'type', 205 - path: fromRef(state.path), 206 - resource: 'definition', 207 - resourceId: $ref, 208 - tags: fromRef(state.tags), 209 - tool: 'typescript', 196 + const symbol = plugin.symbol( 197 + applyNaming(refToName($ref), plugin.config.definitions), 198 + { 199 + meta: { 200 + category: 'type', 201 + path: fromRef(state.path), 202 + resource: 'definition', 203 + resourceId: $ref, 204 + tags: fromRef(state.tags), 205 + tool: 'typescript', 206 + }, 210 207 }, 211 - name: buildName({ 212 - config: plugin.config.definitions, 213 - name: refToName($ref), 214 - }), 215 - }); 208 + ); 216 209 const node = $.type 217 210 .alias(symbol) 218 211 .export()
+72 -77
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts
··· 3 3 import { operationResponsesMap } from '~/ir/operation'; 4 4 import { deduplicateSchema } from '~/ir/schema'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import { $ } from '~/ts-dsl'; 7 + import { applyNaming } from '~/utils/naming'; 8 8 9 9 import { irSchemaToAst } from '../v1/plugin'; 10 10 import type { IrSchemaToAstOptions } from './types'; ··· 122 122 123 123 data.required = dataRequired; 124 124 125 - const symbol = plugin.registerSymbol({ 126 - meta: { 127 - category: 'type', 128 - path: fromRef(state.path), 129 - resource: 'operation', 130 - resourceId: operation.id, 131 - role: 'data', 132 - tags: fromRef(state.tags), 133 - tool: 'typescript', 125 + const symbol = plugin.symbol( 126 + applyNaming(operation.id, plugin.config.requests), 127 + { 128 + meta: { 129 + category: 'type', 130 + path: fromRef(state.path), 131 + resource: 'operation', 132 + resourceId: operation.id, 133 + role: 'data', 134 + tags: fromRef(state.tags), 135 + tool: 'typescript', 136 + }, 134 137 }, 135 - name: buildName({ 136 - config: plugin.config.requests, 137 - name: operation.id, 138 - }), 139 - }); 138 + ); 140 139 const node = $.type 141 140 .alias(symbol) 142 141 .export() ··· 163 162 operationResponsesMap(operation); 164 163 165 164 if (errors) { 166 - const symbolErrors = plugin.registerSymbol({ 167 - meta: { 168 - category: 'type', 169 - path: fromRef(state.path), 170 - resource: 'operation', 171 - resourceId: operation.id, 172 - role: 'errors', 173 - tags: fromRef(state.tags), 174 - tool: 'typescript', 165 + const symbolErrors = plugin.symbol( 166 + applyNaming(operation.id, plugin.config.errors), 167 + { 168 + meta: { 169 + category: 'type', 170 + path: fromRef(state.path), 171 + resource: 'operation', 172 + resourceId: operation.id, 173 + role: 'errors', 174 + tags: fromRef(state.tags), 175 + tool: 'typescript', 176 + }, 175 177 }, 176 - name: buildName({ 177 - config: plugin.config.errors, 178 - name: operation.id, 179 - }), 180 - }); 178 + ); 181 179 const node = $.type 182 180 .alias(symbolErrors) 183 181 .export() ··· 191 189 plugin.node(node); 192 190 193 191 if (error) { 194 - const symbol = plugin.registerSymbol({ 195 - meta: { 196 - category: 'type', 197 - path: fromRef(state.path), 198 - resource: 'operation', 199 - resourceId: operation.id, 200 - role: 'error', 201 - tags: fromRef(state.tags), 202 - tool: 'typescript', 192 + const symbol = plugin.symbol( 193 + applyNaming(operation.id, { 194 + case: plugin.config.errors.case, 195 + name: plugin.config.errors.error, 196 + }), 197 + { 198 + meta: { 199 + category: 'type', 200 + path: fromRef(state.path), 201 + resource: 'operation', 202 + resourceId: operation.id, 203 + role: 'error', 204 + tags: fromRef(state.tags), 205 + tool: 'typescript', 206 + }, 203 207 }, 204 - name: buildName({ 205 - config: { 206 - case: plugin.config.errors.case, 207 - name: plugin.config.errors.error, 208 - }, 209 - name: operation.id, 210 - }), 211 - }); 208 + ); 212 209 const node = $.type 213 210 .alias(symbol) 214 211 .export() ··· 218 215 } 219 216 220 217 if (responses) { 221 - const symbolResponses = plugin.registerSymbol({ 222 - meta: { 223 - category: 'type', 224 - path: fromRef(state.path), 225 - resource: 'operation', 226 - resourceId: operation.id, 227 - role: 'responses', 228 - tags: fromRef(state.tags), 229 - tool: 'typescript', 218 + const symbolResponses = plugin.symbol( 219 + applyNaming(operation.id, plugin.config.responses), 220 + { 221 + meta: { 222 + category: 'type', 223 + path: fromRef(state.path), 224 + resource: 'operation', 225 + resourceId: operation.id, 226 + role: 'responses', 227 + tags: fromRef(state.tags), 228 + tool: 'typescript', 229 + }, 230 230 }, 231 - name: buildName({ 232 - config: plugin.config.responses, 233 - name: operation.id, 234 - }), 235 - }); 231 + ); 236 232 const node = $.type 237 233 .alias(symbolResponses) 238 234 .export() ··· 246 242 plugin.node(node); 247 243 248 244 if (response) { 249 - const symbol = plugin.registerSymbol({ 250 - meta: { 251 - category: 'type', 252 - path: fromRef(state.path), 253 - resource: 'operation', 254 - resourceId: operation.id, 255 - role: 'response', 256 - tags: fromRef(state.tags), 257 - tool: 'typescript', 245 + const symbol = plugin.symbol( 246 + applyNaming(operation.id, { 247 + case: plugin.config.responses.case, 248 + name: plugin.config.responses.response, 249 + }), 250 + { 251 + meta: { 252 + category: 'type', 253 + path: fromRef(state.path), 254 + resource: 'operation', 255 + resourceId: operation.id, 256 + role: 'response', 257 + tags: fromRef(state.tags), 258 + tool: 'typescript', 259 + }, 258 260 }, 259 - name: buildName({ 260 - config: { 261 - case: plugin.config.responses.case, 262 - name: plugin.config.responses.response, 263 - }, 264 - name: operation.id, 265 - }), 266 - }); 261 + ); 267 262 const node = $.type 268 263 .alias(symbol) 269 264 .export()
+30 -32
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts
··· 2 2 import { fromRef } from '@hey-api/codegen-core'; 3 3 4 4 import type { IR } from '~/ir/types'; 5 - import { buildName } from '~/openApi/shared/utils/name'; 6 5 import { createSchemaComment } from '~/plugins/shared/utils/schema'; 7 6 import { $ } from '~/ts-dsl'; 7 + import { applyNaming } from '~/utils/naming'; 8 8 9 9 import { irSchemaToAst } from '../v1/plugin'; 10 10 import type { IrSchemaToAstOptions } from './types'; ··· 26 26 } 27 27 28 28 if (operation.body) { 29 - const symbolWebhookPayload = plugin.registerSymbol({ 30 - meta: { 31 - category: 'type', 32 - path: fromRef(state.path), 33 - resource: 'webhook', 34 - resourceId: operation.id, 35 - role: 'data', 36 - tags: fromRef(state.tags), 37 - tool: 'typescript', 38 - }, 39 - name: buildName({ 40 - config: { 41 - case: plugin.config.webhooks.case, 42 - name: plugin.config.webhooks.payload, 43 - }, 44 - name: operation.id, 29 + const symbolWebhookPayload = plugin.symbol( 30 + applyNaming(operation.id, { 31 + case: plugin.config.webhooks.case, 32 + name: plugin.config.webhooks.payload, 45 33 }), 46 - }); 34 + { 35 + meta: { 36 + category: 'type', 37 + path: fromRef(state.path), 38 + resource: 'webhook', 39 + resourceId: operation.id, 40 + role: 'data', 41 + tags: fromRef(state.tags), 42 + tool: 'typescript', 43 + }, 44 + }, 45 + ); 47 46 const node = $.type 48 47 .alias(symbolWebhookPayload) 49 48 .export() ··· 74 73 75 74 data.required = dataRequired; 76 75 77 - const symbolWebhookRequest = plugin.registerSymbol({ 78 - meta: { 79 - category: 'type', 80 - path: fromRef(state.path), 81 - resource: 'webhook', 82 - resourceId: operation.id, 83 - role: 'data', 84 - tags: fromRef(state.tags), 85 - tool: 'typescript', 76 + const symbolWebhookRequest = plugin.symbol( 77 + applyNaming(operation.id, plugin.config.webhooks), 78 + { 79 + meta: { 80 + category: 'type', 81 + path: fromRef(state.path), 82 + resource: 'webhook', 83 + resourceId: operation.id, 84 + role: 'data', 85 + tags: fromRef(state.tags), 86 + tool: 'typescript', 87 + }, 86 88 }, 87 - name: buildName({ 88 - config: plugin.config.webhooks, 89 - name: operation.id, 90 - }), 91 - }); 89 + ); 92 90 const node = $.type 93 91 .alias(symbolWebhookRequest) 94 92 .export()
+36 -36
packages/openapi-ts/src/plugins/@hey-api/typescript/types.d.ts
··· 1 1 import type { DefinePlugin, Plugin } from '~/plugins'; 2 - import type { StringCase, StringName } from '~/types/case'; 2 + import type { Casing, NameTransformer } from '~/utils/naming'; 3 3 4 4 import type { IApi } from './api'; 5 5 ··· 12 12 * 13 13 * @default 'PascalCase' 14 14 */ 15 - case?: Exclude<StringCase, 'SCREAMING_SNAKE_CASE'>; 15 + case?: Exclude<Casing, 'SCREAMING_SNAKE_CASE'>; 16 16 /** 17 17 * Configuration for reusable schema definitions. 18 18 * ··· 26 26 * @default '{{name}}' 27 27 */ 28 28 definitions?: 29 - | StringName 29 + | NameTransformer 30 30 | { 31 31 /** 32 32 * The casing convention to use for generated definition names. 33 33 * 34 34 * @default 'PascalCase' 35 35 */ 36 - case?: StringCase; 36 + case?: Casing; 37 37 /** 38 38 * Custom naming pattern for generated definition names. The name variable 39 39 * is obtained from the schema name. 40 40 * 41 41 * @default '{{name}}' 42 42 */ 43 - name?: StringName; 43 + name?: NameTransformer; 44 44 }; 45 45 /** 46 46 * By default, enums are emitted as types to preserve runtime-free output. ··· 60 60 * 61 61 * @default 'SCREAMING_SNAKE_CASE' 62 62 */ 63 - case?: StringCase; 63 + case?: Casing; 64 64 /** 65 65 * When generating enums as JavaScript objects, they'll contain a null 66 66 * value if they're nullable. This might be undesirable if you want to do ··· 101 101 * @default '{{name}}Errors' 102 102 */ 103 103 errors?: 104 - | StringName 104 + | NameTransformer 105 105 | { 106 106 /** 107 107 * The casing convention to use for generated error type names. 108 108 * 109 109 * @default 'PascalCase' 110 110 */ 111 - case?: StringCase; 111 + case?: Casing; 112 112 /** 113 113 * Custom naming pattern for generated error type names. The name 114 114 * variable is obtained from the operation name. 115 115 * 116 116 * @default '{{name}}Error' 117 117 */ 118 - error?: StringName; 118 + error?: NameTransformer; 119 119 /** 120 120 * Custom naming pattern for generated error type names. The name 121 121 * variable is obtained from the operation name. 122 122 * 123 123 * @default '{{name}}Errors' 124 124 */ 125 - name?: StringName; 125 + name?: NameTransformer; 126 126 }; 127 127 /** 128 128 * Should the exports from the generated files be re-exported in the index ··· 144 144 * @default '{{name}}Data' 145 145 */ 146 146 requests?: 147 - | StringName 147 + | NameTransformer 148 148 | { 149 149 /** 150 150 * The casing convention to use for generated request type names. 151 151 * 152 152 * @default 'PascalCase' 153 153 */ 154 - case?: StringCase; 154 + case?: Casing; 155 155 /** 156 156 * Custom naming pattern for generated request type names. The name 157 157 * variable is obtained from the operation name. 158 158 * 159 159 * @default '{{name}}Data' 160 160 */ 161 - name?: StringName; 161 + name?: NameTransformer; 162 162 }; 163 163 /** 164 164 * Configuration for response-specific types. ··· 172 172 * @default '{{name}}Responses' 173 173 */ 174 174 responses?: 175 - | StringName 175 + | NameTransformer 176 176 | { 177 177 /** 178 178 * The casing convention to use for generated response type names. 179 179 * 180 180 * @default 'PascalCase' 181 181 */ 182 - case?: StringCase; 182 + case?: Casing; 183 183 /** 184 184 * Custom naming pattern for generated response type names. The name 185 185 * variable is obtained from the operation name. 186 186 * 187 187 * @default '{{name}}Responses' 188 188 */ 189 - name?: StringName; 189 + name?: NameTransformer; 190 190 /** 191 191 * Custom naming pattern for generated response type names. The name 192 192 * variable is obtained from the operation name. 193 193 * 194 194 * @default '{{name}}Response' 195 195 */ 196 - response?: StringName; 196 + response?: NameTransformer; 197 197 }; 198 198 /** 199 199 * The top type to use for untyped or unspecified schema values. ··· 217 217 * @default '{{name}}WebhookRequest' 218 218 */ 219 219 webhooks?: 220 - | StringName 220 + | NameTransformer 221 221 | { 222 222 /** 223 223 * The casing convention to use for generated webhook type names. 224 224 * 225 225 * @default 'PascalCase' 226 226 */ 227 - case?: StringCase; 227 + case?: Casing; 228 228 /** 229 229 * Custom naming pattern for generated webhook type names. The name 230 230 * variable is obtained from the webhook key. 231 231 * 232 232 * @default '{{name}}WebhookRequest' 233 233 */ 234 - name?: StringName; 234 + name?: NameTransformer; 235 235 /** 236 236 * Custom naming pattern for generated webhook type names. The name 237 237 * variable is obtained from the webhook key. 238 238 * 239 239 * @default '{{name}}WebhookPayload' 240 240 */ 241 - payload?: StringName; 241 + payload?: NameTransformer; 242 242 }; 243 243 }; 244 244 ··· 249 249 * 250 250 * @default 'PascalCase' 251 251 */ 252 - case: Exclude<StringCase, 'SCREAMING_SNAKE_CASE'>; 252 + case: Exclude<Casing, 'SCREAMING_SNAKE_CASE'>; 253 253 /** 254 254 * Configuration for reusable schema definitions. 255 255 * ··· 262 262 * 263 263 * @default 'PascalCase' 264 264 */ 265 - case: StringCase; 265 + case: Casing; 266 266 /** 267 267 * Custom naming pattern for generated definition names. The name variable 268 268 * is obtained from the schema name. 269 269 * 270 270 * @default '{{name}}' 271 271 */ 272 - name: StringName; 272 + name: NameTransformer; 273 273 }; 274 274 /** 275 275 * By default, enums are emitted as types to preserve runtime-free output. ··· 284 284 * 285 285 * @default 'SCREAMING_SNAKE_CASE' 286 286 */ 287 - case: StringCase; 287 + case: Casing; 288 288 /** 289 289 * When generating enums as JavaScript objects, they'll contain a null 290 290 * value if they're nullable. This might be undesirable if you want to do ··· 328 328 * 329 329 * @default 'PascalCase' 330 330 */ 331 - case: StringCase; 331 + case: Casing; 332 332 /** 333 333 * Custom naming pattern for generated error type names. The name 334 334 * variable is obtained from the operation name. 335 335 * 336 336 * @default '{{name}}Error' 337 337 */ 338 - error: StringName; 338 + error: NameTransformer; 339 339 /** 340 340 * Custom naming pattern for generated error type names. The name 341 341 * variable is obtained from the operation name. 342 342 * 343 343 * @default '{{name}}Errors' 344 344 */ 345 - name: StringName; 345 + name: NameTransformer; 346 346 }; 347 347 /** 348 348 * Should the exports from the generated files be re-exported in the index ··· 363 363 * 364 364 * @default 'PascalCase' 365 365 */ 366 - case: StringCase; 366 + case: Casing; 367 367 /** 368 368 * Custom naming pattern for generated request type names. The name 369 369 * variable is obtained from the operation name. 370 370 * 371 371 * @default '{{name}}Data' 372 372 */ 373 - name: StringName; 373 + name: NameTransformer; 374 374 }; 375 375 /** 376 376 * Configuration for response-specific types. ··· 383 383 * 384 384 * @default 'PascalCase' 385 385 */ 386 - case: StringCase; 386 + case: Casing; 387 387 /** 388 388 * Custom naming pattern for generated response type names. The name 389 389 * variable is obtained from the operation name. 390 390 * 391 391 * @default '{{name}}Responses' 392 392 */ 393 - name: StringName; 393 + name: NameTransformer; 394 394 /** 395 395 * Custom naming pattern for generated response type names. The name 396 396 * variable is obtained from the operation name. 397 397 * 398 398 * @default '{{name}}Response' 399 399 */ 400 - response: StringName; 400 + response: NameTransformer; 401 401 }; 402 402 /** 403 403 * The top type to use for untyped or unspecified schema values. ··· 416 416 * 417 417 * @default 'PascalCase' 418 418 */ 419 - case: StringCase; 419 + case: Casing; 420 420 /** 421 421 * Custom naming pattern for generated webhook type names. The name 422 422 * variable is obtained from the webhook key. 423 423 * 424 424 * @default '{{name}}WebhookRequest' 425 425 */ 426 - name: StringName; 426 + name: NameTransformer; 427 427 /** 428 428 * Custom naming pattern for generated webhook type names. The name 429 429 * variable is obtained from the webhook key. 430 430 * 431 431 * @default '{{name}}WebhookPayload' 432 432 */ 433 - payload: StringName; 433 + payload: NameTransformer; 434 434 }; 435 435 }; 436 436
+3 -6
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts
··· 3 3 4 4 import { deduplicateSchema } from '~/ir/schema'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { SchemaWithType } from '~/plugins'; 8 7 import type { MaybeTsDsl, TypeTsDsl } from '~/ts-dsl'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 11 11 import { createClientOptions } from '../shared/clientOptions'; 12 12 import { exportType } from '../shared/export'; ··· 175 175 176 176 if (webhooks.length > 0) { 177 177 const symbol = plugin.symbol( 178 - buildName({ 179 - config: { 180 - case: plugin.config.case, 181 - }, 182 - name: 'Webhooks', 178 + applyNaming('Webhooks', { 179 + case: plugin.config.case, 183 180 }), 184 181 { 185 182 meta: {
+1 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/object.ts
··· 69 69 (!!addPropsObj && (addPropsObj.type !== 'never' || !indexSchemas.length)); 70 70 71 71 if (shouldCreateIndex) { 72 - // only inject additionalProperties when it’s not "never" 72 + // only inject additionalProperties when it's not "never" 73 73 const addProps = addPropsObj; 74 74 if (addProps && addProps.type !== 'never') { 75 75 indexSchemas.unshift(addProps);
+8 -10
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/string.ts
··· 3 3 import type { SchemaWithType } from '~/plugins'; 4 4 import type { TypeTsDsl } from '~/ts-dsl'; 5 5 import { $ } from '~/ts-dsl'; 6 - import { stringCase } from '~/utils/stringCase'; 6 + import { toCase } from '~/utils/naming'; 7 7 8 8 import type { IrSchemaToAstOptions } from '../../shared/types'; 9 9 ··· 49 49 }; 50 50 51 51 if (!plugin.getSymbol(queryTypeId)) { 52 - const symbolTypeId = plugin.registerSymbol({ 52 + const symbolTypeId = plugin.symbol('TypeID', { 53 53 meta: queryTypeId, 54 - name: 'TypeID', 55 54 }); 56 55 const nodeTypeId = $.type 57 56 .alias(symbolTypeId) ··· 64 63 } 65 64 66 65 const symbolTypeId = plugin.referenceSymbol(queryTypeId); 67 - const symbolTypeName = plugin.registerSymbol({ 68 - meta: query, 69 - name: stringCase({ 70 - case: plugin.config.case, 71 - value: `${type}_id`, 72 - }), 73 - }); 66 + const symbolTypeName = plugin.symbol( 67 + toCase(`${type}_id`, plugin.config.case), 68 + { 69 + meta: query, 70 + }, 71 + ); 74 72 const node = $.type 75 73 .alias(symbolTypeName) 76 74 .export()
+13 -11
packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import { buildName } from '~/openApi/shared/utils/name'; 3 2 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 4 3 import { createOperationComment } from '~/plugins/shared/utils/operation'; 5 4 import { $ } from '~/ts-dsl'; 5 + import { applyNaming } from '~/utils/naming'; 6 6 7 7 import { handleMeta } from './meta'; 8 8 import type { PiniaColadaPlugin } from './types'; ··· 12 12 export const createMutationOptions = ({ 13 13 operation, 14 14 plugin, 15 - queryFn, 16 15 }: { 17 16 operation: IR.OperationObject; 18 17 plugin: PiniaColadaPlugin['Instance']; 19 - queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 20 18 }): void => { 21 19 const symbolMutationOptionsType = plugin.referenceSymbol({ 22 20 category: 'external', ··· 31 29 const options = plugin.symbol('options'); 32 30 const fnOptions = plugin.symbol('vars'); 33 31 34 - const awaitSdkFn = $.lazy(() => 35 - $(queryFn) 32 + const awaitSdkFn = $.lazy((ctx) => 33 + ctx 34 + .access( 35 + plugin.referenceSymbol({ 36 + category: 'sdk', 37 + resource: 'operation', 38 + resourceId: operation.id, 39 + }), 40 + ) 36 41 .call( 37 42 $.object() 38 43 .pretty() ··· 70 75 .$if(handleMeta(plugin, operation, 'mutationOptions'), (o, v) => 71 76 o.prop('meta', v), 72 77 ); 73 - const symbolMutationOptions = plugin.registerSymbol({ 74 - name: buildName({ 75 - config: plugin.config.mutationOptions, 76 - name: operation.id, 77 - }), 78 - }); 78 + const symbolMutationOptions = plugin.symbol( 79 + applyNaming(operation.id, plugin.config.mutationOptions), 80 + ); 79 81 const statement = $.const(symbolMutationOptions) 80 82 .export() 81 83 .$if(plugin.config.comments && createOperationComment(operation), (c, v) =>
+14 -17
packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts
··· 3 3 import { clientFolderAbsolutePath } from '~/generate/client'; 4 4 import { hasOperationDataRequired } from '~/ir/operation'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import { 8 7 getClientBaseUrlKey, 9 8 getClientPlugin, 10 9 } from '~/plugins/@hey-api/client-core/utils'; 11 10 import { $ } from '~/ts-dsl'; 11 + import { applyNaming } from '~/utils/naming'; 12 12 13 13 import type { PiniaColadaPlugin } from './types'; 14 14 import { getPublicTypeData } from './utils'; ··· 20 20 }: { 21 21 plugin: PiniaColadaPlugin['Instance']; 22 22 }) => { 23 - const symbolCreateQueryKey = plugin.registerSymbol({ 24 - meta: { 25 - category: 'utility', 26 - resource: 'createQueryKey', 27 - tool: plugin.name, 28 - }, 29 - name: buildName({ 30 - config: { 31 - case: plugin.config.case, 32 - }, 33 - name: 'createQueryKey', 23 + const symbolCreateQueryKey = plugin.symbol( 24 + applyNaming('createQueryKey', { 25 + case: plugin.config.case, 34 26 }), 35 - }); 27 + { 28 + meta: { 29 + category: 'utility', 30 + resource: 'createQueryKey', 31 + tool: plugin.name, 32 + }, 33 + }, 34 + ); 36 35 const symbolQueryKeyType = plugin.referenceSymbol({ 37 36 category: 'type', 38 37 resource: 'QueryKey', ··· 57 56 }); 58 57 59 58 const clientModule = clientFolderAbsolutePath(plugin.context.config); 60 - const symbolSerializeQueryValue = plugin.registerSymbol({ 59 + const symbolSerializeQueryValue = plugin.symbol('serializeQueryKeyValue', { 61 60 external: clientModule, 62 61 meta: { 63 62 category: 'external', 64 63 resource: `${clientModule}.serializeQueryKeyValue`, 65 64 }, 66 - name: 'serializeQueryKeyValue', 67 65 }); 68 66 69 67 const fn = $.const(symbolCreateQueryKey).assign( ··· 169 167 resource: 'client-options', 170 168 tool: 'sdk', 171 169 }); 172 - const symbolQueryKeyType = plugin.registerSymbol({ 170 + const symbolQueryKeyType = plugin.symbol('QueryKey', { 173 171 meta: { 174 172 category: 'type', 175 173 resource: 'QueryKey', 176 174 tool: plugin.name, 177 175 }, 178 - name: 'QueryKey', 179 176 }); 180 177 const queryKeyType = $.type 181 178 .alias(symbolQueryKeyType)
+24 -23
packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import { buildName } from '~/openApi/shared/utils/name'; 3 2 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 4 3 import { 5 4 createOperationComment, ··· 7 6 isOperationOptionsRequired, 8 7 } from '~/plugins/shared/utils/operation'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 11 11 import { handleMeta } from './meta'; 12 12 import { ··· 23 23 export const createQueryOptions = ({ 24 24 operation, 25 25 plugin, 26 - queryFn, 27 26 }: { 28 27 operation: IR.OperationObject; 29 28 plugin: PiniaColadaPlugin['Instance']; 30 - queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 31 29 }): void => { 32 30 if (hasOperationSse({ operation })) { 33 31 return; ··· 50 48 51 49 let keyExpression: ReturnType<typeof $.call>; 52 50 if (plugin.config.queryKeys.enabled) { 53 - const symbolQueryKey = plugin.registerSymbol({ 54 - name: buildName({ 55 - config: plugin.config.queryKeys, 56 - name: operation.id, 57 - }), 58 - }); 51 + const symbolQueryKey = plugin.symbol( 52 + applyNaming(operation.id, plugin.config.queryKeys), 53 + ); 59 54 const node = queryKeyStatement({ 60 55 operation, 61 56 plugin, ··· 88 83 const client = getClientPlugin(plugin.context.config); 89 84 const isNuxtClient = client.name === '@hey-api/client-nuxt'; 90 85 const typeData = getPublicTypeData({ isNuxtClient, operation, plugin }); 91 - const awaitSdkFn = $.lazy(() => 92 - $(queryFn) 86 + const awaitSdkFn = $.lazy((ctx) => 87 + ctx 88 + .access( 89 + plugin.referenceSymbol({ 90 + category: 'sdk', 91 + resource: 'operation', 92 + resourceId: operation.id, 93 + }), 94 + ) 93 95 .call( 94 96 $.object() 95 97 .spread(optionsParamName) ··· 123 125 o.prop('meta', v), 124 126 ); 125 127 126 - const symbolQueryOptionsFn = plugin.registerSymbol({ 127 - meta: { 128 - category: 'hook', 129 - resource: 'operation', 130 - resourceId: operation.id, 131 - role: 'queryOptions', 132 - tool: plugin.name, 128 + const symbolQueryOptionsFn = plugin.symbol( 129 + applyNaming(operation.id, plugin.config.queryOptions), 130 + { 131 + meta: { 132 + category: 'hook', 133 + resource: 'operation', 134 + resourceId: operation.id, 135 + role: 'queryOptions', 136 + tool: plugin.name, 137 + }, 133 138 }, 134 - name: buildName({ 135 - config: plugin.config.queryOptions, 136 - name: operation.id, 137 - }), 138 - }); 139 + ); 139 140 const symbolDefineQueryOptions = plugin.referenceSymbol({ 140 141 category: 'external', 141 142 resource: `${plugin.name}.defineQueryOptions`,
+22 -22
packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 3 + import type { Casing, NameTransformer } from '~/utils/naming'; 4 4 5 5 export type UserConfig = Plugin.Name<'@pinia/colada'> & 6 6 Plugin.Hooks & { ··· 9 9 * 10 10 * @default 'camelCase' 11 11 */ 12 - case?: StringCase; 12 + case?: Casing; 13 13 /** 14 14 * Add comments from SDK functions to the generated Pinia Colada code? 15 15 * ··· 42 42 */ 43 43 mutationOptions?: 44 44 | boolean 45 - | StringName 45 + | NameTransformer 46 46 | { 47 47 /** 48 48 * The casing convention to use for generated names. 49 49 * 50 50 * @default 'camelCase' 51 51 */ 52 - case?: StringCase; 52 + case?: Casing; 53 53 /** 54 54 * Whether to generate mutation options helpers. 55 55 * ··· 64 64 * @returns A meta object with any properties you want to include 65 65 * 66 66 * @example 67 - * ```typescript 67 + * ```ts 68 68 * meta: (operation) => ({ 69 69 * customField: operation.id, 70 70 * isDeprecated: operation.deprecated, ··· 85 85 * 86 86 * @default '{{name}}Mutation' 87 87 */ 88 - name?: StringName; 88 + name?: NameTransformer; 89 89 }; 90 90 /** 91 91 * Configuration for generated query keys. ··· 101 101 */ 102 102 queryKeys?: 103 103 | boolean 104 - | StringName 104 + | NameTransformer 105 105 | { 106 106 /** 107 107 * The casing convention to use for generated names. 108 108 * 109 109 * @default 'camelCase' 110 110 */ 111 - case?: StringCase; 111 + case?: Casing; 112 112 /** 113 113 * Whether to generate query keys. 114 114 * ··· 121 121 * 122 122 * @default '{{name}}QueryKey' 123 123 */ 124 - name?: StringName; 124 + name?: NameTransformer; 125 125 /** 126 126 * Whether to include operation tags in query keys. 127 127 * This will make query keys larger but provides better cache invalidation capabilities. ··· 144 144 */ 145 145 queryOptions?: 146 146 | boolean 147 - | StringName 147 + | NameTransformer 148 148 | { 149 149 /** 150 150 * The casing convention to use for generated names. 151 151 * 152 152 * @default 'camelCase' 153 153 */ 154 - case?: StringCase; 154 + case?: Casing; 155 155 /** 156 156 * Whether to generate query options helpers. 157 157 * ··· 166 166 * @returns A meta object with any properties you want to include 167 167 * 168 168 * @example 169 - * ```typescript 169 + * ```ts 170 170 * meta: (operation) => ({ 171 171 * customField: operation.id, 172 172 * isDeprecated: operation.deprecated, ··· 187 187 * 188 188 * @default '{{name}}Query' 189 189 */ 190 - name?: StringName; 190 + name?: NameTransformer; 191 191 }; 192 192 }; 193 193 ··· 198 198 * 199 199 * @default 'camelCase' 200 200 */ 201 - case: StringCase; 201 + case: Casing; 202 202 /** 203 203 * Add comments from SDK functions to the generated Pinia Colada code? 204 204 * ··· 220 220 * 221 221 * @default 'camelCase' 222 222 */ 223 - case: StringCase; 223 + case: Casing; 224 224 /** 225 225 * Whether to generate mutation options helpers. 226 226 * ··· 235 235 * @returns A meta object with any properties you want to include 236 236 * 237 237 * @example 238 - * ```typescript 238 + * ```ts 239 239 * meta: (operation) => ({ 240 240 * customField: operation.id, 241 241 * isDeprecated: operation.deprecated, ··· 258 258 * 259 259 * @default '{{name}}Mutation' 260 260 */ 261 - name: StringName; 261 + name: NameTransformer; 262 262 }; 263 263 /** 264 264 * Resolved configuration for generated query keys. ··· 271 271 * 272 272 * @default 'camelCase' 273 273 */ 274 - case: StringCase; 274 + case: Casing; 275 275 /** 276 276 * Whether to generate query keys. 277 277 * ··· 284 284 * 285 285 * @default '{{name}}QueryKey' 286 286 */ 287 - name: StringName; 287 + name: NameTransformer; 288 288 /** 289 289 * Whether to include operation tags in query keys. 290 290 * This will make query keys larger but provides better cache invalidation capabilities. ··· 304 304 * 305 305 * @default 'camelCase' 306 306 */ 307 - case: StringCase; 307 + case: Casing; 308 308 /** 309 309 * Whether to generate query options helpers. 310 310 * ··· 319 319 * @returns A meta object with any properties you want to include 320 320 * 321 321 * @example 322 - * ```typescript 322 + * ```ts 323 323 * meta: (operation) => ({ 324 324 * customField: operation.id, 325 325 * isDeprecated: operation.deprecated, ··· 342 342 * 343 343 * @default '{{name}}Query' 344 344 */ 345 - name: StringName; 345 + name: NameTransformer; 346 346 }; 347 347 }; 348 348
+7 -53
packages/openapi-ts/src/plugins/@pinia/colada/v0/plugin.ts
··· 1 - import { registryName } from '~/plugins/@hey-api/sdk/shared/class'; 2 - import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 3 - import { $ } from '~/ts-dsl'; 4 - import { stringCase } from '~/utils/stringCase'; 5 - 6 1 import { createMutationOptions } from '../mutationOptions'; 7 2 import { createQueryOptions } from '../queryOptions'; 8 3 import type { PiniaColadaPlugin } from '../types'; 9 4 10 5 export const handlerV0: PiniaColadaPlugin['Handler'] = ({ plugin }) => { 11 - plugin.registerSymbol({ 6 + plugin.symbol('defineQueryOptions', { 12 7 external: plugin.name, 13 8 meta: { 14 9 category: 'external', 15 10 resource: `${plugin.name}.defineQueryOptions`, 16 11 }, 17 - name: 'defineQueryOptions', 18 12 }); 19 - plugin.registerSymbol({ 13 + plugin.symbol('UseMutationOptions', { 20 14 external: plugin.name, 21 15 kind: 'type', 22 16 meta: { 23 17 category: 'external', 24 18 resource: `${plugin.name}.UseMutationOptions`, 25 19 }, 26 - name: 'UseMutationOptions', 27 20 }); 28 - plugin.registerSymbol({ 21 + plugin.symbol('UseQueryOptions', { 29 22 external: plugin.name, 30 23 kind: 'type', 31 24 meta: { 32 25 category: 'external', 33 26 resource: `${plugin.name}.UseQueryOptions`, 34 27 }, 35 - name: 'UseQueryOptions', 36 28 }); 37 - plugin.registerSymbol({ 29 + plugin.symbol('_JSONValue', { 38 30 external: plugin.name, 39 31 kind: 'type', 40 32 meta: { 41 33 category: 'external', 42 34 resource: `${plugin.name}._JSONValue`, 43 35 }, 44 - name: '_JSONValue', 45 36 }); 46 - plugin.registerSymbol({ 37 + plugin.symbol('AxiosError', { 47 38 external: 'axios', 48 39 kind: 'type', 49 40 meta: { 50 41 category: 'external', 51 42 resource: 'axios.AxiosError', 52 43 }, 53 - name: 'AxiosError', 54 44 }); 55 45 56 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 57 - 58 46 plugin.forEach( 59 47 'operation', 60 48 ({ operation }) => { 61 - const classes = sdkPlugin.config.asClass 62 - ? operationClasses({ operation, plugin: sdkPlugin }) 63 - : undefined; 64 - const entry = classes ? classes.values().next().value : undefined; 65 - // TODO: this should use class graph to determine correct path string 66 - // as it's really easy to break once we change the class casing 67 - let queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 68 - if (entry) { 69 - const symbolClass = plugin.referenceSymbol({ 70 - category: 'utility', 71 - resource: 'class', 72 - resourceId: entry.path[0], 73 - tool: 'sdk', 74 - }); 75 - queryFn = $(symbolClass).$if(sdkPlugin.config.instance, (e) => 76 - e.attr(registryName).attr('get').call(), 77 - ); 78 - for (const className of entry.path.slice(1)) { 79 - const cls = stringCase({ 80 - case: 'camelCase', 81 - value: className, 82 - }); 83 - queryFn = queryFn.attr(cls); 84 - } 85 - queryFn = queryFn.attr(entry.methodName); 86 - } else { 87 - const symbol = plugin.referenceSymbol({ 88 - category: 'sdk', 89 - resource: 'operation', 90 - resourceId: operation.id, 91 - }); 92 - queryFn = $(symbol); 93 - } 94 - 95 49 if (plugin.hooks.operation.isQuery(operation)) { 96 50 if (plugin.config.queryOptions.enabled) { 97 - createQueryOptions({ operation, plugin, queryFn }); 51 + createQueryOptions({ operation, plugin }); 98 52 } 99 53 } 100 54 101 55 if (plugin.hooks.operation.isMutation(operation)) { 102 56 if (plugin.config.mutationOptions.enabled) { 103 - createMutationOptions({ operation, plugin, queryFn }); 57 + createMutationOptions({ operation, plugin }); 104 58 } 105 59 } 106 60 },
+34 -34
packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 3 + import type { Casing, NameTransformer } from '~/utils/naming'; 4 4 5 5 export type UserConfig = Plugin.Name<'@tanstack/angular-query-experimental'> & 6 6 Plugin.Hooks & { ··· 9 9 * 10 10 * @default 'camelCase' 11 11 */ 12 - case?: StringCase; 12 + case?: Casing; 13 13 /** 14 14 * Add comments from SDK functions to the generated TanStack Query code? 15 15 * ··· 41 41 */ 42 42 infiniteQueryKeys?: 43 43 | boolean 44 - | StringName 44 + | NameTransformer 45 45 | { 46 46 /** 47 47 * The casing convention to use for generated names. 48 48 * 49 49 * @default 'camelCase' 50 50 */ 51 - case?: StringCase; 51 + case?: Casing; 52 52 /** 53 53 * Whether to generate infinite query key helpers. 54 54 * ··· 62 62 * @default '{{name}}InfiniteQueryKey' 63 63 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/infiniteQueryOptions 64 64 */ 65 - name?: StringName; 65 + name?: NameTransformer; 66 66 /** 67 67 * Whether to include operation tags in infinite query keys. 68 68 * This will make query keys larger but provides better cache invalidation capabilities. ··· 85 85 */ 86 86 infiniteQueryOptions?: 87 87 | boolean 88 - | StringName 88 + | NameTransformer 89 89 | { 90 90 /** 91 91 * The casing convention to use for generated names. 92 92 * 93 93 * @default 'camelCase' 94 94 */ 95 - case?: StringCase; 95 + case?: Casing; 96 96 /** 97 97 * Whether to generate infinite query options helpers. 98 98 * ··· 106 106 * @returns A meta object with any properties you want to include 107 107 * 108 108 * @example 109 - * ```typescript 109 + * ```ts 110 110 * meta: (operation) => ({ 111 111 * customField: operation.id, 112 112 * isDeprecated: operation.deprecated, ··· 128 128 * @default '{{name}}InfiniteOptions' 129 129 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/infiniteQueryOptions 130 130 */ 131 - name?: StringName; 131 + name?: NameTransformer; 132 132 }; 133 133 /** 134 134 * Configuration for generated mutation options helpers. ··· 144 144 */ 145 145 mutationOptions?: 146 146 | boolean 147 - | StringName 147 + | NameTransformer 148 148 | { 149 149 /** 150 150 * The casing convention to use for generated names. 151 151 * 152 152 * @default 'camelCase' 153 153 */ 154 - case?: StringCase; 154 + case?: Casing; 155 155 /** 156 156 * Whether to generate mutation options helpers. 157 157 * ··· 165 165 * @returns A meta object with any properties you want to include 166 166 * 167 167 * @example 168 - * ```typescript 168 + * ```ts 169 169 * meta: (operation) => ({ 170 170 * customField: operation.id, 171 171 * isDeprecated: operation.deprecated, ··· 187 187 * @default '{{name}}Mutation' 188 188 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/useMutation 189 189 */ 190 - name?: StringName; 190 + name?: NameTransformer; 191 191 }; 192 192 /** 193 193 * Configuration for generated query keys. ··· 203 203 */ 204 204 queryKeys?: 205 205 | boolean 206 - | StringName 206 + | NameTransformer 207 207 | { 208 208 /** 209 209 * The casing convention to use for generated names. 210 210 * 211 211 * @default 'camelCase' 212 212 */ 213 - case?: StringCase; 213 + case?: Casing; 214 214 /** 215 215 * Whether to generate query keys. 216 216 * ··· 224 224 * @default '{{name}}QueryKey' 225 225 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/queryKey 226 226 */ 227 - name?: StringName; 227 + name?: NameTransformer; 228 228 /** 229 229 * Whether to include operation tags in query keys. 230 230 * This will make query keys larger but provides better cache invalidation capabilities. ··· 247 247 */ 248 248 queryOptions?: 249 249 | boolean 250 - | StringName 250 + | NameTransformer 251 251 | { 252 252 /** 253 253 * The casing convention to use for generated names. 254 254 * 255 255 * @default 'camelCase' 256 256 */ 257 - case?: StringCase; 257 + case?: Casing; 258 258 /** 259 259 * Whether to generate query options helpers. 260 260 * ··· 274 274 * @returns A meta object with any properties you want to include 275 275 * 276 276 * @example 277 - * ```typescript 277 + * ```ts 278 278 * meta: (operation) => ({ 279 279 * customField: operation.id, 280 280 * isDeprecated: operation.deprecated, ··· 296 296 * @default '{{name}}Options' 297 297 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/queryOptions 298 298 */ 299 - name?: StringName; 299 + name?: NameTransformer; 300 300 }; 301 301 }; 302 302 ··· 307 307 * 308 308 * @default 'camelCase' 309 309 */ 310 - case: StringCase; 310 + case: Casing; 311 311 /** 312 312 * Add comments from SDK functions to the generated TanStack Query code? 313 313 * ··· 331 331 * 332 332 * @default 'camelCase' 333 333 */ 334 - case: StringCase; 334 + case: Casing; 335 335 /** 336 336 * Whether to generate infinite query key helpers. 337 337 * ··· 345 345 * @default '{{name}}InfiniteQueryKey' 346 346 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/infiniteQueryOptions 347 347 */ 348 - name: StringName; 348 + name: NameTransformer; 349 349 /** 350 350 * Whether to include operation tags in infinite query keys. 351 351 * This will make query keys larger but provides better cache invalidation capabilities. ··· 365 365 * 366 366 * @default 'camelCase' 367 367 */ 368 - case: StringCase; 368 + case: Casing; 369 369 /** 370 370 * Whether to generate infinite query options helpers. 371 371 * ··· 379 379 * @returns A meta object with any properties you want to include 380 380 * 381 381 * @example 382 - * ```typescript 382 + * ```ts 383 383 * meta: (operation) => ({ 384 384 * customField: operation.id, 385 385 * isDeprecated: operation.deprecated, ··· 401 401 * @default '{{name}}InfiniteOptions' 402 402 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/infiniteQueryOptions 403 403 */ 404 - name: StringName; 404 + name: NameTransformer; 405 405 }; 406 406 /** 407 407 * Resolved configuration for generated mutation options helpers. ··· 414 414 * 415 415 * @default 'camelCase' 416 416 */ 417 - case: StringCase; 417 + case: Casing; 418 418 /** 419 419 * Whether to generate mutation options helpers. 420 420 * ··· 428 428 * @returns A meta object with any properties you want to include 429 429 * 430 430 * @example 431 - * ```typescript 431 + * ```ts 432 432 * meta: (operation) => ({ 433 433 * customField: operation.id, 434 434 * isDeprecated: operation.deprecated, ··· 450 450 * @default '{{name}}Mutation' 451 451 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/useMutation 452 452 */ 453 - name: StringName; 453 + name: NameTransformer; 454 454 }; 455 455 /** 456 456 * Resolved configuration for generated query keys. ··· 463 463 * 464 464 * @default 'camelCase' 465 465 */ 466 - case: StringCase; 466 + case: Casing; 467 467 /** 468 468 * Whether to generate query keys. 469 469 * ··· 477 477 * @default '{{name}}QueryKey' 478 478 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/queryKey 479 479 */ 480 - name: StringName; 480 + name: NameTransformer; 481 481 /** 482 482 * Whether to include operation tags in query keys. 483 483 * This will make query keys larger but provides better cache invalidation capabilities. ··· 497 497 * 498 498 * @default 'camelCase' 499 499 */ 500 - case: StringCase; 500 + case: Casing; 501 501 /** 502 502 * Whether to generate query options helpers. 503 503 * ··· 517 517 * @returns A meta object with any properties you want to include 518 518 * 519 519 * @example 520 - * ```typescript 520 + * ```ts 521 521 * meta: (operation) => ({ 522 522 * customField: operation.id, 523 523 * isDeprecated: operation.deprecated, ··· 539 539 * @default '{{name}}Options' 540 540 * @see https://tanstack.com/query/v5/docs/framework/angular/reference/queryOptions 541 541 */ 542 - name: StringName; 542 + name: NameTransformer; 543 543 }; 544 544 }; 545 545
+13 -15
packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts
··· 3 3 4 4 import { hasOperationDataRequired } from '~/ir/operation'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import { getClientBaseUrlKey } from '~/plugins/@hey-api/client-core/utils'; 8 7 import type { TsDsl } from '~/ts-dsl'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 11 11 import { useTypeData } from './shared/useType'; 12 12 import type { PluginInstance } from './types'; ··· 18 18 }: { 19 19 plugin: PluginInstance; 20 20 }) => { 21 - const symbolCreateQueryKey = plugin.registerSymbol({ 22 - meta: { 23 - category: 'utility', 24 - resource: 'createQueryKey', 25 - tool: plugin.name, 21 + const symbolCreateQueryKey = plugin.symbol( 22 + applyNaming('createQueryKey', { 23 + case: plugin.config.case, 24 + }), 25 + { 26 + meta: { 27 + category: 'utility', 28 + resource: 'createQueryKey', 29 + tool: plugin.name, 30 + }, 26 31 }, 27 - name: buildName({ 28 - config: { 29 - case: plugin.config.case, 30 - }, 31 - name: 'createQueryKey', 32 - }), 33 - }); 32 + ); 34 33 const symbolQueryKeyType = plugin.referenceSymbol({ 35 34 category: 'type', 36 35 resource: 'QueryKey', ··· 140 139 resource: 'client-options', 141 140 tool: 'sdk', 142 141 }); 143 - const symbolQueryKeyType = plugin.registerSymbol({ 142 + const symbolQueryKeyType = plugin.symbol('QueryKey', { 144 143 meta: { 145 144 category: 'type', 146 145 resource: 'QueryKey', 147 146 tool: plugin.name, 148 147 }, 149 - name: 'QueryKey', 150 148 }); 151 149 const queryKeyType = $.type 152 150 .alias(symbolQueryKeyType)
+27 -29
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/infiniteQueryOptions.ts
··· 2 2 3 3 import { operationPagination } from '~/ir/operation'; 4 4 import type { IR } from '~/ir/types'; 5 - import { buildName } from '~/openApi/shared/utils/name'; 6 5 import { 7 6 createOperationComment, 8 7 isOperationOptionsRequired, 9 8 } from '~/plugins/shared/utils/operation'; 10 9 import type { TsDsl } from '~/ts-dsl'; 11 10 import { $ } from '~/ts-dsl'; 11 + import { applyNaming } from '~/utils/naming'; 12 12 13 13 import { 14 14 createQueryKeyFunction, ··· 24 24 }: { 25 25 plugin: PluginInstance; 26 26 }) => { 27 - const symbolCreateInfiniteParams = plugin.registerSymbol({ 28 - meta: { 29 - category: 'utility', 30 - resource: 'createInfiniteParams', 31 - tool: plugin.name, 32 - }, 33 - name: buildName({ 34 - config: { 35 - case: plugin.config.case, 36 - }, 37 - name: 'createInfiniteParams', 27 + const symbolCreateInfiniteParams = plugin.symbol( 28 + applyNaming('createInfiniteParams', { 29 + case: plugin.config.case, 38 30 }), 39 - }); 31 + { 32 + meta: { 33 + category: 'utility', 34 + resource: 'createInfiniteParams', 35 + tool: plugin.name, 36 + }, 37 + }, 38 + ); 40 39 41 40 const fn = $.const(symbolCreateInfiniteParams).assign( 42 41 $.func() ··· 106 105 export const createInfiniteQueryOptions = ({ 107 106 operation, 108 107 plugin, 109 - queryFn, 110 108 }: { 111 109 operation: IR.OperationObject; 112 110 plugin: PluginInstance; 113 - queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 114 111 }): void => { 115 112 const pagination = operationPagination({ 116 113 context: plugin.context, ··· 183 180 }, 184 181 }); 185 182 186 - const symbolInfiniteQueryKey = plugin.registerSymbol({ 187 - name: buildName({ 188 - config: plugin.config.infiniteQueryKeys, 189 - name: operation.id, 190 - }), 191 - }); 183 + const symbolInfiniteQueryKey = plugin.symbol( 184 + applyNaming(operation.id, plugin.config.infiniteQueryKeys), 185 + ); 192 186 const node = queryKeyStatement({ 193 187 isInfinite: true, 194 188 operation, ··· 198 192 }); 199 193 plugin.node(node); 200 194 201 - const awaitSdkFn = $.lazy(() => 202 - $(queryFn) 195 + const awaitSdkFn = $.lazy((ctx) => 196 + ctx 197 + .access( 198 + plugin.referenceSymbol({ 199 + category: 'sdk', 200 + resource: 'operation', 201 + resourceId: operation.id, 202 + }), 203 + ) 203 204 .call( 204 205 $.object() 205 206 .spread('options') ··· 246 247 ); 247 248 } 248 249 249 - const symbolInfiniteQueryOptionsFn = plugin.registerSymbol({ 250 - name: buildName({ 251 - config: plugin.config.infiniteQueryOptions, 252 - name: operation.id, 253 - }), 254 - }); 250 + const symbolInfiniteQueryOptionsFn = plugin.symbol( 251 + applyNaming(operation.id, plugin.config.infiniteQueryOptions), 252 + ); 255 253 const statement = $.const(symbolInfiniteQueryOptionsFn) 256 254 .export() 257 255 .$if(plugin.config.comments && createOperationComment(operation), (c, v) =>
+13 -11
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/mutationOptions.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import { buildName } from '~/openApi/shared/utils/name'; 3 2 import { createOperationComment } from '~/plugins/shared/utils/operation'; 4 3 import type { TsDsl } from '~/ts-dsl'; 5 4 import { $ } from '~/ts-dsl'; 5 + import { applyNaming } from '~/utils/naming'; 6 6 7 7 import { handleMeta } from '../shared/meta'; 8 8 import { useTypeData, useTypeError, useTypeResponse } from '../shared/useType'; ··· 11 11 export const createMutationOptions = ({ 12 12 operation, 13 13 plugin, 14 - queryFn, 15 14 }: { 16 15 operation: IR.OperationObject; 17 16 plugin: PluginInstance; 18 - queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 19 17 }): void => { 20 18 const symbolMutationOptionsType = plugin.referenceSymbol({ 21 19 category: 'external', ··· 30 28 31 29 const fnOptions = 'fnOptions'; 32 30 33 - const awaitSdkFn = $.lazy(() => 34 - $(queryFn) 31 + const awaitSdkFn = $.lazy((ctx) => 32 + ctx 33 + .access( 34 + plugin.referenceSymbol({ 35 + category: 'sdk', 36 + resource: 'operation', 37 + resourceId: operation.id, 38 + }), 39 + ) 35 40 .call( 36 41 $.object() 37 42 .spread('options') ··· 52 57 } 53 58 54 59 const mutationOptionsFn = 'mutationOptions'; 55 - const symbolMutationOptions = plugin.registerSymbol({ 56 - name: buildName({ 57 - config: plugin.config.mutationOptions, 58 - name: operation.id, 59 - }), 60 - }); 60 + const symbolMutationOptions = plugin.symbol( 61 + applyNaming(operation.id, plugin.config.mutationOptions), 62 + ); 61 63 const statement = $.const(symbolMutationOptions) 62 64 .export() 63 65 .$if(plugin.config.comments && createOperationComment(operation), (c, v) =>
+10 -58
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/plugin.ts
··· 1 - import { registryName } from '~/plugins/@hey-api/sdk/shared/class'; 2 - import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 3 - import { $ } from '~/ts-dsl'; 4 - import { stringCase } from '~/utils/stringCase'; 5 - 6 1 import type { PluginHandler } from '../types'; 7 2 import { createInfiniteQueryOptions } from './infiniteQueryOptions'; 8 3 import { createMutationOptions } from './mutationOptions'; ··· 10 5 import { createUseQuery } from './useQuery'; 11 6 12 7 export const handlerV5: PluginHandler = ({ plugin }) => { 13 - plugin.registerSymbol({ 8 + plugin.symbol('DefaultError', { 14 9 external: plugin.name, 15 10 kind: 'type', 16 11 meta: { 17 12 category: 'external', 18 13 resource: `${plugin.name}.DefaultError`, 19 14 }, 20 - name: 'DefaultError', 21 15 }); 22 - plugin.registerSymbol({ 16 + plugin.symbol('InfiniteData', { 23 17 external: plugin.name, 24 18 kind: 'type', 25 19 meta: { 26 20 category: 'external', 27 21 resource: `${plugin.name}.InfiniteData`, 28 22 }, 29 - name: 'InfiniteData', 30 23 }); 31 24 const mutationsType = 32 25 plugin.name === '@tanstack/angular-query-experimental' || ··· 34 27 plugin.name === '@tanstack/solid-query' 35 28 ? 'MutationOptions' 36 29 : 'UseMutationOptions'; 37 - plugin.registerSymbol({ 30 + plugin.symbol(mutationsType, { 38 31 external: plugin.name, 39 32 kind: 'type', 40 33 meta: { 41 34 category: 'external', 42 35 resource: `${plugin.name}.MutationOptions`, 43 36 }, 44 - name: mutationsType, 45 37 }); 46 - plugin.registerSymbol({ 38 + plugin.symbol('infiniteQueryOptions', { 47 39 external: plugin.name, 48 40 meta: { 49 41 category: 'external', 50 42 resource: `${plugin.name}.infiniteQueryOptions`, 51 43 }, 52 - name: 'infiniteQueryOptions', 53 44 }); 54 - plugin.registerSymbol({ 45 + plugin.symbol('queryOptions', { 55 46 external: plugin.name, 56 47 meta: { 57 48 category: 'external', 58 49 resource: `${plugin.name}.queryOptions`, 59 50 }, 60 - name: 'queryOptions', 61 51 }); 62 - plugin.registerSymbol({ 52 + plugin.symbol('useQuery', { 63 53 external: plugin.name, 64 54 meta: { 65 55 category: 'external', 66 56 resource: `${plugin.name}.useQuery`, 67 57 }, 68 - name: 'useQuery', 69 58 }); 70 - plugin.registerSymbol({ 59 + plugin.symbol('AxiosError', { 71 60 external: 'axios', 72 61 kind: 'type', 73 62 meta: { 74 63 category: 'external', 75 64 resource: 'axios.AxiosError', 76 65 }, 77 - name: 'AxiosError', 78 66 }); 79 67 80 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 81 - 82 68 plugin.forEach( 83 69 'operation', 84 70 ({ operation }) => { 85 - const classes = sdkPlugin.config.asClass 86 - ? operationClasses({ operation, plugin: sdkPlugin }) 87 - : undefined; 88 - const entry = classes ? classes.values().next().value : undefined; 89 - // TODO: this should use class graph to determine correct path string 90 - // as it's really easy to break once we change the class casing 91 - let queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 92 - if (entry) { 93 - const symbolClass = plugin.referenceSymbol({ 94 - category: 'utility', 95 - resource: 'class', 96 - resourceId: entry.path[0], 97 - tool: 'sdk', 98 - }); 99 - queryFn = $(symbolClass).$if(sdkPlugin.config.instance, (e) => 100 - e.attr(registryName).attr('get').call(), 101 - ); 102 - for (const className of entry.path.slice(1)) { 103 - const cls = stringCase({ 104 - case: 'camelCase', 105 - value: className, 106 - }); 107 - queryFn = queryFn.attr(cls); 108 - } 109 - queryFn = queryFn.attr(entry.methodName); 110 - } else { 111 - const symbol = plugin.referenceSymbol({ 112 - category: 'sdk', 113 - resource: 'operation', 114 - resourceId: operation.id, 115 - }); 116 - queryFn = $(symbol); 117 - } 118 - 119 71 if (plugin.hooks.operation.isQuery(operation)) { 120 72 if (plugin.config.queryOptions.enabled) { 121 - createQueryOptions({ operation, plugin, queryFn }); 73 + createQueryOptions({ operation, plugin }); 122 74 } 123 75 124 76 if (plugin.config.infiniteQueryOptions.enabled) { 125 - createInfiniteQueryOptions({ operation, plugin, queryFn }); 77 + createInfiniteQueryOptions({ operation, plugin }); 126 78 } 127 79 128 80 if ('useQuery' in plugin.config && plugin.config.useQuery.enabled) { ··· 132 84 133 85 if (plugin.hooks.operation.isMutation(operation)) { 134 86 if (plugin.config.mutationOptions.enabled) { 135 - createMutationOptions({ operation, plugin, queryFn }); 87 + createMutationOptions({ operation, plugin }); 136 88 } 137 89 } 138 90 },
+24 -23
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/queryOptions.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import { buildName } from '~/openApi/shared/utils/name'; 3 2 import { 4 3 createOperationComment, 5 4 hasOperationSse, ··· 7 6 } from '~/plugins/shared/utils/operation'; 8 7 import type { TsDsl } from '~/ts-dsl'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 11 11 import { 12 12 createQueryKeyFunction, ··· 22 22 export const createQueryOptions = ({ 23 23 operation, 24 24 plugin, 25 - queryFn, 26 25 }: { 27 26 operation: IR.OperationObject; 28 27 plugin: PluginInstance; 29 - queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 30 28 }): void => { 31 29 if (hasOperationSse({ operation })) { 32 30 return; ··· 53 51 resource: `${plugin.name}.queryOptions`, 54 52 }); 55 53 56 - const symbolQueryKey = plugin.registerSymbol({ 57 - name: buildName({ 58 - config: plugin.config.queryKeys, 59 - name: operation.id, 60 - }), 61 - }); 54 + const symbolQueryKey = plugin.symbol( 55 + applyNaming(operation.id, plugin.config.queryKeys), 56 + ); 62 57 const node = queryKeyStatement({ 63 58 isInfinite: false, 64 59 operation, ··· 69 64 70 65 const typeResponse = useTypeResponse({ operation, plugin }); 71 66 72 - const awaitSdkFn = $.lazy(() => 73 - $(queryFn) 67 + const awaitSdkFn = $.lazy((ctx) => 68 + ctx 69 + .access( 70 + plugin.referenceSymbol({ 71 + category: 'sdk', 72 + resource: 'operation', 73 + resourceId: operation.id, 74 + }), 75 + ) 74 76 .call( 75 77 $.object() 76 78 .spread(optionsParamName) ··· 105 107 o.prop('meta', v), 106 108 ); 107 109 108 - const symbolQueryOptionsFn = plugin.registerSymbol({ 109 - meta: { 110 - category: 'hook', 111 - resource: 'operation', 112 - resourceId: operation.id, 113 - role: 'queryOptions', 114 - tool: plugin.name, 110 + const symbolQueryOptionsFn = plugin.symbol( 111 + applyNaming(operation.id, plugin.config.queryOptions), 112 + { 113 + meta: { 114 + category: 'hook', 115 + resource: 'operation', 116 + resourceId: operation.id, 117 + role: 'queryOptions', 118 + tool: plugin.name, 119 + }, 115 120 }, 116 - name: buildName({ 117 - config: plugin.config.queryOptions, 118 - name: operation.id, 119 - }), 120 - }); 121 + ); 121 122 // TODO: add type error 122 123 // TODO: AxiosError<PutSubmissionMetaError> 123 124 const statement = $.const(symbolQueryOptionsFn)
+4 -7
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/useQuery.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import { buildName } from '~/openApi/shared/utils/name'; 3 2 import { 4 3 createOperationComment, 5 4 hasOperationSse, 6 5 isOperationOptionsRequired, 7 6 } from '~/plugins/shared/utils/operation'; 8 7 import { $ } from '~/ts-dsl'; 8 + import { applyNaming } from '~/utils/naming'; 9 9 10 10 import { useTypeData } from '../shared/useType'; 11 11 import type { PluginInstance } from '../types'; ··· 27 27 return; 28 28 } 29 29 30 - const symbolUseQueryFn = plugin.registerSymbol({ 31 - name: buildName({ 32 - config: plugin.config.useQuery, 33 - name: operation.id, 34 - }), 35 - }); 30 + const symbolUseQueryFn = plugin.symbol( 31 + applyNaming(operation.id, plugin.config.useQuery), 32 + ); 36 33 37 34 const symbolUseQuery = plugin.referenceSymbol({ 38 35 category: 'external',
+39 -39
packages/openapi-ts/src/plugins/@tanstack/react-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 3 + import type { Casing, NameTransformer } from '~/utils/naming'; 4 4 5 5 export type UserConfig = Plugin.Name<'@tanstack/react-query'> & 6 6 Plugin.Hooks & { ··· 9 9 * 10 10 * @default 'camelCase' 11 11 */ 12 - case?: StringCase; 12 + case?: Casing; 13 13 /** 14 14 * Add comments from SDK functions to the generated TanStack Query code? 15 15 * ··· 42 42 */ 43 43 infiniteQueryKeys?: 44 44 | boolean 45 - | StringName 45 + | NameTransformer 46 46 | { 47 47 /** 48 48 * The casing convention to use for generated names. 49 49 * 50 50 * @default 'camelCase' 51 51 */ 52 - case?: StringCase; 52 + case?: Casing; 53 53 /** 54 54 * Whether to generate infinite query key helpers. 55 55 * ··· 64 64 * 65 65 * @default '{{name}}InfiniteQueryKey' 66 66 */ 67 - name?: StringName; 67 + name?: NameTransformer; 68 68 /** 69 69 * Whether to include operation tags in infinite query keys. 70 70 * This will make query keys larger but provides better cache invalidation capabilities. ··· 87 87 */ 88 88 infiniteQueryOptions?: 89 89 | boolean 90 - | StringName 90 + | NameTransformer 91 91 | { 92 92 /** 93 93 * The casing convention to use for generated names. 94 94 * 95 95 * @default 'camelCase' 96 96 */ 97 - case?: StringCase; 97 + case?: Casing; 98 98 /** 99 99 * Whether to generate infinite query options helpers. 100 100 * ··· 109 109 * @returns A meta object with any properties you want to include 110 110 * 111 111 * @example 112 - * ```typescript 112 + * ```ts 113 113 * meta: (operation) => ({ 114 114 * customField: operation.id, 115 115 * isDeprecated: operation.deprecated, ··· 132 132 * 133 133 * @default '{{name}}InfiniteOptions' 134 134 */ 135 - name?: StringName; 135 + name?: NameTransformer; 136 136 }; 137 137 /** 138 138 * Configuration for generated mutation options helpers. ··· 148 148 */ 149 149 mutationOptions?: 150 150 | boolean 151 - | StringName 151 + | NameTransformer 152 152 | { 153 153 /** 154 154 * The casing convention to use for generated names. 155 155 * 156 156 * @default 'camelCase' 157 157 */ 158 - case?: StringCase; 158 + case?: Casing; 159 159 /** 160 160 * Whether to generate mutation options helpers. 161 161 * ··· 170 170 * @returns A meta object with any properties you want to include 171 171 * 172 172 * @example 173 - * ```typescript 173 + * ```ts 174 174 * meta: (operation) => ({ 175 175 * customField: operation.id, 176 176 * isDeprecated: operation.deprecated, ··· 193 193 * 194 194 * @default '{{name}}Mutation' 195 195 */ 196 - name?: StringName; 196 + name?: NameTransformer; 197 197 }; 198 198 /** 199 199 * Configuration for generated query keys. ··· 209 209 */ 210 210 queryKeys?: 211 211 | boolean 212 - | StringName 212 + | NameTransformer 213 213 | { 214 214 /** 215 215 * The casing convention to use for generated names. 216 216 * 217 217 * @default 'camelCase' 218 218 */ 219 - case?: StringCase; 219 + case?: Casing; 220 220 /** 221 221 * Whether to generate query keys. 222 222 * ··· 231 231 * 232 232 * @default '{{name}}QueryKey' 233 233 */ 234 - name?: StringName; 234 + name?: NameTransformer; 235 235 /** 236 236 * Whether to include operation tags in query keys. 237 237 * This will make query keys larger but provides better cache invalidation capabilities. ··· 254 254 */ 255 255 queryOptions?: 256 256 | boolean 257 - | StringName 257 + | NameTransformer 258 258 | { 259 259 /** 260 260 * The casing convention to use for generated names. 261 261 * 262 262 * @default 'camelCase' 263 263 */ 264 - case?: StringCase; 264 + case?: Casing; 265 265 /** 266 266 * Whether to generate query options helpers. 267 267 * ··· 282 282 * @returns A meta object with any properties you want to include 283 283 * 284 284 * @example 285 - * ```typescript 285 + * ```ts 286 286 * meta: (operation) => ({ 287 287 * customField: operation.id, 288 288 * isDeprecated: operation.deprecated, ··· 305 305 * 306 306 * @default '{{name}}Options' 307 307 */ 308 - name?: StringName; 308 + name?: NameTransformer; 309 309 }; 310 310 /** 311 311 * Configuration for generated `useQuery()` function helpers. ··· 321 321 */ 322 322 useQuery?: 323 323 | boolean 324 - | StringName 324 + | NameTransformer 325 325 | { 326 326 /** 327 327 * The casing convention to use for generated names. 328 328 * 329 329 * @default 'camelCase' 330 330 */ 331 - case?: StringCase; 331 + case?: Casing; 332 332 /** 333 333 * Whether to generate `useQuery()` function helpers. 334 334 * ··· 343 343 * 344 344 * @default 'use{{name}}Query' 345 345 */ 346 - name?: StringName; 346 + name?: NameTransformer; 347 347 }; 348 348 }; 349 349 ··· 354 354 * 355 355 * @default 'camelCase' 356 356 */ 357 - case: StringCase; 357 + case: Casing; 358 358 /** 359 359 * Add comments from SDK functions to the generated TanStack Query code? 360 360 * ··· 378 378 * 379 379 * @default 'camelCase' 380 380 */ 381 - case: StringCase; 381 + case: Casing; 382 382 /** 383 383 * Whether to generate infinite query key helpers. 384 384 * ··· 392 392 * 393 393 * @default '{{name}}InfiniteQueryKey' 394 394 */ 395 - name: StringName; 395 + name: NameTransformer; 396 396 /** 397 397 * Whether to include operation tags in infinite query keys. 398 398 * This will make query keys larger but provides better cache invalidation capabilities. ··· 412 412 * 413 413 * @default 'camelCase' 414 414 */ 415 - case: StringCase; 415 + case: Casing; 416 416 /** 417 417 * Whether to generate infinite query options helpers. 418 418 * ··· 427 427 * @returns A meta object with any properties you want to include 428 428 * 429 429 * @example 430 - * ```typescript 430 + * ```ts 431 431 * meta: (operation) => ({ 432 432 * customField: operation.id, 433 433 * isDeprecated: operation.deprecated, ··· 449 449 * 450 450 * @default '{{name}}InfiniteOptions' 451 451 */ 452 - name: StringName; 452 + name: NameTransformer; 453 453 }; 454 454 /** 455 455 * Resolved configuration for generated mutation options helpers. ··· 462 462 * 463 463 * @default 'camelCase' 464 464 */ 465 - case: StringCase; 465 + case: Casing; 466 466 /** 467 467 * Whether to generate mutation options helpers. 468 468 * ··· 477 477 * @returns A meta object with any properties you want to include 478 478 * 479 479 * @example 480 - * ```typescript 480 + * ```ts 481 481 * meta: (operation) => ({ 482 482 * customField: operation.id, 483 483 * isDeprecated: operation.deprecated, ··· 499 499 * 500 500 * @default '{{name}}Mutation' 501 501 */ 502 - name: StringName; 502 + name: NameTransformer; 503 503 }; 504 504 /** 505 505 * Resolved configuration for generated query keys. ··· 512 512 * 513 513 * @default 'camelCase' 514 514 */ 515 - case: StringCase; 515 + case: Casing; 516 516 /** 517 517 * Whether to generate query keys. 518 518 * ··· 526 526 * 527 527 * @default '{{name}}QueryKey' 528 528 */ 529 - name: StringName; 529 + name: NameTransformer; 530 530 /** 531 531 * Whether to include operation tags in query keys. 532 532 * This will make query keys larger but provides better cache invalidation capabilities. ··· 546 546 * 547 547 * @default 'camelCase' 548 548 */ 549 - case: StringCase; 549 + case: Casing; 550 550 /** 551 551 * Whether to generate query options helpers. 552 552 * ··· 567 567 * @returns A meta object with any properties you want to include 568 568 * 569 569 * @example 570 - * ```typescript 570 + * ```ts 571 571 * meta: (operation) => ({ 572 572 * customField: operation.id, 573 573 * isDeprecated: operation.deprecated, ··· 589 589 * 590 590 * @default '{{name}}Options' 591 591 */ 592 - name: StringName; 592 + name: NameTransformer; 593 593 }; 594 594 /** 595 595 * Configuration for generated `useQuery()` function helpers. ··· 602 602 * 603 603 * @default 'camelCase' 604 604 */ 605 - case: StringCase; 605 + case: Casing; 606 606 /** 607 607 * Whether to generate `useQuery()` function helpers. 608 608 * ··· 617 617 * 618 618 * @default 'use{{name}}Query' 619 619 */ 620 - name: StringName; 620 + name: NameTransformer; 621 621 }; 622 622 }; 623 623
+34 -34
packages/openapi-ts/src/plugins/@tanstack/solid-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 3 + import type { Casing, NameTransformer } from '~/utils/naming'; 4 4 5 5 export type UserConfig = Plugin.Name<'@tanstack/solid-query'> & 6 6 Plugin.Hooks & { ··· 9 9 * 10 10 * @default 'camelCase' 11 11 */ 12 - case?: StringCase; 12 + case?: Casing; 13 13 /** 14 14 * Add comments from SDK functions to the generated TanStack Query code? 15 15 * ··· 41 41 */ 42 42 infiniteQueryKeys?: 43 43 | boolean 44 - | StringName 44 + | NameTransformer 45 45 | { 46 46 /** 47 47 * The casing convention to use for generated names. 48 48 * 49 49 * @default 'camelCase' 50 50 */ 51 - case?: StringCase; 51 + case?: Casing; 52 52 /** 53 53 * Whether to generate infinite query key helpers. 54 54 * ··· 62 62 * @default '{{name}}InfiniteQueryKey' 63 63 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createInfiniteQuery 64 64 */ 65 - name?: StringName; 65 + name?: NameTransformer; 66 66 /** 67 67 * Whether to include operation tags in infinite query keys. 68 68 * This will make query keys larger but provides better cache invalidation capabilities. ··· 85 85 */ 86 86 infiniteQueryOptions?: 87 87 | boolean 88 - | StringName 88 + | NameTransformer 89 89 | { 90 90 /** 91 91 * The casing convention to use for generated names. 92 92 * 93 93 * @default 'camelCase' 94 94 */ 95 - case?: StringCase; 95 + case?: Casing; 96 96 /** 97 97 * Whether to generate infinite query options helpers. 98 98 * ··· 107 107 * @returns A meta object with any properties you want to include 108 108 * 109 109 * @example 110 - * ```typescript 110 + * ```ts 111 111 * meta: (operation) => ({ 112 112 * customField: operation.id, 113 113 * isDeprecated: operation.deprecated, ··· 129 129 * @default '{{name}}InfiniteOptions' 130 130 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createInfiniteQuery 131 131 */ 132 - name?: StringName; 132 + name?: NameTransformer; 133 133 }; 134 134 /** 135 135 * Configuration for generated mutation options helpers. ··· 145 145 */ 146 146 mutationOptions?: 147 147 | boolean 148 - | StringName 148 + | NameTransformer 149 149 | { 150 150 /** 151 151 * The casing convention to use for generated names. 152 152 * 153 153 * @default 'camelCase' 154 154 */ 155 - case?: StringCase; 155 + case?: Casing; 156 156 /** 157 157 * Whether to generate mutation options helpers. 158 158 * ··· 166 166 * @returns A meta object with any properties you want to include 167 167 * 168 168 * @example 169 - * ```typescript 169 + * ```ts 170 170 * meta: (operation) => ({ 171 171 * customField: operation.id, 172 172 * isDeprecated: operation.deprecated, ··· 188 188 * @default '{{name}}Mutation' 189 189 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createMutation 190 190 */ 191 - name?: StringName; 191 + name?: NameTransformer; 192 192 }; 193 193 /** 194 194 * Configuration for generated query keys. ··· 204 204 */ 205 205 queryKeys?: 206 206 | boolean 207 - | StringName 207 + | NameTransformer 208 208 | { 209 209 /** 210 210 * The casing convention to use for generated names. 211 211 * 212 212 * @default 'camelCase' 213 213 */ 214 - case?: StringCase; 214 + case?: Casing; 215 215 /** 216 216 * Whether to generate query keys. 217 217 * ··· 225 225 * @default '{{name}}QueryKey' 226 226 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/queryKey 227 227 */ 228 - name?: StringName; 228 + name?: NameTransformer; 229 229 /** 230 230 * Whether to include operation tags in query keys. 231 231 * This will make query keys larger but provides better cache invalidation capabilities. ··· 248 248 */ 249 249 queryOptions?: 250 250 | boolean 251 - | StringName 251 + | NameTransformer 252 252 | { 253 253 /** 254 254 * The casing convention to use for generated names. 255 255 * 256 256 * @default 'camelCase' 257 257 */ 258 - case?: StringCase; 258 + case?: Casing; 259 259 /** 260 260 * Whether to generate query options helpers. 261 261 * ··· 275 275 * @returns A meta object with any properties you want to include 276 276 * 277 277 * @example 278 - * ```typescript 278 + * ```ts 279 279 * meta: (operation) => ({ 280 280 * customField: operation.id, 281 281 * isDeprecated: operation.deprecated, ··· 297 297 * @default '{{name}}Options' 298 298 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createQuery 299 299 */ 300 - name?: StringName; 300 + name?: NameTransformer; 301 301 }; 302 302 }; 303 303 ··· 308 308 * 309 309 * @default 'camelCase' 310 310 */ 311 - case: StringCase; 311 + case: Casing; 312 312 /** 313 313 * Add comments from SDK functions to the generated TanStack Query code? 314 314 * ··· 332 332 * 333 333 * @default 'camelCase' 334 334 */ 335 - case: StringCase; 335 + case: Casing; 336 336 /** 337 337 * Whether to generate infinite query key helpers. 338 338 * ··· 346 346 * @default '{{name}}InfiniteQueryKey' 347 347 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createInfiniteQuery 348 348 */ 349 - name: StringName; 349 + name: NameTransformer; 350 350 /** 351 351 * Whether to include operation tags in infinite query keys. 352 352 * This will make query keys larger but provides better cache invalidation capabilities. ··· 366 366 * 367 367 * @default 'camelCase' 368 368 */ 369 - case: StringCase; 369 + case: Casing; 370 370 /** 371 371 * Whether to generate infinite query options helpers. 372 372 * ··· 380 380 * @returns A meta object with any properties you want to include 381 381 * 382 382 * @example 383 - * ```typescript 383 + * ```ts 384 384 * meta: (operation) => ({ 385 385 * customField: operation.id, 386 386 * isDeprecated: operation.deprecated, ··· 402 402 * @default '{{name}}InfiniteOptions' 403 403 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createInfiniteQuery 404 404 */ 405 - name: StringName; 405 + name: NameTransformer; 406 406 }; 407 407 /** 408 408 * Resolved configuration for generated mutation options helpers. ··· 415 415 * 416 416 * @default 'camelCase' 417 417 */ 418 - case: StringCase; 418 + case: Casing; 419 419 /** 420 420 * Whether to generate mutation options helpers. 421 421 * ··· 429 429 * @returns A meta object with any properties you want to include 430 430 * 431 431 * @example 432 - * ```typescript 432 + * ```ts 433 433 * meta: (operation) => ({ 434 434 * customField: operation.id, 435 435 * isDeprecated: operation.deprecated, ··· 451 451 * @default '{{name}}Mutation' 452 452 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createMutation 453 453 */ 454 - name: StringName; 454 + name: NameTransformer; 455 455 }; 456 456 /** 457 457 * Resolved configuration for generated query keys. ··· 464 464 * 465 465 * @default 'camelCase' 466 466 */ 467 - case: StringCase; 467 + case: Casing; 468 468 /** 469 469 * Whether to generate query keys. 470 470 * ··· 478 478 * @default '{{name}}QueryKey' 479 479 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/queryKey 480 480 */ 481 - name: StringName; 481 + name: NameTransformer; 482 482 /** 483 483 * Whether to include operation tags in query keys. 484 484 * This will make query keys larger but provides better cache invalidation capabilities. ··· 498 498 * 499 499 * @default 'camelCase' 500 500 */ 501 - case: StringCase; 501 + case: Casing; 502 502 /** 503 503 * Whether to generate query options helpers. 504 504 * ··· 518 518 * @returns A meta object with any properties you want to include 519 519 * 520 520 * @example 521 - * ```typescript 521 + * ```ts 522 522 * meta: (operation) => ({ 523 523 * customField: operation.id, 524 524 * isDeprecated: operation.deprecated, ··· 540 540 * @default '{{name}}Options' 541 541 * @see https://tanstack.com/query/v5/docs/framework/solid/reference/createQuery 542 542 */ 543 - name: StringName; 543 + name: NameTransformer; 544 544 }; 545 545 }; 546 546
+34 -34
packages/openapi-ts/src/plugins/@tanstack/svelte-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 3 + import type { Casing, NameTransformer } from '~/utils/naming'; 4 4 5 5 export type UserConfig = Plugin.Name<'@tanstack/svelte-query'> & 6 6 Plugin.Hooks & { ··· 9 9 * 10 10 * @default 'camelCase' 11 11 */ 12 - case?: StringCase; 12 + case?: Casing; 13 13 /** 14 14 * Add comments from SDK functions to the generated TanStack Query code? 15 15 * ··· 41 41 */ 42 42 infiniteQueryKeys?: 43 43 | boolean 44 - | StringName 44 + | NameTransformer 45 45 | { 46 46 /** 47 47 * The casing convention to use for generated names. 48 48 * 49 49 * @default 'camelCase' 50 50 */ 51 - case?: StringCase; 51 + case?: Casing; 52 52 /** 53 53 * Whether to generate infinite query key helpers. 54 54 * ··· 62 62 * @default '{{name}}InfiniteQueryKey' 63 63 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createinfinitequery 64 64 */ 65 - name?: StringName; 65 + name?: NameTransformer; 66 66 /** 67 67 * Whether to include operation tags in infinite query keys. 68 68 * This will make query keys larger but provides better cache invalidation capabilities. ··· 85 85 */ 86 86 infiniteQueryOptions?: 87 87 | boolean 88 - | StringName 88 + | NameTransformer 89 89 | { 90 90 /** 91 91 * The casing convention to use for generated names. 92 92 * 93 93 * @default 'camelCase' 94 94 */ 95 - case?: StringCase; 95 + case?: Casing; 96 96 /** 97 97 * Whether to generate infinite query options helpers. 98 98 * ··· 106 106 * @returns A meta object with any properties you want to include 107 107 * 108 108 * @example 109 - * ```typescript 109 + * ```ts 110 110 * meta: (operation) => ({ 111 111 * customField: operation.id, 112 112 * isDeprecated: operation.deprecated, ··· 128 128 * @default '{{name}}InfiniteOptions' 129 129 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createinfinitequery 130 130 */ 131 - name?: StringName; 131 + name?: NameTransformer; 132 132 }; 133 133 /** 134 134 * Configuration for generated mutation options helpers. ··· 144 144 */ 145 145 mutationOptions?: 146 146 | boolean 147 - | StringName 147 + | NameTransformer 148 148 | { 149 149 /** 150 150 * The casing convention to use for generated names. 151 151 * 152 152 * @default 'camelCase' 153 153 */ 154 - case?: StringCase; 154 + case?: Casing; 155 155 /** 156 156 * Whether to generate mutation options helpers. 157 157 * ··· 165 165 * @returns A meta object with any properties you want to include 166 166 * 167 167 * @example 168 - * ```typescript 168 + * ```ts 169 169 * meta: (operation) => ({ 170 170 * customField: operation.id, 171 171 * isDeprecated: operation.deprecated, ··· 187 187 * @default '{{name}}Mutation' 188 188 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createmutation 189 189 */ 190 - name?: StringName; 190 + name?: NameTransformer; 191 191 }; 192 192 /** 193 193 * Configuration for generated query keys. ··· 203 203 */ 204 204 queryKeys?: 205 205 | boolean 206 - | StringName 206 + | NameTransformer 207 207 | { 208 208 /** 209 209 * The casing convention to use for generated names. 210 210 * 211 211 * @default 'camelCase' 212 212 */ 213 - case?: StringCase; 213 + case?: Casing; 214 214 /** 215 215 * Whether to generate query keys. 216 216 * ··· 224 224 * @default '{{name}}QueryKey' 225 225 * @see https://tanstack.com/query/v5/docs/framework/react/guides/query-keys 226 226 */ 227 - name?: StringName; 227 + name?: NameTransformer; 228 228 /** 229 229 * Whether to include operation tags in query keys. 230 230 * This will make query keys larger but provides better cache invalidation capabilities. ··· 247 247 */ 248 248 queryOptions?: 249 249 | boolean 250 - | StringName 250 + | NameTransformer 251 251 | { 252 252 /** 253 253 * The casing convention to use for generated names. 254 254 * 255 255 * @default 'camelCase' 256 256 */ 257 - case?: StringCase; 257 + case?: Casing; 258 258 /** 259 259 * Whether to generate query options helpers. 260 260 * ··· 274 274 * @returns A meta object with any properties you want to include 275 275 * 276 276 * @example 277 - * ```typescript 277 + * ```ts 278 278 * meta: (operation) => ({ 279 279 * customField: operation.id, 280 280 * isDeprecated: operation.deprecated, ··· 296 296 * @default '{{name}}Options' 297 297 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createquery 298 298 */ 299 - name?: StringName; 299 + name?: NameTransformer; 300 300 }; 301 301 }; 302 302 ··· 307 307 * 308 308 * @default 'camelCase' 309 309 */ 310 - case: StringCase; 310 + case: Casing; 311 311 /** 312 312 * Add comments from SDK functions to the generated TanStack Query code? 313 313 * ··· 331 331 * 332 332 * @default 'camelCase' 333 333 */ 334 - case: StringCase; 334 + case: Casing; 335 335 /** 336 336 * Whether to generate infinite query key helpers. 337 337 * ··· 345 345 * @default '{{name}}InfiniteQueryKey' 346 346 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createinfinitequery 347 347 */ 348 - name: StringName; 348 + name: NameTransformer; 349 349 /** 350 350 * Whether to include operation tags in infinite query keys. 351 351 * This will make query keys larger but provides better cache invalidation capabilities. ··· 365 365 * 366 366 * @default 'camelCase' 367 367 */ 368 - case: StringCase; 368 + case: Casing; 369 369 /** 370 370 * Whether to generate infinite query options helpers. 371 371 * ··· 379 379 * @returns A meta object with any properties you want to include 380 380 * 381 381 * @example 382 - * ```typescript 382 + * ```ts 383 383 * meta: (operation) => ({ 384 384 * customField: operation.id, 385 385 * isDeprecated: operation.deprecated, ··· 401 401 * @default '{{name}}InfiniteOptions' 402 402 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createinfinitequery 403 403 */ 404 - name: StringName; 404 + name: NameTransformer; 405 405 }; 406 406 /** 407 407 * Resolved configuration for generated mutation options helpers. ··· 414 414 * 415 415 * @default 'camelCase' 416 416 */ 417 - case: StringCase; 417 + case: Casing; 418 418 /** 419 419 * Whether to generate mutation options helpers. 420 420 * ··· 428 428 * @returns A meta object with any properties you want to include 429 429 * 430 430 * @example 431 - * ```typescript 431 + * ```ts 432 432 * meta: (operation) => ({ 433 433 * customField: operation.id, 434 434 * isDeprecated: operation.deprecated, ··· 450 450 * @default '{{name}}Mutation' 451 451 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createmutation 452 452 */ 453 - name: StringName; 453 + name: NameTransformer; 454 454 }; 455 455 /** 456 456 * Resolved configuration for generated query keys. ··· 463 463 * 464 464 * @default 'camelCase' 465 465 */ 466 - case: StringCase; 466 + case: Casing; 467 467 /** 468 468 * Whether to generate query keys. 469 469 * ··· 477 477 * @default '{{name}}QueryKey' 478 478 * @see https://tanstack.com/query/v5/docs/framework/react/guides/query-keys 479 479 */ 480 - name: StringName; 480 + name: NameTransformer; 481 481 /** 482 482 * Whether to include operation tags in query keys. 483 483 * This will make query keys larger but provides better cache invalidation capabilities. ··· 497 497 * 498 498 * @default 'camelCase' 499 499 */ 500 - case: StringCase; 500 + case: Casing; 501 501 /** 502 502 * Whether to generate query options helpers. 503 503 * ··· 517 517 * @returns A meta object with any properties you want to include 518 518 * 519 519 * @example 520 - * ```typescript 520 + * ```ts 521 521 * meta: (operation) => ({ 522 522 * customField: operation.id, 523 523 * isDeprecated: operation.deprecated, ··· 539 539 * @default '{{name}}Options' 540 540 * @see https://tanstack.com/query/v5/docs/framework/svelte/reference/functions/createquery 541 541 */ 542 - name: StringName; 542 + name: NameTransformer; 543 543 }; 544 544 }; 545 545
+34 -34
packages/openapi-ts/src/plugins/@tanstack/vue-query/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 3 + import type { Casing, NameTransformer } from '~/utils/naming'; 4 4 5 5 export type UserConfig = Plugin.Name<'@tanstack/vue-query'> & 6 6 Plugin.Hooks & { ··· 9 9 * 10 10 * @default 'camelCase' 11 11 */ 12 - case?: StringCase; 12 + case?: Casing; 13 13 /** 14 14 * Add comments from SDK functions to the generated TanStack Query code? 15 15 * ··· 41 41 */ 42 42 infiniteQueryKeys?: 43 43 | boolean 44 - | StringName 44 + | NameTransformer 45 45 | { 46 46 /** 47 47 * The casing convention to use for generated names. 48 48 * 49 49 * @default 'camelCase' 50 50 */ 51 - case?: StringCase; 51 + case?: Casing; 52 52 /** 53 53 * Whether to generate infinite query key helpers. 54 54 * ··· 62 62 * @default '{{name}}InfiniteQueryKey' 63 63 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/infiniteQueryOptions 64 64 */ 65 - name?: StringName; 65 + name?: NameTransformer; 66 66 /** 67 67 * Whether to include operation tags in infinite query keys. 68 68 * This will make query keys larger but provides better cache invalidation capabilities. ··· 85 85 */ 86 86 infiniteQueryOptions?: 87 87 | boolean 88 - | StringName 88 + | NameTransformer 89 89 | { 90 90 /** 91 91 * The casing convention to use for generated names. 92 92 * 93 93 * @default 'camelCase' 94 94 */ 95 - case?: StringCase; 95 + case?: Casing; 96 96 /** 97 97 * Whether to generate infinite query options helpers. 98 98 * ··· 107 107 * @returns A meta object with any properties you want to include 108 108 * 109 109 * @example 110 - * ```typescript 110 + * ```ts 111 111 * meta: (operation) => ({ 112 112 * customField: operation.id, 113 113 * isDeprecated: operation.deprecated, ··· 129 129 * @default '{{name}}InfiniteOptions' 130 130 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/infiniteQueryOptions 131 131 */ 132 - name?: StringName; 132 + name?: NameTransformer; 133 133 }; 134 134 /** 135 135 * Configuration for generated mutation options helpers. ··· 145 145 */ 146 146 mutationOptions?: 147 147 | boolean 148 - | StringName 148 + | NameTransformer 149 149 | { 150 150 /** 151 151 * The casing convention to use for generated names. 152 152 * 153 153 * @default 'camelCase' 154 154 */ 155 - case?: StringCase; 155 + case?: Casing; 156 156 /** 157 157 * Whether to generate mutation options helpers. 158 158 * ··· 167 167 * @returns A meta object with any properties you want to include 168 168 * 169 169 * @example 170 - * ```typescript 170 + * ```ts 171 171 * meta: (operation) => ({ 172 172 * customField: operation.id, 173 173 * isDeprecated: operation.deprecated, ··· 189 189 * @default '{{name}}Mutation' 190 190 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/useMutation 191 191 */ 192 - name?: StringName; 192 + name?: NameTransformer; 193 193 }; 194 194 /** 195 195 * Configuration for generated query keys. ··· 205 205 */ 206 206 queryKeys?: 207 207 | boolean 208 - | StringName 208 + | NameTransformer 209 209 | { 210 210 /** 211 211 * The casing convention to use for generated names. 212 212 * 213 213 * @default 'camelCase' 214 214 */ 215 - case?: StringCase; 215 + case?: Casing; 216 216 /** 217 217 * Whether to generate query keys. 218 218 * ··· 226 226 * @default '{{name}}QueryKey' 227 227 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/queryKey 228 228 */ 229 - name?: StringName; 229 + name?: NameTransformer; 230 230 /** 231 231 * Whether to include operation tags in query keys. 232 232 * This will make query keys larger but provides better cache invalidation capabilities. ··· 249 249 */ 250 250 queryOptions?: 251 251 | boolean 252 - | StringName 252 + | NameTransformer 253 253 | { 254 254 /** 255 255 * The casing convention to use for generated names. 256 256 * 257 257 * @default 'camelCase' 258 258 */ 259 - case?: StringCase; 259 + case?: Casing; 260 260 /** 261 261 * Whether to generate query options helpers. 262 262 * ··· 277 277 * @returns A meta object with any properties you want to include 278 278 * 279 279 * @example 280 - * ```typescript 280 + * ```ts 281 281 * meta: (operation) => ({ 282 282 * customField: operation.id, 283 283 * isDeprecated: operation.deprecated, ··· 299 299 * @default '{{name}}Options' 300 300 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/queryOptions 301 301 */ 302 - name?: StringName; 302 + name?: NameTransformer; 303 303 }; 304 304 }; 305 305 ··· 310 310 * 311 311 * @default 'camelCase' 312 312 */ 313 - case: StringCase; 313 + case: Casing; 314 314 /** 315 315 * Add comments from SDK functions to the generated TanStack Query code? 316 316 * ··· 334 334 * 335 335 * @default 'camelCase' 336 336 */ 337 - case: StringCase; 337 + case: Casing; 338 338 /** 339 339 * Whether to generate infinite query key helpers. 340 340 * ··· 348 348 * @default '{{name}}InfiniteQueryKey' 349 349 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/infiniteQueryOptions 350 350 */ 351 - name: StringName; 351 + name: NameTransformer; 352 352 /** 353 353 * Whether to include operation tags in infinite query keys. 354 354 * This will make query keys larger but provides better cache invalidation capabilities. ··· 368 368 * 369 369 * @default 'camelCase' 370 370 */ 371 - case: StringCase; 371 + case: Casing; 372 372 /** 373 373 * Whether to generate infinite query options helpers. 374 374 * ··· 383 383 * @returns A meta object with any properties you want to include 384 384 * 385 385 * @example 386 - * ```typescript 386 + * ```ts 387 387 * meta: (operation) => ({ 388 388 * customField: operation.id, 389 389 * isDeprecated: operation.deprecated, ··· 405 405 * @default '{{name}}InfiniteOptions' 406 406 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/infiniteQueryOptions 407 407 */ 408 - name: StringName; 408 + name: NameTransformer; 409 409 }; 410 410 /** 411 411 * Resolved configuration for generated mutation options helpers. ··· 418 418 * 419 419 * @default 'camelCase' 420 420 */ 421 - case: StringCase; 421 + case: Casing; 422 422 /** 423 423 * Whether to generate mutation options helpers. 424 424 * ··· 433 433 * @returns A meta object with any properties you want to include 434 434 * 435 435 * @example 436 - * ```typescript 436 + * ```ts 437 437 * meta: (operation) => ({ 438 438 * customField: operation.id, 439 439 * isDeprecated: operation.deprecated, ··· 455 455 * @default '{{name}}Mutation' 456 456 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/useMutation 457 457 */ 458 - name: StringName; 458 + name: NameTransformer; 459 459 }; 460 460 /** 461 461 * Resolved configuration for generated query keys. ··· 468 468 * 469 469 * @default 'camelCase' 470 470 */ 471 - case: StringCase; 471 + case: Casing; 472 472 /** 473 473 * Whether to generate query keys. 474 474 * ··· 482 482 * @default '{{name}}QueryKey' 483 483 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/queryKey 484 484 */ 485 - name: StringName; 485 + name: NameTransformer; 486 486 /** 487 487 * Whether to include operation tags in query keys. 488 488 * This will make query keys larger but provides better cache invalidation capabilities. ··· 502 502 * 503 503 * @default 'camelCase' 504 504 */ 505 - case: StringCase; 505 + case: Casing; 506 506 /** 507 507 * Whether to generate query options helpers. 508 508 * ··· 523 523 * @returns A meta object with any properties you want to include 524 524 * 525 525 * @example 526 - * ```typescript 526 + * ```ts 527 527 * meta: (operation) => ({ 528 528 * customField: operation.id, 529 529 * isDeprecated: operation.deprecated, ··· 545 545 * @default '{{name}}Options' 546 546 * @see https://tanstack.com/query/v5/docs/framework/vue/reference/queryOptions 547 547 */ 548 - name: StringName; 548 + name: NameTransformer; 549 549 }; 550 550 }; 551 551
+46 -46
packages/openapi-ts/src/plugins/arktype/types.d.ts
··· 1 1 import type { DefinePlugin, Plugin } from '~/plugins'; 2 - import type { StringCase, StringName } from '~/types/case'; 2 + import type { Casing, NameTransformer } from '~/utils/naming'; 3 3 4 4 import type { IApi } from './api'; 5 5 ··· 10 10 * 11 11 * @default 'PascalCase' 12 12 */ 13 - case?: StringCase; 13 + case?: Casing; 14 14 /** 15 15 * Add comments from input to the generated Arktype schemas? 16 16 * ··· 30 30 */ 31 31 definitions?: 32 32 | boolean 33 - | StringName 33 + | NameTransformer 34 34 | { 35 35 /** 36 36 * The casing convention to use for generated names. 37 37 * 38 38 * @default 'PascalCase' 39 39 */ 40 - case?: StringCase; 40 + case?: Casing; 41 41 /** 42 42 * Whether to generate Arktype schemas for reusable definitions. 43 43 * ··· 50 50 * 51 51 * @default '{{name}}' 52 52 */ 53 - name?: StringName; 53 + name?: NameTransformer; 54 54 /** 55 55 * Configuration for TypeScript type generation from Arktype schemas. 56 56 * ··· 69 69 */ 70 70 infer?: 71 71 | boolean 72 - | StringName 72 + | NameTransformer 73 73 | { 74 74 /** 75 75 * The casing convention to use for generated type names. 76 76 * 77 77 * @default 'PascalCase' 78 78 */ 79 - case?: StringCase; 79 + case?: Casing; 80 80 /** 81 81 * Whether to generate TypeScript types from Arktype schemas. 82 82 * ··· 89 89 * 90 90 * @default '{{name}}' 91 91 */ 92 - name?: StringName; 92 + name?: NameTransformer; 93 93 }; 94 94 }; 95 95 }; ··· 123 123 */ 124 124 requests?: 125 125 | boolean 126 - | StringName 126 + | NameTransformer 127 127 | { 128 128 /** 129 129 * The casing convention to use for generated names. 130 130 * 131 131 * @default 'PascalCase' 132 132 */ 133 - case?: StringCase; 133 + case?: Casing; 134 134 /** 135 135 * Whether to generate Arktype schemas for request definitions. 136 136 * ··· 143 143 * 144 144 * @default '{{name}}Data' 145 145 */ 146 - name?: StringName; 146 + name?: NameTransformer; 147 147 /** 148 148 * Configuration for TypeScript type generation from Arktype schemas. 149 149 * ··· 162 162 */ 163 163 infer?: 164 164 | boolean 165 - | StringName 165 + | NameTransformer 166 166 | { 167 167 /** 168 168 * The casing convention to use for generated type names. 169 169 * 170 170 * @default 'PascalCase' 171 171 */ 172 - case?: StringCase; 172 + case?: Casing; 173 173 /** 174 174 * Whether to generate TypeScript types from Arktype schemas. 175 175 * ··· 182 182 * 183 183 * @default '{{name}}Data' 184 184 */ 185 - name?: StringName; 185 + name?: NameTransformer; 186 186 }; 187 187 }; 188 188 }; ··· 201 201 */ 202 202 responses?: 203 203 | boolean 204 - | StringName 204 + | NameTransformer 205 205 | { 206 206 /** 207 207 * The casing convention to use for generated names. 208 208 * 209 209 * @default 'PascalCase' 210 210 */ 211 - case?: StringCase; 211 + case?: Casing; 212 212 /** 213 213 * Whether to generate Arktype schemas for response definitions. 214 214 * ··· 221 221 * 222 222 * @default '{{name}}Response' 223 223 */ 224 - name?: StringName; 224 + name?: NameTransformer; 225 225 /** 226 226 * Configuration for TypeScript type generation from Arktype schemas. 227 227 * ··· 240 240 */ 241 241 infer?: 242 242 | boolean 243 - | StringName 243 + | NameTransformer 244 244 | { 245 245 /** 246 246 * The casing convention to use for generated type names. 247 247 * 248 248 * @default 'PascalCase' 249 249 */ 250 - case?: StringCase; 250 + case?: Casing; 251 251 /** 252 252 * Whether to generate TypeScript types from Arktype schemas. 253 253 * ··· 260 260 * 261 261 * @default '{{name}}Response' 262 262 */ 263 - name?: StringName; 263 + name?: NameTransformer; 264 264 }; 265 265 }; 266 266 }; ··· 282 282 */ 283 283 infer?: 284 284 | boolean 285 - | StringName 285 + | NameTransformer 286 286 | { 287 287 /** 288 288 * The casing convention to use for generated type names. 289 289 * 290 290 * @default 'PascalCase' 291 291 */ 292 - case?: StringCase; 292 + case?: Casing; 293 293 /** 294 294 * Whether to generate TypeScript types from Arktype schemas. 295 295 * ··· 312 312 */ 313 313 webhooks?: 314 314 | boolean 315 - | StringName 315 + | NameTransformer 316 316 | { 317 317 /** 318 318 * The casing convention to use for generated names. 319 319 * 320 320 * @default 'PascalCase' 321 321 */ 322 - case?: StringCase; 322 + case?: Casing; 323 323 /** 324 324 * Whether to generate Arktype schemas for webhook definitions. 325 325 * ··· 332 332 * 333 333 * @default '{{name}}WebhookRequest' 334 334 */ 335 - name?: StringName; 335 + name?: NameTransformer; 336 336 /** 337 337 * Configuration for TypeScript type generation from Arktype schemas. 338 338 * ··· 351 351 */ 352 352 infer?: 353 353 | boolean 354 - | StringName 354 + | NameTransformer 355 355 | { 356 356 /** 357 357 * The casing convention to use for generated type names. 358 358 * 359 359 * @default 'PascalCase' 360 360 */ 361 - case?: StringCase; 361 + case?: Casing; 362 362 /** 363 363 * Whether to generate TypeScript types from Arktype schemas. 364 364 * ··· 371 371 * 372 372 * @default '{{name}}WebhookRequest' 373 373 */ 374 - name?: StringName; 374 + name?: NameTransformer; 375 375 }; 376 376 }; 377 377 }; ··· 384 384 * 385 385 * @default 'PascalCase' 386 386 */ 387 - case: StringCase; 387 + case: Casing; 388 388 /** 389 389 * Add comments from input to the generated Arktype schemas? 390 390 * ··· 403 403 * 404 404 * @default 'PascalCase' 405 405 */ 406 - case: StringCase; 406 + case: Casing; 407 407 /** 408 408 * Whether to generate Arktype schemas for reusable definitions. 409 409 * ··· 416 416 * 417 417 * @default '{{name}}' 418 418 */ 419 - name: StringName; 419 + name: NameTransformer; 420 420 /** 421 421 * Configuration for TypeScript type generation from Arktype schemas. 422 422 * ··· 432 432 * 433 433 * @default 'PascalCase' 434 434 */ 435 - case: StringCase; 435 + case: Casing; 436 436 /** 437 437 * Whether to generate TypeScript types from Arktype schemas. 438 438 * ··· 445 445 * 446 446 * @default '{{name}}' 447 447 */ 448 - name: StringName; 448 + name: NameTransformer; 449 449 }; 450 450 }; 451 451 }; ··· 476 476 * 477 477 * @default 'PascalCase' 478 478 */ 479 - case: StringCase; 479 + case: Casing; 480 480 /** 481 481 * Whether to generate Arktype schemas for request definitions. 482 482 * ··· 489 489 * 490 490 * @default '{{name}}Data' 491 491 */ 492 - name: StringName; 492 + name: NameTransformer; 493 493 /** 494 494 * Configuration for TypeScript type generation from Arktype schemas. 495 495 * ··· 505 505 * 506 506 * @default 'PascalCase' 507 507 */ 508 - case: StringCase; 508 + case: Casing; 509 509 /** 510 510 * Whether to generate TypeScript types from Arktype schemas. 511 511 * ··· 518 518 * 519 519 * @default '{{name}}Data' 520 520 */ 521 - name: StringName; 521 + name: NameTransformer; 522 522 }; 523 523 }; 524 524 }; ··· 534 534 * 535 535 * @default 'PascalCase' 536 536 */ 537 - case: StringCase; 537 + case: Casing; 538 538 /** 539 539 * Whether to generate Arktype schemas for response definitions. 540 540 * ··· 547 547 * 548 548 * @default '{{name}}Response' 549 549 */ 550 - name: StringName; 550 + name: NameTransformer; 551 551 /** 552 552 * Configuration for TypeScript type generation from Arktype schemas. 553 553 * ··· 563 563 * 564 564 * @default 'PascalCase' 565 565 */ 566 - case: StringCase; 566 + case: Casing; 567 567 /** 568 568 * Whether to generate TypeScript types from Arktype schemas. 569 569 * ··· 576 576 * 577 577 * @default '{{name}}Response' 578 578 */ 579 - name: StringName; 579 + name: NameTransformer; 580 580 }; 581 581 }; 582 582 }; ··· 595 595 * 596 596 * @default 'PascalCase' 597 597 */ 598 - case: StringCase; 598 + case: Casing; 599 599 /** 600 600 * Whether to generate TypeScript types from Arktype schemas. 601 601 * ··· 615 615 * 616 616 * @default 'PascalCase' 617 617 */ 618 - case: StringCase; 618 + case: Casing; 619 619 /** 620 620 * Whether to generate Arktype schemas for webhook definitions. 621 621 * ··· 628 628 * 629 629 * @default '{{name}}WebhookRequest' 630 630 */ 631 - name: StringName; 631 + name: NameTransformer; 632 632 /** 633 633 * Configuration for TypeScript type generation from Arktype schemas. 634 634 * ··· 644 644 * 645 645 * @default 'PascalCase' 646 646 */ 647 - case: StringCase; 647 + case: Casing; 648 648 /** 649 649 * Whether to generate TypeScript types from Arktype schemas. 650 650 * ··· 657 657 * 658 658 * @default '{{name}}WebhookRequest' 659 659 */ 660 - name: StringName; 660 + name: NameTransformer; 661 661 }; 662 662 }; 663 663 };
+26 -29
packages/openapi-ts/src/plugins/arktype/v2/plugin.ts
··· 3 3 4 4 import { deduplicateSchema } from '~/ir/schema'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { SchemaWithType } from '~/plugins/shared/types/schema'; 8 7 import { $ } from '~/ts-dsl'; 8 + import { applyNaming } from '~/utils/naming'; 9 9 import { pathToJsonPointer, refToName } from '~/utils/ref'; 10 10 11 11 import { exportAst } from '../shared/export'; ··· 242 242 const $ref = pathToJsonPointer(fromRef(state.path)); 243 243 const ast = irSchemaToAst({ plugin, schema, state }); 244 244 const baseName = refToName($ref); 245 - const symbol = plugin.registerSymbol({ 246 - meta: { 247 - category: 'schema', 248 - path: fromRef(state.path), 249 - resource: 'definition', 250 - resourceId: $ref, 251 - tags: fromRef(state.tags), 252 - tool: 'arktype', 245 + const symbol = plugin.symbol( 246 + applyNaming(baseName, plugin.config.definitions), 247 + { 248 + meta: { 249 + category: 'schema', 250 + path: fromRef(state.path), 251 + resource: 'definition', 252 + resourceId: $ref, 253 + tags: fromRef(state.tags), 254 + tool: 'arktype', 255 + }, 253 256 }, 254 - name: buildName({ 255 - config: plugin.config.definitions, 256 - name: baseName, 257 - }), 258 - }); 257 + ); 259 258 const typeInferSymbol = plugin.config.definitions.types.infer.enabled 260 - ? plugin.registerSymbol({ 261 - meta: { 262 - category: 'type', 263 - path: fromRef(state.path), 264 - resource: 'definition', 265 - resourceId: $ref, 266 - tool: 'arktype', 267 - variant: 'infer', 259 + ? plugin.symbol( 260 + applyNaming(baseName, plugin.config.definitions.types.infer), 261 + { 262 + meta: { 263 + category: 'type', 264 + path: fromRef(state.path), 265 + resource: 'definition', 266 + resourceId: $ref, 267 + tool: 'arktype', 268 + variant: 'infer', 269 + }, 268 270 }, 269 - name: buildName({ 270 - config: plugin.config.definitions.types.infer, 271 - name: baseName, 272 - }), 273 - }) 271 + ) 274 272 : undefined; 275 273 exportAst({ 276 274 ast, ··· 282 280 }; 283 281 284 282 export const handlerV2: ArktypePlugin['Handler'] = ({ plugin }) => { 285 - plugin.registerSymbol({ 283 + plugin.symbol('type', { 286 284 external: 'arktype', 287 285 meta: { 288 286 category: 'external', 289 287 resource: 'arktype.type', 290 288 }, 291 - name: 'type', 292 289 }); 293 290 294 291 plugin.forEach(
+2 -5
packages/openapi-ts/src/plugins/fastify/plugin.ts
··· 130 130 }; 131 131 132 132 export const handler: FastifyPlugin['Handler'] = ({ plugin }) => { 133 - plugin.registerSymbol({ 133 + plugin.symbol('RouteHandler', { 134 134 external: 'fastify', 135 135 kind: 'type', 136 136 meta: { ··· 138 138 resource: 'route-handler', 139 139 tool: 'fastify', 140 140 }, 141 - name: 'RouteHandler', 142 141 }); 143 142 144 - const symbolRouteHandlers = plugin.registerSymbol({ 145 - name: 'RouteHandlers', 146 - }); 143 + const symbolRouteHandlers = plugin.symbol('RouteHandlers'); 147 144 148 145 const type = $.type.object(); 149 146
+1 -1
packages/openapi-ts/src/plugins/shared/utils/config.ts
··· 25 25 */ 26 26 export const mappers = { 27 27 boolean: (enabled: boolean) => ({ enabled }), 28 - function: (name: (...args: any[]) => any) => ({ name }), 28 + function: (name: (...args: Array<any>) => any) => ({ name }), 29 29 string: (name: string) => ({ name }), 30 30 } as const;
+12 -8
packages/openapi-ts/src/plugins/shared/utils/instance.ts
··· 25 25 import type { Hooks } from '~/parser/types/hooks'; 26 26 import type { Plugin } from '~/plugins'; 27 27 import type { PluginConfigMap } from '~/plugins/config'; 28 + import type { TsDsl } from '~/ts-dsl'; 28 29 import { jsonPointerToPath } from '~/utils/ref'; 29 30 30 31 import type { BaseEvent, WalkEvent } from '../types/instance'; ··· 334 335 return result as T extends number ? void : number; 335 336 } 336 337 337 - querySymbol(filter: SymbolMeta): Symbol | undefined { 338 - return this.gen.symbols.query(filter)[0]; 338 + querySymbol(filter: SymbolMeta): Symbol<TsDsl> | undefined { 339 + return this.gen.symbols.query(filter)[0] as Symbol<TsDsl> | undefined; 339 340 } 340 341 341 - referenceSymbol(meta: SymbolMeta): Symbol { 342 - return this.gen.symbols.reference(meta); 342 + referenceSymbol(meta: SymbolMeta): Symbol<TsDsl> { 343 + return this.gen.symbols.reference(meta) as Symbol<TsDsl>; 343 344 } 344 345 345 346 /** 346 347 * @deprecated use `plugin.symbol()` instead 347 348 */ 348 - registerSymbol(symbol: SymbolIn): Symbol { 349 - return this.symbol(symbol.name, symbol); 349 + registerSymbol(symbol: SymbolIn): Symbol<TsDsl> { 350 + return this.symbol(symbol.name, symbol) as Symbol<TsDsl>; 350 351 } 351 352 352 353 /** ··· 362 363 } 363 364 } 364 365 365 - symbol(name: SymbolIn['name'], symbol?: Omit<SymbolIn, 'name'>): Symbol { 366 + symbol( 367 + name: SymbolIn['name'], 368 + symbol?: Omit<SymbolIn, 'name'>, 369 + ): Symbol<TsDsl> { 366 370 const symbolIn: SymbolIn = { 367 371 ...symbol, 368 372 exportFrom: ··· 386 390 for (const hook of this.eventHooks['symbol:register:after']) { 387 391 hook({ plugin: this, symbol: symbolOut }); 388 392 } 389 - return symbolOut; 393 + return symbolOut as Symbol<TsDsl>; 390 394 } 391 395 392 396 /**
+11 -1
packages/openapi-ts/src/plugins/shared/utils/operation.ts
··· 2 2 import { hasOperationDataRequired } from '~/ir/operation'; 3 3 import type { IR } from '~/ir/types'; 4 4 import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; 5 + import type { HeyApiSdkPlugin } from '~/plugins/@hey-api/sdk'; 6 + import { isInstance } from '~/plugins/@hey-api/sdk/v1/node'; 5 7 import { escapeComment } from '~/utils/escape'; 6 8 7 9 export const createOperationComment = ( ··· 32 34 return comments.length ? comments : undefined; 33 35 }; 34 36 37 + /** 38 + * TODO: replace with plugin logic... 39 + * 40 + * @deprecated this needs to be refactored 41 + */ 35 42 export const isOperationOptionsRequired = ({ 36 43 context, 37 44 operation, ··· 43 50 const isNuxtClient = client.name === '@hey-api/client-nuxt'; 44 51 const plugin = context.config.plugins['@hey-api/sdk']; 45 52 if (plugin) { 46 - if (!plugin.config.client && !plugin.config.instance) { 53 + if ( 54 + !plugin.config.client && 55 + !isInstance(plugin as unknown as HeyApiSdkPlugin['Instance']) 56 + ) { 47 57 return true; 48 58 } 49 59 if (plugin.config.paramsStructure === 'flat') {
+39 -39
packages/openapi-ts/src/plugins/swr/types.d.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 2 import type { DefinePlugin, Plugin } from '~/plugins'; 3 - import type { StringCase, StringName } from '~/types/case'; 3 + import type { Casing, NameTransformer } from '~/utils/naming'; 4 4 5 5 export type UserConfig = Plugin.Name<'swr'> & 6 6 Plugin.Hooks & { ··· 9 9 * 10 10 * @default 'camelCase' 11 11 */ 12 - case?: StringCase; 12 + case?: Casing; 13 13 /** 14 14 * Add comments from SDK functions to the generated SWR code? 15 15 * ··· 42 42 */ 43 43 infiniteQueryKeys?: 44 44 | boolean 45 - | StringName 45 + | NameTransformer 46 46 | { 47 47 /** 48 48 * The casing convention to use for generated names. 49 49 * 50 50 * @default 'camelCase' 51 51 */ 52 - case?: StringCase; 52 + case?: Casing; 53 53 /** 54 54 * Whether to generate infinite query key helpers. 55 55 * ··· 64 64 * 65 65 * @default '{{name}}InfiniteQueryKey' 66 66 */ 67 - name?: StringName; 67 + name?: NameTransformer; 68 68 /** 69 69 * Whether to include operation tags in infinite query keys. 70 70 * This will make query keys larger but provides better cache invalidation capabilities. ··· 87 87 */ 88 88 infiniteQueryOptions?: 89 89 | boolean 90 - | StringName 90 + | NameTransformer 91 91 | { 92 92 /** 93 93 * The casing convention to use for generated names. 94 94 * 95 95 * @default 'camelCase' 96 96 */ 97 - case?: StringCase; 97 + case?: Casing; 98 98 /** 99 99 * Whether to generate infinite query options helpers. 100 100 * ··· 109 109 * @returns A meta object with any properties you want to include 110 110 * 111 111 * @example 112 - * ```typescript 112 + * ```ts 113 113 * meta: (operation) => ({ 114 114 * customField: operation.id, 115 115 * isDeprecated: operation.deprecated, ··· 132 132 * 133 133 * @default '{{name}}InfiniteOptions' 134 134 */ 135 - name?: StringName; 135 + name?: NameTransformer; 136 136 }; 137 137 /** 138 138 * Configuration for generated mutation options helpers. ··· 148 148 */ 149 149 mutationOptions?: 150 150 | boolean 151 - | StringName 151 + | NameTransformer 152 152 | { 153 153 /** 154 154 * The casing convention to use for generated names. 155 155 * 156 156 * @default 'camelCase' 157 157 */ 158 - case?: StringCase; 158 + case?: Casing; 159 159 /** 160 160 * Whether to generate mutation options helpers. 161 161 * ··· 170 170 * @returns A meta object with any properties you want to include 171 171 * 172 172 * @example 173 - * ```typescript 173 + * ```ts 174 174 * meta: (operation) => ({ 175 175 * customField: operation.id, 176 176 * isDeprecated: operation.deprecated, ··· 193 193 * 194 194 * @default '{{name}}Mutation' 195 195 */ 196 - name?: StringName; 196 + name?: NameTransformer; 197 197 }; 198 198 /** 199 199 * Configuration for generated query keys. ··· 209 209 */ 210 210 queryKeys?: 211 211 | boolean 212 - | StringName 212 + | NameTransformer 213 213 | { 214 214 /** 215 215 * The casing convention to use for generated names. 216 216 * 217 217 * @default 'camelCase' 218 218 */ 219 - case?: StringCase; 219 + case?: Casing; 220 220 /** 221 221 * Whether to generate query keys. 222 222 * ··· 231 231 * 232 232 * @default '{{name}}QueryKey' 233 233 */ 234 - name?: StringName; 234 + name?: NameTransformer; 235 235 /** 236 236 * Whether to include operation tags in query keys. 237 237 * This will make query keys larger but provides better cache invalidation capabilities. ··· 254 254 */ 255 255 queryOptions?: 256 256 | boolean 257 - | StringName 257 + | NameTransformer 258 258 | { 259 259 /** 260 260 * The casing convention to use for generated names. 261 261 * 262 262 * @default 'camelCase' 263 263 */ 264 - case?: StringCase; 264 + case?: Casing; 265 265 /** 266 266 * Whether to generate query options helpers. 267 267 * ··· 282 282 * @returns A meta object with any properties you want to include 283 283 * 284 284 * @example 285 - * ```typescript 285 + * ```ts 286 286 * meta: (operation) => ({ 287 287 * customField: operation.id, 288 288 * isDeprecated: operation.deprecated, ··· 305 305 * 306 306 * @default '{{name}}Options' 307 307 */ 308 - name?: StringName; 308 + name?: NameTransformer; 309 309 }; 310 310 /** 311 311 * Configuration for generated `useSwr()` function helpers. ··· 321 321 */ 322 322 useSwr?: 323 323 | boolean 324 - | StringName 324 + | NameTransformer 325 325 | { 326 326 /** 327 327 * The casing convention to use for generated names. 328 328 * 329 329 * @default 'camelCase' 330 330 */ 331 - case?: StringCase; 331 + case?: Casing; 332 332 /** 333 333 * Whether to generate `useSwr()` function helpers. 334 334 * ··· 343 343 * 344 344 * @default 'use{{name}}' 345 345 */ 346 - name?: StringName; 346 + name?: NameTransformer; 347 347 }; 348 348 }; 349 349 ··· 354 354 * 355 355 * @default 'camelCase' 356 356 */ 357 - case: StringCase; 357 + case: Casing; 358 358 /** 359 359 * Add comments from SDK functions to the generated SWR code? 360 360 * ··· 378 378 * 379 379 * @default 'camelCase' 380 380 */ 381 - case: StringCase; 381 + case: Casing; 382 382 /** 383 383 * Whether to generate infinite query key helpers. 384 384 * ··· 392 392 * 393 393 * @default '{{name}}InfiniteQueryKey' 394 394 */ 395 - name: StringName; 395 + name: NameTransformer; 396 396 /** 397 397 * Whether to include operation tags in infinite query keys. 398 398 * This will make query keys larger but provides better cache invalidation capabilities. ··· 412 412 * 413 413 * @default 'camelCase' 414 414 */ 415 - case: StringCase; 415 + case: Casing; 416 416 /** 417 417 * Whether to generate infinite query options helpers. 418 418 * ··· 427 427 * @returns A meta object with any properties you want to include 428 428 * 429 429 * @example 430 - * ```typescript 430 + * ```ts 431 431 * meta: (operation) => ({ 432 432 * customField: operation.id, 433 433 * isDeprecated: operation.deprecated, ··· 449 449 * 450 450 * @default '{{name}}InfiniteOptions' 451 451 */ 452 - name: StringName; 452 + name: NameTransformer; 453 453 }; 454 454 /** 455 455 * Resolved configuration for generated mutation options helpers. ··· 462 462 * 463 463 * @default 'camelCase' 464 464 */ 465 - case: StringCase; 465 + case: Casing; 466 466 /** 467 467 * Whether to generate mutation options helpers. 468 468 * ··· 477 477 * @returns A meta object with any properties you want to include 478 478 * 479 479 * @example 480 - * ```typescript 480 + * ```ts 481 481 * meta: (operation) => ({ 482 482 * customField: operation.id, 483 483 * isDeprecated: operation.deprecated, ··· 499 499 * 500 500 * @default '{{name}}Mutation' 501 501 */ 502 - name: StringName; 502 + name: NameTransformer; 503 503 }; 504 504 /** 505 505 * Resolved configuration for generated query keys. ··· 512 512 * 513 513 * @default 'camelCase' 514 514 */ 515 - case: StringCase; 515 + case: Casing; 516 516 /** 517 517 * Whether to generate query keys. 518 518 * ··· 526 526 * 527 527 * @default '{{name}}QueryKey' 528 528 */ 529 - name: StringName; 529 + name: NameTransformer; 530 530 /** 531 531 * Whether to include operation tags in query keys. 532 532 * This will make query keys larger but provides better cache invalidation capabilities. ··· 546 546 * 547 547 * @default 'camelCase' 548 548 */ 549 - case: StringCase; 549 + case: Casing; 550 550 /** 551 551 * Whether to generate query options helpers. 552 552 * ··· 567 567 * @returns A meta object with any properties you want to include 568 568 * 569 569 * @example 570 - * ```typescript 570 + * ```ts 571 571 * meta: (operation) => ({ 572 572 * customField: operation.id, 573 573 * isDeprecated: operation.deprecated, ··· 589 589 * 590 590 * @default '{{name}}Options' 591 591 */ 592 - name: StringName; 592 + name: NameTransformer; 593 593 }; 594 594 /** 595 595 * Configuration for generated `useSwr()` function helpers. ··· 602 602 * 603 603 * @default 'camelCase' 604 604 */ 605 - case: StringCase; 605 + case: Casing; 606 606 /** 607 607 * Whether to generate `useSwr()` function helpers. 608 608 * ··· 617 617 * 618 618 * @default 'use{{name}}' 619 619 */ 620 - name: StringName; 620 + name: NameTransformer; 621 621 }; 622 622 }; 623 623
+4 -46
packages/openapi-ts/src/plugins/swr/v2/plugin.ts
··· 1 - import { registryName } from '~/plugins/@hey-api/sdk/shared/class'; 2 - import { operationClasses } from '~/plugins/@hey-api/sdk/shared/operation'; 3 - import { $ } from '~/ts-dsl'; 4 - import { stringCase } from '~/utils/stringCase'; 5 - 6 1 import type { SwrPlugin } from '../types'; 7 2 import { createUseSwr } from './useSwr'; 8 3 9 4 export const handlerV2: SwrPlugin['Handler'] = ({ plugin }) => { 10 - plugin.registerSymbol({ 5 + plugin.symbol('useSWR', { 11 6 external: 'swr', 12 7 importKind: 'default', 13 8 kind: 'function', ··· 15 10 category: 'external', 16 11 resource: 'swr', 17 12 }, 18 - name: 'useSWR', 19 13 }); 20 - 21 - const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); 22 14 23 15 plugin.forEach( 24 16 'operation', 25 17 ({ operation }) => { 26 - const classes = sdkPlugin.config.asClass 27 - ? operationClasses({ operation, plugin: sdkPlugin }) 28 - : undefined; 29 - const entry = classes ? classes.values().next().value : undefined; 30 - // TODO: this should use class graph to determine correct path string 31 - // as it's really easy to break once we change the class casing 32 - let queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 33 - if (entry) { 34 - const symbolClass = plugin.referenceSymbol({ 35 - category: 'utility', 36 - resource: 'class', 37 - resourceId: entry.path[0], 38 - tool: 'sdk', 39 - }); 40 - queryFn = $(symbolClass).$if(sdkPlugin.config.instance, (e) => 41 - e.attr(registryName).attr('get').call(), 42 - ); 43 - for (const className of entry.path.slice(1)) { 44 - const cls = stringCase({ 45 - case: 'camelCase', 46 - value: className, 47 - }); 48 - queryFn = queryFn.attr(cls); 49 - } 50 - queryFn = queryFn.attr(entry.methodName); 51 - } else { 52 - const symbol = plugin.referenceSymbol({ 53 - category: 'sdk', 54 - resource: 'operation', 55 - resourceId: operation.id, 56 - }); 57 - queryFn = $(symbol); 58 - } 59 - 60 18 if (plugin.hooks.operation.isQuery(operation)) { 61 19 // if (plugin.config.queryOptions.enabled) { 62 - // createQueryOptions({ operation, plugin, queryFn }); 20 + // createQueryOptions({ operation, plugin }); 63 21 // } 64 22 65 23 // if (plugin.config.infiniteQueryOptions.enabled) { 66 - // createInfiniteQueryOptions({ operation, plugin, queryFn }); 24 + // createInfiniteQueryOptions({ operation, plugin }); 67 25 // } 68 26 69 27 if (plugin.config.useSwr.enabled) { 70 - createUseSwr({ operation, plugin, queryFn }); 28 + createUseSwr({ operation, plugin }); 71 29 } 72 30 } 73 31 },
+13 -11
packages/openapi-ts/src/plugins/swr/v2/useSwr.ts
··· 1 1 import type { IR } from '~/ir/types'; 2 - import { buildName } from '~/openApi/shared/utils/name'; 3 2 import { 4 3 createOperationComment, 5 4 hasOperationSse, 6 5 } from '~/plugins/shared/utils/operation'; 7 6 import type { TsDsl } from '~/ts-dsl'; 8 7 import { $ } from '~/ts-dsl'; 8 + import { applyNaming } from '~/utils/naming'; 9 9 10 10 import type { SwrPlugin } from '../types'; 11 11 12 12 export const createUseSwr = ({ 13 13 operation, 14 14 plugin, 15 - queryFn, 16 15 }: { 17 16 operation: IR.OperationObject; 18 17 plugin: SwrPlugin['Instance']; 19 - queryFn: ReturnType<typeof $.expr | typeof $.call | typeof $.attr>; 20 18 }): void => { 21 19 if (hasOperationSse({ operation })) { 22 20 return; ··· 26 24 category: 'external', 27 25 resource: 'swr', 28 26 }); 29 - const symbolUseQueryFn = plugin.registerSymbol({ 30 - name: buildName({ 31 - config: plugin.config.useSwr, 32 - name: operation.id, 33 - }), 34 - }); 27 + const symbolUseQueryFn = plugin.symbol( 28 + applyNaming(operation.id, plugin.config.useSwr), 29 + ); 35 30 36 - const awaitSdkFn = $.lazy(() => 37 - $(queryFn) 31 + const awaitSdkFn = $.lazy((ctx) => 32 + ctx 33 + .access( 34 + plugin.referenceSymbol({ 35 + category: 'sdk', 36 + resource: 'operation', 37 + resourceId: operation.id, 38 + }), 39 + ) 38 40 .call($.object().prop('throwOnError', $.literal(true))) 39 41 .await(), 40 42 );
+27 -29
packages/openapi-ts/src/plugins/valibot/shared/operation.ts
··· 2 2 3 3 import { operationResponsesMap } from '~/ir/operation'; 4 4 import type { IR } from '~/ir/types'; 5 - import { buildName } from '~/openApi/shared/utils/name'; 5 + import { applyNaming } from '~/utils/naming'; 6 6 7 7 import { exportAst } from './export'; 8 8 import type { Ast, IrSchemaToAstOptions } from './types'; ··· 118 118 schemaData.required = [...requiredProperties]; 119 119 120 120 const ast = getAst(schemaData, fromRef(state.path)); 121 - const symbol = plugin.registerSymbol({ 122 - meta: { 123 - category: 'schema', 124 - path: fromRef(state.path), 125 - resource: 'operation', 126 - resourceId: operation.id, 127 - role: 'data', 128 - tags: fromRef(state.tags), 129 - tool: 'valibot', 121 + const symbol = plugin.symbol( 122 + applyNaming(operation.id, plugin.config.requests), 123 + { 124 + meta: { 125 + category: 'schema', 126 + path: fromRef(state.path), 127 + resource: 'operation', 128 + resourceId: operation.id, 129 + role: 'data', 130 + tags: fromRef(state.tags), 131 + tool: 'valibot', 132 + }, 130 133 }, 131 - name: buildName({ 132 - config: plugin.config.requests, 133 - name: operation.id, 134 - }), 135 - }); 134 + ); 136 135 exportAst({ 137 136 ast, 138 137 plugin, ··· 149 148 if (response) { 150 149 const path = [...fromRef(state.path), 'responses']; 151 150 const ast = getAst(response, path); 152 - const symbol = plugin.registerSymbol({ 153 - meta: { 154 - category: 'schema', 155 - path, 156 - resource: 'operation', 157 - resourceId: operation.id, 158 - role: 'responses', 159 - tags: fromRef(state.tags), 160 - tool: 'valibot', 151 + const symbol = plugin.symbol( 152 + applyNaming(operation.id, plugin.config.responses), 153 + { 154 + meta: { 155 + category: 'schema', 156 + path, 157 + resource: 'operation', 158 + resourceId: operation.id, 159 + role: 'responses', 160 + tags: fromRef(state.tags), 161 + tool: 'valibot', 162 + }, 161 163 }, 162 - name: buildName({ 163 - config: plugin.config.responses, 164 - name: operation.id, 165 - }), 166 - }); 164 + ); 167 165 exportAst({ 168 166 ast, 169 167 plugin,
+14 -15
packages/openapi-ts/src/plugins/valibot/shared/webhook.ts
··· 1 1 import { fromRef } from '@hey-api/codegen-core'; 2 2 3 3 import type { IR } from '~/ir/types'; 4 - import { buildName } from '~/openApi/shared/utils/name'; 4 + import { applyNaming } from '~/utils/naming'; 5 5 6 6 import { exportAst } from './export'; 7 7 import type { Ast, IrSchemaToAstOptions } from './types'; ··· 117 117 schemaData.required = [...requiredProperties]; 118 118 119 119 const ast = getAst(schemaData, fromRef(state.path)); 120 - const symbol = plugin.registerSymbol({ 121 - meta: { 122 - category: 'schema', 123 - path: fromRef(state.path), 124 - resource: 'webhook', 125 - resourceId: operation.id, 126 - role: 'data', 127 - tags: fromRef(state.tags), 128 - tool: 'valibot', 120 + const symbol = plugin.symbol( 121 + applyNaming(operation.id, plugin.config.webhooks), 122 + { 123 + meta: { 124 + category: 'schema', 125 + path: fromRef(state.path), 126 + resource: 'webhook', 127 + resourceId: operation.id, 128 + role: 'data', 129 + tags: fromRef(state.tags), 130 + tool: 'valibot', 131 + }, 129 132 }, 130 - name: buildName({ 131 - config: plugin.config.webhooks, 132 - name: operation.id, 133 - }), 134 - }); 133 + ); 135 134 exportAst({ 136 135 ast, 137 136 plugin,
+23 -23
packages/openapi-ts/src/plugins/valibot/types.d.ts
··· 8 8 } from '~/plugins/shared/utils/coerce'; 9 9 import type { GetIntegerLimit } from '~/plugins/shared/utils/formats'; 10 10 import type { $, DollarTsDsl } from '~/ts-dsl'; 11 - import type { StringCase, StringName } from '~/types/case'; 11 + import type { Casing, NameTransformer } from '~/utils/naming'; 12 12 13 13 import type { IApi } from './api'; 14 14 import type { Pipe, PipeResult, PipesUtils } from './shared/pipes'; ··· 22 22 * 23 23 * @default 'camelCase' 24 24 */ 25 - case?: StringCase; 25 + case?: Casing; 26 26 /** 27 27 * Add comments from input to the generated Valibot schemas? 28 28 * ··· 42 42 */ 43 43 definitions?: 44 44 | boolean 45 - | StringName 45 + | NameTransformer 46 46 | { 47 47 /** 48 48 * The casing convention to use for generated names. 49 49 * 50 50 * @default 'camelCase' 51 51 */ 52 - case?: StringCase; 52 + case?: Casing; 53 53 /** 54 54 * Whether to generate Valibot schemas for reusable definitions. 55 55 * ··· 62 62 * 63 63 * @default 'v{{name}}' 64 64 */ 65 - name?: StringName; 65 + name?: NameTransformer; 66 66 }; 67 67 /** 68 68 * Should the exports from the generated files be re-exported in the index ··· 92 92 */ 93 93 requests?: 94 94 | boolean 95 - | StringName 95 + | NameTransformer 96 96 | { 97 97 /** 98 98 * The casing convention to use for generated names. 99 99 * 100 100 * @default 'camelCase' 101 101 */ 102 - case?: StringCase; 102 + case?: Casing; 103 103 /** 104 104 * Whether to generate Valibot schemas for request definitions. 105 105 * ··· 112 112 * 113 113 * @default 'v{{name}}Data' 114 114 */ 115 - name?: StringName; 115 + name?: NameTransformer; 116 116 }; 117 117 /** 118 118 * Configuration for response-specific Valibot schemas. ··· 127 127 */ 128 128 responses?: 129 129 | boolean 130 - | StringName 130 + | NameTransformer 131 131 | { 132 132 /** 133 133 * The casing convention to use for generated names. 134 134 * 135 135 * @default 'camelCase' 136 136 */ 137 - case?: StringCase; 137 + case?: Casing; 138 138 /** 139 139 * Whether to generate Valibot schemas for response definitions. 140 140 * ··· 147 147 * 148 148 * @default 'v{{name}}Response' 149 149 */ 150 - name?: StringName; 150 + name?: NameTransformer; 151 151 }; 152 152 /** 153 153 * Configuration for webhook-specific Valibot schemas. ··· 163 163 */ 164 164 webhooks?: 165 165 | boolean 166 - | StringName 166 + | NameTransformer 167 167 | { 168 168 /** 169 169 * The casing convention to use for generated names. 170 170 * 171 171 * @default 'camelCase' 172 172 */ 173 - case?: StringCase; 173 + case?: Casing; 174 174 /** 175 175 * Whether to generate Valibot schemas for webhook definitions. 176 176 * ··· 183 183 * 184 184 * @default 'v{{name}}WebhookRequest' 185 185 */ 186 - name?: StringName; 186 + name?: NameTransformer; 187 187 }; 188 188 }; 189 189 ··· 195 195 * 196 196 * @default 'camelCase' 197 197 */ 198 - case: StringCase; 198 + case: Casing; 199 199 /** 200 200 * Add comments from input to the generated Valibot schemas? 201 201 * ··· 214 214 * 215 215 * @default 'camelCase' 216 216 */ 217 - case: StringCase; 217 + case: Casing; 218 218 /** 219 219 * Whether to generate Valibot schemas for reusable definitions. 220 220 * ··· 227 227 * 228 228 * @default 'v{{name}}' 229 229 */ 230 - name: StringName; 230 + name: NameTransformer; 231 231 }; 232 232 /** 233 233 * Should the exports from the generated files be re-exported in the index ··· 256 256 * 257 257 * @default 'camelCase' 258 258 */ 259 - case: StringCase; 259 + case: Casing; 260 260 /** 261 261 * Whether to generate Valibot schemas for request definitions. 262 262 * ··· 269 269 * 270 270 * @default 'v{{name}}Data' 271 271 */ 272 - name: StringName; 272 + name: NameTransformer; 273 273 }; 274 274 /** 275 275 * Configuration for response-specific Valibot schemas. ··· 283 283 * 284 284 * @default 'camelCase' 285 285 */ 286 - case: StringCase; 286 + case: Casing; 287 287 /** 288 288 * Whether to generate Valibot schemas for response definitions. 289 289 * ··· 296 296 * 297 297 * @default 'v{{name}}Response' 298 298 */ 299 - name: StringName; 299 + name: NameTransformer; 300 300 }; 301 301 /** 302 302 * Configuration for webhook-specific Valibot schemas. ··· 309 309 * 310 310 * @default 'camelCase' 311 311 */ 312 - case: StringCase; 312 + case: Casing; 313 313 /** 314 314 * Whether to generate Valibot schemas for webhook definitions. 315 315 * ··· 322 322 * 323 323 * @default 'v{{name}}WebhookRequest' 324 324 */ 325 - name: StringName; 325 + name: NameTransformer; 326 326 }; 327 327 }; 328 328
+14 -16
packages/openapi-ts/src/plugins/valibot/v1/plugin.ts
··· 3 3 4 4 import { deduplicateSchema } from '~/ir/schema'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { SchemaWithType } from '~/plugins'; 8 7 import { maybeBigInt } from '~/plugins/shared/utils/coerce'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 import { pathToJsonPointer, refToName } from '~/utils/ref'; 11 11 12 12 import { exportAst } from '../shared/export'; ··· 157 157 const $ref = pathToJsonPointer(fromRef(state.path)); 158 158 const ast = irSchemaToAst({ plugin, schema, state }); 159 159 const baseName = refToName($ref); 160 - const symbol = plugin.registerSymbol({ 161 - meta: { 162 - category: 'schema', 163 - path: fromRef(state.path), 164 - resource: 'definition', 165 - resourceId: $ref, 166 - tags: fromRef(state.tags), 167 - tool: 'valibot', 160 + const symbol = plugin.symbol( 161 + applyNaming(baseName, plugin.config.definitions), 162 + { 163 + meta: { 164 + category: 'schema', 165 + path: fromRef(state.path), 166 + resource: 'definition', 167 + resourceId: $ref, 168 + tags: fromRef(state.tags), 169 + tool: 'valibot', 170 + }, 168 171 }, 169 - name: buildName({ 170 - config: plugin.config.definitions, 171 - name: baseName, 172 - }), 173 - }); 172 + ); 174 173 exportAst({ 175 174 ast, 176 175 plugin, ··· 181 180 }; 182 181 183 182 export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { 184 - plugin.registerSymbol({ 183 + plugin.symbol('v', { 185 184 external: 'valibot', 186 185 importKind: 'namespace', 187 186 meta: { 188 187 category: 'external', 189 188 resource: 'valibot.v', 190 189 }, 191 - name: 'v', 192 190 }); 193 191 194 192 plugin.forEach(
+27 -30
packages/openapi-ts/src/plugins/zod/mini/plugin.ts
··· 3 3 4 4 import { deduplicateSchema } from '~/ir/schema'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { SchemaWithType } from '~/plugins'; 8 7 import { maybeBigInt } from '~/plugins/shared/utils/coerce'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 import { pathToJsonPointer, refToName } from '~/utils/ref'; 11 11 12 12 import { identifiers } from '../constants'; ··· 176 176 const $ref = pathToJsonPointer(fromRef(state.path)); 177 177 const ast = irSchemaToAst({ plugin, schema, state }); 178 178 const baseName = refToName($ref); 179 - const symbol = plugin.registerSymbol({ 180 - meta: { 181 - category: 'schema', 182 - path: fromRef(state.path), 183 - resource: 'definition', 184 - resourceId: $ref, 185 - tags: fromRef(state.tags), 186 - tool: 'zod', 179 + const symbol = plugin.symbol( 180 + applyNaming(baseName, plugin.config.definitions), 181 + { 182 + meta: { 183 + category: 'schema', 184 + path: fromRef(state.path), 185 + resource: 'definition', 186 + resourceId: $ref, 187 + tags: fromRef(state.tags), 188 + tool: 'zod', 189 + }, 187 190 }, 188 - name: buildName({ 189 - config: plugin.config.definitions, 190 - name: baseName, 191 - }), 192 - }); 191 + ); 193 192 const typeInferSymbol = plugin.config.definitions.types.infer.enabled 194 - ? plugin.registerSymbol({ 195 - meta: { 196 - category: 'type', 197 - path: fromRef(state.path), 198 - resource: 'definition', 199 - resourceId: $ref, 200 - tags: fromRef(state.tags), 201 - tool: 'zod', 202 - variant: 'infer', 193 + ? plugin.symbol( 194 + applyNaming(baseName, plugin.config.definitions.types.infer), 195 + { 196 + meta: { 197 + category: 'type', 198 + path: fromRef(state.path), 199 + resource: 'definition', 200 + resourceId: $ref, 201 + tags: fromRef(state.tags), 202 + tool: 'zod', 203 + variant: 'infer', 204 + }, 203 205 }, 204 - name: buildName({ 205 - config: plugin.config.definitions.types.infer, 206 - name: baseName, 207 - }), 208 - }) 206 + ) 209 207 : undefined; 210 208 exportAst({ 211 209 ast, ··· 217 215 }; 218 216 219 217 export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { 220 - plugin.registerSymbol({ 218 + plugin.symbol('z', { 221 219 external: getZodModule({ plugin }), 222 220 importKind: 'namespace', 223 221 meta: { 224 222 category: 'external', 225 223 resource: 'zod.z', 226 224 }, 227 - name: 'z', 228 225 }); 229 226 230 227 plugin.forEach(
+5 -17
packages/openapi-ts/src/plugins/zod/shared/operation.ts
··· 2 2 3 3 import { operationResponsesMap } from '~/ir/operation'; 4 4 import type { IR } from '~/ir/types'; 5 - import { buildName } from '~/openApi/shared/utils/name'; 5 + import { applyNaming } from '~/utils/naming'; 6 6 7 7 import { exportAst } from './export'; 8 8 import type { Ast, IrSchemaToAstOptions } from './types'; ··· 119 119 120 120 const ast = getAst(schemaData, fromRef(state.path)); 121 121 const symbol = plugin.symbol( 122 - buildName({ 123 - config: plugin.config.requests, 124 - name: operation.id, 125 - }), 122 + applyNaming(operation.id, plugin.config.requests), 126 123 { 127 124 meta: { 128 125 category: 'schema', ··· 137 134 ); 138 135 const typeInferSymbol = plugin.config.requests.types.infer.enabled 139 136 ? plugin.symbol( 140 - buildName({ 141 - config: plugin.config.requests.types.infer, 142 - name: operation.id, 143 - }), 137 + applyNaming(operation.id, plugin.config.requests.types.infer), 144 138 { 145 139 meta: { 146 140 category: 'type', ··· 172 166 const path = [...fromRef(state.path), 'responses']; 173 167 const ast = getAst(response, path); 174 168 const symbol = plugin.symbol( 175 - buildName({ 176 - config: plugin.config.responses, 177 - name: operation.id, 178 - }), 169 + applyNaming(operation.id, plugin.config.responses), 179 170 { 180 171 meta: { 181 172 category: 'schema', ··· 190 181 ); 191 182 const typeInferSymbol = plugin.config.responses.types.infer.enabled 192 183 ? plugin.symbol( 193 - buildName({ 194 - config: plugin.config.responses.types.infer, 195 - name: operation.id, 196 - }), 184 + applyNaming(operation.id, plugin.config.responses.types.infer), 197 185 { 198 186 meta: { 199 187 category: 'type',
+28 -30
packages/openapi-ts/src/plugins/zod/shared/webhook.ts
··· 1 1 import { fromRef } from '@hey-api/codegen-core'; 2 2 3 3 import type { IR } from '~/ir/types'; 4 - import { buildName } from '~/openApi/shared/utils/name'; 4 + import { applyNaming } from '~/utils/naming'; 5 5 6 6 import { exportAst } from './export'; 7 7 import type { Ast, IrSchemaToAstOptions } from './types'; ··· 117 117 schemaData.required = [...requiredProperties]; 118 118 119 119 const ast = getAst(schemaData, fromRef(state.path)); 120 - const symbol = plugin.registerSymbol({ 121 - meta: { 122 - category: 'schema', 123 - path: fromRef(state.path), 124 - resource: 'webhook', 125 - resourceId: operation.id, 126 - role: 'data', 127 - tags: fromRef(state.tags), 128 - tool: 'zod', 120 + const symbol = plugin.symbol( 121 + applyNaming(operation.id, plugin.config.webhooks), 122 + { 123 + meta: { 124 + category: 'schema', 125 + path: fromRef(state.path), 126 + resource: 'webhook', 127 + resourceId: operation.id, 128 + role: 'data', 129 + tags: fromRef(state.tags), 130 + tool: 'zod', 131 + }, 129 132 }, 130 - name: buildName({ 131 - config: plugin.config.webhooks, 132 - name: operation.id, 133 - }), 134 - }); 133 + ); 135 134 const typeInferSymbol = plugin.config.webhooks.types.infer.enabled 136 - ? plugin.registerSymbol({ 137 - meta: { 138 - category: 'type', 139 - path: fromRef(state.path), 140 - resource: 'webhook', 141 - resourceId: operation.id, 142 - role: 'data', 143 - tags: fromRef(state.tags), 144 - tool: 'zod', 145 - variant: 'infer', 135 + ? plugin.symbol( 136 + applyNaming(operation.id, plugin.config.webhooks.types.infer), 137 + { 138 + meta: { 139 + category: 'type', 140 + path: fromRef(state.path), 141 + resource: 'webhook', 142 + resourceId: operation.id, 143 + role: 'data', 144 + tags: fromRef(state.tags), 145 + tool: 'zod', 146 + variant: 'infer', 147 + }, 146 148 }, 147 - name: buildName({ 148 - config: plugin.config.webhooks.types.infer, 149 - name: operation.id, 150 - }), 151 - }) 149 + ) 152 150 : undefined; 153 151 exportAst({ 154 152 ast,
+46 -46
packages/openapi-ts/src/plugins/zod/types.d.ts
··· 9 9 } from '~/plugins/shared/utils/coerce'; 10 10 import type { GetIntegerLimit } from '~/plugins/shared/utils/formats'; 11 11 import type { $, DollarTsDsl, TsDsl } from '~/ts-dsl'; 12 - import type { StringCase, StringName } from '~/types/case'; 13 12 import type { MaybeArray } from '~/types/utils'; 13 + import type { Casing, NameTransformer } from '~/utils/naming'; 14 14 15 15 import type { IApi } from './api'; 16 16 import type { Chain } from './shared/chain'; ··· 24 24 * 25 25 * @default 'camelCase' 26 26 */ 27 - case?: StringCase; 27 + case?: Casing; 28 28 /** 29 29 * Add comments from input to the generated Zod schemas? 30 30 * ··· 83 83 */ 84 84 definitions?: 85 85 | boolean 86 - | StringName 86 + | NameTransformer 87 87 | { 88 88 /** 89 89 * The casing convention to use for generated names. 90 90 * 91 91 * @default 'camelCase' 92 92 */ 93 - case?: StringCase; 93 + case?: Casing; 94 94 /** 95 95 * Whether to generate Zod schemas for reusable definitions. 96 96 * ··· 103 103 * 104 104 * @default 'z{{name}}' 105 105 */ 106 - name?: StringName; 106 + name?: NameTransformer; 107 107 /** 108 108 * Configuration for TypeScript type generation from Zod schemas. 109 109 * ··· 122 122 */ 123 123 infer?: 124 124 | boolean 125 - | StringName 125 + | NameTransformer 126 126 | { 127 127 /** 128 128 * The casing convention to use for generated type names. 129 129 * 130 130 * @default 'PascalCase' 131 131 */ 132 - case?: StringCase; 132 + case?: Casing; 133 133 /** 134 134 * Whether to generate TypeScript types from Zod schemas. 135 135 * ··· 142 142 * 143 143 * @default '{{name}}ZodType' 144 144 */ 145 - name?: StringName; 145 + name?: NameTransformer; 146 146 }; 147 147 }; 148 148 }; ··· 176 176 */ 177 177 requests?: 178 178 | boolean 179 - | StringName 179 + | NameTransformer 180 180 | { 181 181 /** 182 182 * The casing convention to use for generated names. 183 183 * 184 184 * @default 'camelCase' 185 185 */ 186 - case?: StringCase; 186 + case?: Casing; 187 187 /** 188 188 * Whether to generate Zod schemas for request definitions. 189 189 * ··· 196 196 * 197 197 * @default 'z{{name}}Data' 198 198 */ 199 - name?: StringName; 199 + name?: NameTransformer; 200 200 /** 201 201 * Configuration for TypeScript type generation from Zod schemas. 202 202 * ··· 215 215 */ 216 216 infer?: 217 217 | boolean 218 - | StringName 218 + | NameTransformer 219 219 | { 220 220 /** 221 221 * The casing convention to use for generated type names. 222 222 * 223 223 * @default 'PascalCase' 224 224 */ 225 - case?: StringCase; 225 + case?: Casing; 226 226 /** 227 227 * Whether to generate TypeScript types from Zod schemas. 228 228 * ··· 235 235 * 236 236 * @default '{{name}}DataZodType' 237 237 */ 238 - name?: StringName; 238 + name?: NameTransformer; 239 239 }; 240 240 }; 241 241 }; ··· 254 254 */ 255 255 responses?: 256 256 | boolean 257 - | StringName 257 + | NameTransformer 258 258 | { 259 259 /** 260 260 * The casing convention to use for generated names. 261 261 * 262 262 * @default 'camelCase' 263 263 */ 264 - case?: StringCase; 264 + case?: Casing; 265 265 /** 266 266 * Whether to generate Zod schemas for response definitions. 267 267 * ··· 274 274 * 275 275 * @default 'z{{name}}Response' 276 276 */ 277 - name?: StringName; 277 + name?: NameTransformer; 278 278 /** 279 279 * Configuration for TypeScript type generation from Zod schemas. 280 280 * ··· 293 293 */ 294 294 infer?: 295 295 | boolean 296 - | StringName 296 + | NameTransformer 297 297 | { 298 298 /** 299 299 * The casing convention to use for generated type names. 300 300 * 301 301 * @default 'PascalCase' 302 302 */ 303 - case?: StringCase; 303 + case?: Casing; 304 304 /** 305 305 * Whether to generate TypeScript types from Zod schemas. 306 306 * ··· 313 313 * 314 314 * @default '{{name}}ResponseZodType' 315 315 */ 316 - name?: StringName; 316 + name?: NameTransformer; 317 317 }; 318 318 }; 319 319 }; ··· 335 335 */ 336 336 infer?: 337 337 | boolean 338 - | StringName 338 + | NameTransformer 339 339 | { 340 340 /** 341 341 * The casing convention to use for generated type names. 342 342 * 343 343 * @default 'PascalCase' 344 344 */ 345 - case?: StringCase; 345 + case?: Casing; 346 346 /** 347 347 * Whether to generate TypeScript types from Zod schemas. 348 348 * ··· 365 365 */ 366 366 webhooks?: 367 367 | boolean 368 - | StringName 368 + | NameTransformer 369 369 | { 370 370 /** 371 371 * The casing convention to use for generated names. 372 372 * 373 373 * @default 'camelCase' 374 374 */ 375 - case?: StringCase; 375 + case?: Casing; 376 376 /** 377 377 * Whether to generate Zod schemas for webhook definitions. 378 378 * ··· 385 385 * 386 386 * @default 'z{{name}}WebhookRequest' 387 387 */ 388 - name?: StringName; 388 + name?: NameTransformer; 389 389 /** 390 390 * Configuration for TypeScript type generation from Zod schemas. 391 391 * ··· 404 404 */ 405 405 infer?: 406 406 | boolean 407 - | StringName 407 + | NameTransformer 408 408 | { 409 409 /** 410 410 * The casing convention to use for generated type names. 411 411 * 412 412 * @default 'PascalCase' 413 413 */ 414 - case?: StringCase; 414 + case?: Casing; 415 415 /** 416 416 * Whether to generate TypeScript types from Zod schemas. 417 417 * ··· 424 424 * 425 425 * @default '{{name}}WebhookRequestZodType' 426 426 */ 427 - name?: StringName; 427 + name?: NameTransformer; 428 428 }; 429 429 }; 430 430 }; ··· 438 438 * 439 439 * @default 'camelCase' 440 440 */ 441 - case: StringCase; 441 + case: Casing; 442 442 /** 443 443 * Add comments from input to the generated Zod schemas? 444 444 * ··· 494 494 * 495 495 * @default 'camelCase' 496 496 */ 497 - case: StringCase; 497 + case: Casing; 498 498 /** 499 499 * Whether to generate Zod schemas for reusable definitions. 500 500 * ··· 507 507 * 508 508 * @default 'z{{name}}' 509 509 */ 510 - name: StringName; 510 + name: NameTransformer; 511 511 /** 512 512 * Configuration for TypeScript type generation from Zod schemas. 513 513 * ··· 523 523 * 524 524 * @default 'PascalCase' 525 525 */ 526 - case: StringCase; 526 + case: Casing; 527 527 /** 528 528 * Whether to generate TypeScript types from Zod schemas. 529 529 * ··· 536 536 * 537 537 * @default '{{name}}ZodType' 538 538 */ 539 - name: StringName; 539 + name: NameTransformer; 540 540 }; 541 541 }; 542 542 }; ··· 567 567 * 568 568 * @default 'camelCase' 569 569 */ 570 - case: StringCase; 570 + case: Casing; 571 571 /** 572 572 * Whether to generate Zod schemas for request definitions. 573 573 * ··· 580 580 * 581 581 * @default 'z{{name}}Data' 582 582 */ 583 - name: StringName; 583 + name: NameTransformer; 584 584 /** 585 585 * Configuration for TypeScript type generation from Zod schemas. 586 586 * ··· 596 596 * 597 597 * @default 'PascalCase' 598 598 */ 599 - case: StringCase; 599 + case: Casing; 600 600 /** 601 601 * Whether to generate TypeScript types from Zod schemas. 602 602 * ··· 609 609 * 610 610 * @default '{{name}}DataZodType' 611 611 */ 612 - name: StringName; 612 + name: NameTransformer; 613 613 }; 614 614 }; 615 615 }; ··· 625 625 * 626 626 * @default 'camelCase' 627 627 */ 628 - case: StringCase; 628 + case: Casing; 629 629 /** 630 630 * Whether to generate Zod schemas for response definitions. 631 631 * ··· 638 638 * 639 639 * @default 'z{{name}}Response' 640 640 */ 641 - name: StringName; 641 + name: NameTransformer; 642 642 /** 643 643 * Configuration for TypeScript type generation from Zod schemas. 644 644 * ··· 654 654 * 655 655 * @default 'PascalCase' 656 656 */ 657 - case: StringCase; 657 + case: Casing; 658 658 /** 659 659 * Whether to generate TypeScript types from Zod schemas. 660 660 * ··· 667 667 * 668 668 * @default '{{name}}ResponseZodType' 669 669 */ 670 - name: StringName; 670 + name: NameTransformer; 671 671 }; 672 672 }; 673 673 }; ··· 686 686 * 687 687 * @default 'PascalCase' 688 688 */ 689 - case: StringCase; 689 + case: Casing; 690 690 /** 691 691 * Whether to generate TypeScript types from Zod schemas. 692 692 * ··· 706 706 * 707 707 * @default 'camelCase' 708 708 */ 709 - case: StringCase; 709 + case: Casing; 710 710 /** 711 711 * Whether to generate Zod schemas for webhook definitions. 712 712 * ··· 719 719 * 720 720 * @default 'z{{name}}WebhookRequest' 721 721 */ 722 - name: StringName; 722 + name: NameTransformer; 723 723 /** 724 724 * Configuration for TypeScript type generation from Zod schemas. 725 725 * ··· 735 735 * 736 736 * @default 'PascalCase' 737 737 */ 738 - case: StringCase; 738 + case: Casing; 739 739 /** 740 740 * Whether to generate TypeScript types from Zod schemas. 741 741 * ··· 748 748 * 749 749 * @default '{{name}}WebhookRequestZodType' 750 750 */ 751 - name: StringName; 751 + name: NameTransformer; 752 752 }; 753 753 }; 754 754 };
+27 -30
packages/openapi-ts/src/plugins/zod/v3/plugin.ts
··· 3 3 4 4 import { deduplicateSchema } from '~/ir/schema'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { SchemaWithType } from '~/plugins'; 8 7 import { maybeBigInt } from '~/plugins/shared/utils/coerce'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 import { pathToJsonPointer, refToName } from '~/utils/ref'; 11 11 12 12 import { identifiers } from '../constants'; ··· 170 170 const $ref = pathToJsonPointer(fromRef(state.path)); 171 171 const ast = irSchemaToAst({ plugin, schema, state }); 172 172 const baseName = refToName($ref); 173 - const symbol = plugin.registerSymbol({ 174 - meta: { 175 - category: 'schema', 176 - path: fromRef(state.path), 177 - resource: 'definition', 178 - resourceId: $ref, 179 - tags: fromRef(state.tags), 180 - tool: 'zod', 173 + const symbol = plugin.symbol( 174 + applyNaming(baseName, plugin.config.definitions), 175 + { 176 + meta: { 177 + category: 'schema', 178 + path: fromRef(state.path), 179 + resource: 'definition', 180 + resourceId: $ref, 181 + tags: fromRef(state.tags), 182 + tool: 'zod', 183 + }, 181 184 }, 182 - name: buildName({ 183 - config: plugin.config.definitions, 184 - name: baseName, 185 - }), 186 - }); 185 + ); 187 186 const typeInferSymbol = plugin.config.definitions.types.infer.enabled 188 - ? plugin.registerSymbol({ 189 - meta: { 190 - category: 'type', 191 - path: fromRef(state.path), 192 - resource: 'definition', 193 - resourceId: $ref, 194 - tags: fromRef(state.tags), 195 - tool: 'zod', 196 - variant: 'infer', 187 + ? plugin.symbol( 188 + applyNaming(baseName, plugin.config.definitions.types.infer), 189 + { 190 + meta: { 191 + category: 'type', 192 + path: fromRef(state.path), 193 + resource: 'definition', 194 + resourceId: $ref, 195 + tags: fromRef(state.tags), 196 + tool: 'zod', 197 + variant: 'infer', 198 + }, 197 199 }, 198 - name: buildName({ 199 - config: plugin.config.definitions.types.infer, 200 - name: baseName, 201 - }), 202 - }) 200 + ) 203 201 : undefined; 204 202 exportAst({ 205 203 ast, ··· 211 209 }; 212 210 213 211 export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { 214 - plugin.registerSymbol({ 212 + plugin.symbol('z', { 215 213 external: getZodModule({ plugin }), 216 214 meta: { 217 215 category: 'external', 218 216 resource: 'zod.z', 219 217 }, 220 - name: 'z', 221 218 }); 222 219 223 220 plugin.forEach(
+27 -30
packages/openapi-ts/src/plugins/zod/v4/plugin.ts
··· 3 3 4 4 import { deduplicateSchema } from '~/ir/schema'; 5 5 import type { IR } from '~/ir/types'; 6 - import { buildName } from '~/openApi/shared/utils/name'; 7 6 import type { SchemaWithType } from '~/plugins'; 8 7 import { maybeBigInt } from '~/plugins/shared/utils/coerce'; 9 8 import { $ } from '~/ts-dsl'; 9 + import { applyNaming } from '~/utils/naming'; 10 10 import { pathToJsonPointer, refToName } from '~/utils/ref'; 11 11 12 12 import { identifiers } from '../constants'; ··· 178 178 const $ref = pathToJsonPointer(fromRef(state.path)); 179 179 const ast = irSchemaToAst({ plugin, schema, state }); 180 180 const baseName = refToName($ref); 181 - const symbol = plugin.registerSymbol({ 182 - meta: { 183 - category: 'schema', 184 - path: fromRef(state.path), 185 - resource: 'definition', 186 - resourceId: $ref, 187 - tags: fromRef(state.tags), 188 - tool: 'zod', 181 + const symbol = plugin.symbol( 182 + applyNaming(baseName, plugin.config.definitions), 183 + { 184 + meta: { 185 + category: 'schema', 186 + path: fromRef(state.path), 187 + resource: 'definition', 188 + resourceId: $ref, 189 + tags: fromRef(state.tags), 190 + tool: 'zod', 191 + }, 189 192 }, 190 - name: buildName({ 191 - config: plugin.config.definitions, 192 - name: baseName, 193 - }), 194 - }); 193 + ); 195 194 const typeInferSymbol = plugin.config.definitions.types.infer.enabled 196 - ? plugin.registerSymbol({ 197 - meta: { 198 - category: 'type', 199 - path: fromRef(state.path), 200 - resource: 'definition', 201 - resourceId: $ref, 202 - tags: fromRef(state.tags), 203 - tool: 'zod', 204 - variant: 'infer', 195 + ? plugin.symbol( 196 + applyNaming(baseName, plugin.config.definitions.types.infer), 197 + { 198 + meta: { 199 + category: 'type', 200 + path: fromRef(state.path), 201 + resource: 'definition', 202 + resourceId: $ref, 203 + tags: fromRef(state.tags), 204 + tool: 'zod', 205 + variant: 'infer', 206 + }, 205 207 }, 206 - name: buildName({ 207 - config: plugin.config.definitions.types.infer, 208 - name: baseName, 209 - }), 210 - }) 208 + ) 211 209 : undefined; 212 210 exportAst({ 213 211 ast, ··· 219 217 }; 220 218 221 219 export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { 222 - plugin.registerSymbol({ 220 + plugin.symbol('z', { 223 221 external: getZodModule({ plugin }), 224 222 meta: { 225 223 category: 'external', 226 224 resource: 'zod.z', 227 225 }, 228 - name: 'z', 229 226 }); 230 227 231 228 plugin.forEach(
+80 -18
packages/openapi-ts/src/ts-dsl/base.ts
··· 1 1 // TODO: symbol should be protected, but needs to be public to satisfy types 2 2 import type { 3 3 AnalysisContext, 4 - AstContext, 5 4 File, 6 5 FromRef, 7 6 Language, 8 7 Node, 8 + NodeName, 9 + NodeNameSanitizer, 10 + NodeRelationship, 11 + NodeScope, 12 + Ref, 9 13 Symbol, 10 14 } from '@hey-api/codegen-core'; 11 15 import { ··· 14 18 isRef, 15 19 isSymbol, 16 20 nodeBrand, 21 + ref, 17 22 } from '@hey-api/codegen-core'; 18 23 import ts from 'typescript'; 19 24 25 + import type { AccessOptions } from './utils/context'; 26 + 27 + /** 28 + * Accepts a value or a readonly array of values of type T. 29 + */ 20 30 export type MaybeArray<T> = T | ReadonlyArray<T>; 21 31 32 + /** 33 + * Accepts a value or a function returning a value. 34 + */ 35 + export type MaybeFunc<T extends (...args: Array<any>) => any> = 36 + | T 37 + | ReturnType<T>; 38 + 22 39 export abstract class TsDsl<T extends ts.Node = ts.Node> implements Node<T> { 23 40 // eslint-disable-next-line @typescript-eslint/no-unused-vars 24 41 analyze(_: AnalysisContext): void {} 42 + clone(): this { 43 + const cloned = Object.create(Object.getPrototypeOf(this)); 44 + Object.assign(cloned, this); 45 + return cloned; 46 + } 25 47 exported?: boolean; 26 48 file?: File; 49 + get name(): Node['name'] { 50 + return { 51 + ...this._name, 52 + set: (value) => { 53 + this._name = ref(value); 54 + if (isSymbol(value)) { 55 + value.setNode(this); 56 + } 57 + }, 58 + toString: () => (this._name ? this.$name(this._name) : ''), 59 + } as Node['name']; 60 + } 61 + readonly nameSanitizer?: NodeNameSanitizer; 27 62 language: Language = 'typescript'; 28 63 parent?: Node; 29 - root?: Node; 64 + root: boolean = false; 65 + scope?: NodeScope = 'value'; 66 + structuralChildren?: Map<TsDsl, NodeRelationship>; 67 + structuralParents?: Map<TsDsl, NodeRelationship>; 30 68 symbol?: Symbol; 31 - // eslint-disable-next-line @typescript-eslint/no-unused-vars 32 - toAst(_: AstContext): T { 69 + toAst(): T { 33 70 return undefined as unknown as T; 34 71 } 35 72 readonly '~brand' = nodeBrand; 36 73 74 + /** Access patterns for this node. */ 75 + toAccessNode?( 76 + node: this, 77 + options: AccessOptions, 78 + ctx: { 79 + /** The full chain. */ 80 + chain: ReadonlyArray<TsDsl>; 81 + /** Position in the chain (0 = root). */ 82 + index: number; 83 + /** Is this the leaf node? */ 84 + isLeaf: boolean; 85 + /** Is this the root node? */ 86 + isRoot: boolean; 87 + /** Total length of the chain. */ 88 + length: number; 89 + }, 90 + ): TsDsl | undefined; 37 91 /** Branding property to identify the DSL class at runtime. */ 38 - abstract readonly '~dsl': string; 92 + abstract readonly '~dsl': string & {}; 39 93 40 94 /** Conditionally applies a callback to this builder. */ 41 95 $if<T extends TsDsl, V, R extends TsDsl = T>( ··· 120 174 ) as T extends string ? ts.Identifier : T; 121 175 } 122 176 123 - protected $node<I>(ctx: AstContext, value: I): NodeOfMaybe<I> { 177 + protected $name(name: Ref<NodeName>): string { 178 + const value = fromRef(name); 179 + if (isSymbol(value)) { 180 + try { 181 + return value.finalName; 182 + } catch { 183 + return value.name; 184 + } 185 + } 186 + return String(value); 187 + } 188 + 189 + protected $node<I>(value: I): NodeOfMaybe<I> { 124 190 if (value === undefined) { 125 191 return undefined as NodeOfMaybe<I>; 126 192 } ··· 135 201 if (value instanceof Array) { 136 202 return value.map((item) => { 137 203 if (isRef(item)) item = fromRef(item); 138 - return this.unwrap(item, ctx); 204 + return this.unwrap(item); 139 205 }) as NodeOfMaybe<I>; 140 206 } 141 - return this.unwrap(value as any, ctx) as NodeOfMaybe<I>; 207 + return this.unwrap(value as any) as NodeOfMaybe<I>; 142 208 } 143 209 144 210 protected $type<I>( 145 - ctx: AstContext, 146 211 value: I, 147 212 args?: ReadonlyArray<ts.TypeNode>, 148 213 ): TypeOfMaybe<I> { ··· 172 237 ) as TypeOfMaybe<I>; 173 238 } 174 239 if (value instanceof Array) { 175 - return value.map((item) => this.$type(ctx, item, args)) as TypeOfMaybe<I>; 240 + return value.map((item) => this.$type(item, args)) as TypeOfMaybe<I>; 176 241 } 177 - return this.unwrap(value as any, ctx) as TypeOfMaybe<I>; 242 + return this.unwrap(value as any) as TypeOfMaybe<I>; 178 243 } 244 + 245 + private _name?: Ref<NodeName>; 179 246 180 247 /** Unwraps nested nodes into raw TypeScript AST. */ 181 - private unwrap<I>( 182 - value: I, 183 - ctx: AstContext, 184 - ): I extends TsDsl<infer N> ? N : I { 185 - return (isNode(value) ? value.toAst(ctx) : value) as I extends TsDsl< 186 - infer N 187 - > 248 + private unwrap<I>(value: I): I extends TsDsl<infer N> ? N : I { 249 + return (isNode(value) ? value.toAst() : value) as I extends TsDsl<infer N> 188 250 ? N 189 251 : I; 190 252 }
+25 -29
packages/openapi-ts/src/ts-dsl/decl/class.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { isSymbol, ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 15 10 import { AbstractMixin, DefaultMixin, ExportMixin } from '../mixins/modifiers'; 16 11 import { TypeParamsMixin } from '../mixins/type-params'; 17 12 import { safeRuntimeName } from '../utils/name'; 18 - import type { FieldName } from './field'; 19 13 import { FieldTsDsl } from './field'; 20 14 import { InitTsDsl } from './init'; 21 15 import { MethodTsDsl } from './method'; 22 16 23 - type Base = Symbol | string; 24 - type Name = Symbol | string; 25 17 type Body = Array<MaybeTsDsl<ts.ClassElement | ts.Node>>; 26 18 27 19 const Mixed = AbstractMixin( ··· 34 26 35 27 export class ClassTsDsl extends Mixed { 36 28 readonly '~dsl' = 'ClassTsDsl'; 29 + override readonly nameSanitizer = safeRuntimeName; 37 30 38 - protected baseClass?: Ref<Base>; 31 + protected baseClass?: Ref<NodeName>; 39 32 protected body: Body = []; 40 - protected name: Ref<Name>; 41 33 42 - constructor(name: Name) { 34 + constructor(name: NodeName) { 43 35 super(); 44 - this.name = ref(name); 36 + this.name.set(name); 45 37 if (isSymbol(name)) { 46 38 name.setKind('class'); 47 - name.setNameSanitizer(safeRuntimeName); 48 - name.setNode(this); 49 39 } 50 40 } 51 41 ··· 63 53 } 64 54 } 65 55 56 + /** Returns true if the class has any members. */ 57 + get hasBody(): boolean { 58 + return this.body.length > 0; 59 + } 60 + 66 61 /** Adds one or more class members (fields, methods, etc.). */ 67 62 do(...items: Body): this { 68 63 this.body.push(...items); ··· 70 65 } 71 66 72 67 /** Records a base class to extend from. */ 73 - extends(base?: Base): this { 68 + extends(base?: NodeName): this { 74 69 this.baseClass = base ? ref(base) : undefined; 75 70 return this; 76 71 } 77 72 78 73 /** Adds a class field. */ 79 - field(name: FieldName, fn?: (f: FieldTsDsl) => void): this { 74 + field(name: NodeName, fn?: (f: FieldTsDsl) => void): this { 80 75 const f = new FieldTsDsl(name, fn); 81 76 this.body.push(f); 82 77 return this; 83 78 } 84 79 85 80 /** Adds a class constructor. */ 86 - init(fn?: (i: InitTsDsl) => void): this { 87 - const i = new InitTsDsl(fn); 81 + init(fn?: InitTsDsl | ((i: InitTsDsl) => void)): this { 82 + const i = 83 + typeof fn === 'function' ? new InitTsDsl(fn) : fn || new InitTsDsl(); 88 84 this.body.push(i); 89 85 return this; 90 86 } 91 87 92 88 /** Adds a class method. */ 93 - method(name: string, fn?: (m: MethodTsDsl) => void): this { 89 + method(name: NodeName, fn?: (m: MethodTsDsl) => void): this { 94 90 const m = new MethodTsDsl(name, fn); 95 91 this.body.push(m); 96 92 return this; ··· 102 98 return this; 103 99 } 104 100 105 - override toAst(ctx: AstContext) { 106 - const body = this.$node(ctx, this.body) as ReadonlyArray<ts.ClassElement>; 101 + override toAst() { 102 + const body = this.$node(this.body) as ReadonlyArray<ts.ClassElement>; 107 103 const node = ts.factory.createClassDeclaration( 108 - [...this.$decorators(ctx), ...this.modifiers], 109 - this.$node(ctx, this.name) as ts.Identifier, 110 - this.$generics(ctx), 111 - this._heritage(ctx), 104 + [...this.$decorators(), ...this.modifiers], 105 + this.$node(this.name) as ts.Identifier, 106 + this.$generics(), 107 + this._heritage(), 112 108 body, 113 109 ); 114 - return this.$docs(ctx, node); 110 + return this.$docs(node); 115 111 } 116 112 117 113 /** Builds heritage clauses (extends). */ 118 - private _heritage(ctx: AstContext): ReadonlyArray<ts.HeritageClause> { 119 - const node = this.$node(ctx, this.baseClass); 114 + private _heritage(): ReadonlyArray<ts.HeritageClause> { 115 + const node = this.$node(this.baseClass); 120 116 if (!node) return []; 121 117 return [ 122 118 ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
+7 -19
packages/openapi-ts/src/ts-dsl/decl/decorator.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { isSymbol, ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 8 2 import ts from 'typescript'; 9 3 10 4 import type { MaybeTsDsl } from '../base'; 11 5 import { TsDsl } from '../base'; 12 6 import { ArgsMixin } from '../mixins/args'; 13 7 import { safeRuntimeName } from '../utils/name'; 14 - 15 - export type DecoratorName = Symbol | string | MaybeTsDsl<ts.Expression>; 16 8 17 9 const Mixed = ArgsMixin(TsDsl<ts.Decorator>); 18 10 19 11 export class DecoratorTsDsl extends Mixed { 20 12 readonly '~dsl' = 'DecoratorTsDsl'; 21 - 22 - protected name: Ref<DecoratorName>; 13 + override readonly nameSanitizer = safeRuntimeName; 23 14 24 15 constructor( 25 - name: DecoratorName, 16 + name: NodeName, 26 17 ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 27 18 ) { 28 19 super(); 29 - this.name = ref(name); 30 - if (isSymbol(name)) { 31 - name.setNameSanitizer(safeRuntimeName); 32 - } 20 + this.name.set(name); 33 21 this.args(...args); 34 22 } 35 23 ··· 38 26 ctx.analyze(this.name); 39 27 } 40 28 41 - override toAst(ctx: AstContext) { 42 - const target = this.$node(ctx, this.name); 43 - const args = this.$args(ctx); 29 + override toAst() { 30 + const target = this.$node(this.name); 31 + const args = this.$args(); 44 32 return ts.factory.createDecorator( 45 33 args.length 46 34 ? ts.factory.createCallExpression(target, undefined, args)
+10 -18
packages/openapi-ts/src/ts-dsl/decl/enum.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { isSymbol, ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; ··· 14 9 import { safeRuntimeName } from '../utils/name'; 15 10 import { EnumMemberTsDsl } from './member'; 16 11 17 - export type EnumName = Symbol | string; 18 12 type Value = string | number | MaybeTsDsl<ts.Expression>; 19 13 type ValueFn = Value | ((m: EnumMemberTsDsl) => void); 20 14 ··· 22 16 23 17 export class EnumTsDsl extends Mixed { 24 18 readonly '~dsl' = 'EnumTsDsl'; 19 + override readonly nameSanitizer = safeRuntimeName; 25 20 26 21 private _members: Array<EnumMemberTsDsl> = []; 27 - private _name: Ref<EnumName>; 28 22 29 - constructor(name: EnumName, fn?: (e: EnumTsDsl) => void) { 23 + constructor(name: NodeName, fn?: (e: EnumTsDsl) => void) { 30 24 super(); 31 - this._name = ref(name); 25 + this.name.set(name); 32 26 if (isSymbol(name)) { 33 27 name.setKind('enum'); 34 - name.setNameSanitizer(safeRuntimeName); 35 - name.setNode(this); 36 28 } 37 29 fn?.(this); 38 30 } 39 31 40 32 override analyze(ctx: AnalysisContext): void { 41 33 super.analyze(ctx); 42 - ctx.analyze(this._name); 34 + ctx.analyze(this.name); 43 35 ctx.pushScope(); 44 36 try { 45 37 for (const member of this._members) { ··· 63 55 return this; 64 56 } 65 57 66 - override toAst(ctx: AstContext) { 58 + override toAst() { 67 59 const node = ts.factory.createEnumDeclaration( 68 60 this.modifiers, 69 - this.$node(ctx, this._name) as ts.Identifier, 70 - this.$node(ctx, this._members) as ReadonlyArray<ts.EnumMember>, 61 + this.$node(this.name) as ts.Identifier, 62 + this.$node(this._members) as ReadonlyArray<ts.EnumMember>, 71 63 ); 72 - return this.$docs(ctx, node); 64 + return this.$docs(node); 73 65 } 74 66 }
+14 -18
packages/openapi-ts/src/ts-dsl/decl/field.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Symbol, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl, TypeTsDsl } from '../base'; ··· 18 14 import { OptionalMixin } from '../mixins/optional'; 19 15 import { ValueMixin } from '../mixins/value'; 20 16 import { TokenTsDsl } from '../token'; 21 - import type { TypeExprName } from '../type/expr'; 22 17 import { TypeExprTsDsl } from '../type/expr'; 18 + import { safeAccessorName } from '../utils/name'; 23 19 24 - export type FieldName = Symbol | string; 25 - export type FieldType = TypeExprName | TypeTsDsl; 20 + export type FieldType = NodeName | TypeTsDsl; 26 21 27 22 const Mixed = DecoratorMixin( 28 23 DocMixin( ··· 42 37 43 38 export class FieldTsDsl extends Mixed { 44 39 readonly '~dsl' = 'FieldTsDsl'; 40 + override readonly nameSanitizer = safeAccessorName; 45 41 46 - protected name: FieldName; 47 42 protected _type?: TypeTsDsl; 48 43 49 - constructor(name: FieldName, fn?: (f: FieldTsDsl) => void) { 44 + constructor(name: NodeName, fn?: (f: FieldTsDsl) => void) { 50 45 super(); 51 - this.name = name; 46 + this.name.set(name); 52 47 fn?.(this); 53 48 } 54 49 55 50 override analyze(ctx: AnalysisContext): void { 56 51 super.analyze(ctx); 52 + ctx.analyze(this.name); 57 53 ctx.analyze(this._type); 58 54 } 59 55 ··· 63 59 return this; 64 60 } 65 61 66 - override toAst(ctx: AstContext) { 62 + override toAst() { 67 63 const node = ts.factory.createPropertyDeclaration( 68 - [...this.$decorators(ctx), ...this.modifiers], 69 - this.$node(ctx, this.name) as ts.PropertyName, 70 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 71 - this.$type(ctx, this._type), 72 - this.$value(ctx), 64 + [...this.$decorators(), ...this.modifiers], 65 + this.$node(this.name) as ts.PropertyName, 66 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 67 + this.$type(this._type), 68 + this.$value(), 73 69 ); 74 - return this.$docs(ctx, node); 70 + return this.$docs(node); 75 71 } 76 72 }
+28 -37
packages/openapi-ts/src/ts-dsl/decl/func.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { isSymbol, ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 10 5 import { TsDsl } from '../base'; ··· 27 22 import { safeRuntimeName } from '../utils/name'; 28 23 29 24 export type FuncMode = 'arrow' | 'decl' | 'expr'; 30 - export type FuncName = Symbol | string; 31 25 32 26 const Mixed = AbstractMixin( 33 27 AsMixin( ··· 57 51 58 52 class ImplFuncTsDsl<M extends FuncMode = 'arrow'> extends Mixed { 59 53 readonly '~dsl' = 'FuncTsDsl'; 54 + override readonly nameSanitizer = safeRuntimeName; 60 55 61 56 protected mode?: FuncMode; 62 - protected name?: Ref<FuncName>; 63 57 64 58 constructor(); 65 59 constructor(fn: (f: ImplFuncTsDsl<'arrow'>) => void); 66 - constructor(name: FuncName); 67 - constructor(name: FuncName, fn: (f: ImplFuncTsDsl<'decl'>) => void); 60 + constructor(name: NodeName); 61 + constructor(name: NodeName, fn: (f: ImplFuncTsDsl<'decl'>) => void); 68 62 constructor( 69 - name?: FuncName | ((f: ImplFuncTsDsl<'arrow'>) => void), 63 + name?: NodeName | ((f: ImplFuncTsDsl<'arrow'>) => void), 70 64 fn?: (f: ImplFuncTsDsl<'decl'>) => void, 71 65 ) { 72 66 super(); ··· 75 69 name(this as unknown as FuncTsDsl<'arrow'>); 76 70 } else if (name) { 77 71 this.mode = 'decl'; 78 - this.name = ref(name); 72 + this.name.set(name); 79 73 if (isSymbol(name)) { 80 74 name.setKind('function'); 81 - name.setNameSanitizer(safeRuntimeName); 82 - name.setNode(this); 83 75 } 84 76 fn?.(this as unknown as FuncTsDsl<'decl'>); 85 77 } ··· 114 106 } 115 107 116 108 // @ts-expect-error --- need to fix types --- 117 - override toAst( 118 - ctx: AstContext, 119 - ): M extends 'decl' 109 + override toAst(): M extends 'decl' 120 110 ? ts.FunctionDeclaration 121 111 : M extends 'expr' 122 112 ? ts.FunctionExpression 123 113 : ts.ArrowFunction { 124 - const body = this.$node(ctx, new BlockTsDsl(...this._do).pretty()); 114 + const body = this.$node(new BlockTsDsl(...this._do).pretty()); 125 115 126 116 if (this.mode === 'decl') { 127 - if (!this.name) throw new Error('Function declaration requires a name'); 117 + const name = this.name.toString(); 118 + if (!name) throw new Error('Function declaration requires a name'); 128 119 const node = ts.factory.createFunctionDeclaration( 129 - [...this.$decorators(ctx), ...this.modifiers], 120 + [...this.$decorators(), ...this.modifiers], 130 121 undefined, 131 - this.$node(ctx, this.name) as ts.Identifier, 132 - this.$generics(ctx), 133 - this.$params(ctx), 134 - this.$returns(ctx), 122 + this.$node(this.name) as ts.Identifier, 123 + this.$generics(), 124 + this.$params(), 125 + this.$returns(), 135 126 body, 136 127 ) as any; 137 - return this.$docs(ctx, node); 128 + return this.$docs(node); 138 129 } 139 130 140 131 if (this.mode === 'expr') { 141 132 const node = ts.factory.createFunctionExpression( 142 133 this.modifiers, 143 134 undefined, 144 - this.$node(ctx, this.name) as ts.Identifier, 145 - this.$generics(ctx), 146 - this.$params(ctx), 147 - this.$returns(ctx), 135 + this.$node(this.name) as ts.Identifier, 136 + this.$generics(), 137 + this.$params(), 138 + this.$returns(), 148 139 body, 149 140 ) as any; 150 - return this.$docs(ctx, node); 141 + return this.$docs(node); 151 142 } 152 143 153 144 const node = ts.factory.createArrowFunction( 154 145 this.modifiers, 155 - this.$generics(ctx), 156 - this.$params(ctx), 157 - this.$returns(ctx), 146 + this.$generics(), 147 + this.$params(), 148 + this.$returns(), 158 149 undefined, 159 150 body.statements.length === 1 && 160 151 ts.isReturnStatement(body.statements[0]!) && ··· 162 153 ? body.statements[0].expression 163 154 : body, 164 155 ) as any; 165 - return this.$docs(ctx, node); 156 + return this.$docs(node); 166 157 } 167 158 } 168 159 169 160 export const FuncTsDsl = ImplFuncTsDsl as { 170 161 new (): FuncTsDsl<'arrow'>; 171 162 new (fn: (f: FuncTsDsl<'arrow'>) => void): FuncTsDsl<'arrow'>; 172 - new (name: string): FuncTsDsl<'decl'>; 173 - new (name: string, fn: (f: FuncTsDsl<'decl'>) => void): FuncTsDsl<'decl'>; 163 + new (name: NodeName): FuncTsDsl<'decl'>; 164 + new (name: NodeName, fn: (f: FuncTsDsl<'decl'>) => void): FuncTsDsl<'decl'>; 174 165 } & typeof ImplFuncTsDsl; 175 166 export type FuncTsDsl<M extends FuncMode = 'arrow'> = ImplFuncTsDsl<M>;
+14 -14
packages/openapi-ts/src/ts-dsl/decl/getter.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 16 16 import { ParamMixin } from '../mixins/param'; 17 17 import { TypeReturnsMixin } from '../mixins/type-returns'; 18 18 import { BlockTsDsl } from '../stmt/block'; 19 - 20 - export type GetterName = string | ts.PropertyName; 19 + import { safeAccessorName } from '../utils/name'; 21 20 22 21 const Mixed = AbstractMixin( 23 22 AsyncMixin( ··· 43 42 44 43 export class GetterTsDsl extends Mixed { 45 44 readonly '~dsl' = 'GetterTsDsl'; 46 - 47 - protected name: GetterName; 45 + override readonly nameSanitizer = safeAccessorName; 48 46 49 - constructor(name: GetterName, fn?: (g: GetterTsDsl) => void) { 47 + constructor(name: NodeName, fn?: (g: GetterTsDsl) => void) { 50 48 super(); 51 - this.name = name; 49 + this.name.set(name); 52 50 fn?.(this); 53 51 } 54 52 55 53 override analyze(ctx: AnalysisContext): void { 54 + ctx.analyze(this.name); 55 + 56 56 ctx.pushScope(); 57 57 try { 58 58 super.analyze(ctx); ··· 61 61 } 62 62 } 63 63 64 - override toAst(ctx: AstContext) { 64 + override toAst() { 65 65 const node = ts.factory.createGetAccessorDeclaration( 66 - [...this.$decorators(ctx), ...this.modifiers], 67 - this.name, 68 - this.$params(ctx), 69 - this.$returns(ctx), 70 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 66 + [...this.$decorators(), ...this.modifiers], 67 + this.$node(this.name) as ts.PropertyName, 68 + this.$params(), 69 + this.$returns(), 70 + this.$node(new BlockTsDsl(...this._do).pretty()), 71 71 ); 72 - return this.$docs(ctx, node); 72 + return this.$docs(node); 73 73 } 74 74 }
+6 -6
packages/openapi-ts/src/ts-dsl/decl/init.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 38 38 } 39 39 } 40 40 41 - override toAst(ctx: AstContext) { 41 + override toAst() { 42 42 const node = ts.factory.createConstructorDeclaration( 43 - [...this.$decorators(ctx), ...this.modifiers], 44 - this.$params(ctx), 45 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 43 + [...this.$decorators(), ...this.modifiers], 44 + this.$params(), 45 + this.$node(new BlockTsDsl(...this._do).pretty()), 46 46 ); 47 - return this.$docs(ctx, node); 47 + return this.$docs(node); 48 48 } 49 49 }
+7 -8
packages/openapi-ts/src/ts-dsl/decl/member.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 14 14 export class EnumMemberTsDsl extends Mixed { 15 15 readonly '~dsl' = 'EnumMemberTsDsl'; 16 16 17 - private _name: string; 18 17 private _value?: Value; 19 18 20 - constructor(name: string, value?: ValueFn) { 19 + constructor(name: NodeName, value?: ValueFn) { 21 20 super(); 22 - this._name = name; 21 + this.name.set(name); 23 22 if (typeof value === 'function') { 24 23 value(this); 25 24 } else { ··· 38 37 return this; 39 38 } 40 39 41 - override toAst(ctx: AstContext) { 40 + override toAst() { 42 41 const node = ts.factory.createEnumMember( 43 - this.$node(ctx, safeMemberName(this._name)), 44 - this.$node(ctx, this._value), 42 + this.$node(safeMemberName(this.name.toString())), 43 + this.$node(this._value), 45 44 ); 46 - return this.$docs(ctx, node); 45 + return this.$docs(node); 47 46 } 48 47 }
+16 -14
packages/openapi-ts/src/ts-dsl/decl/method.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 19 19 import { TypeReturnsMixin } from '../mixins/type-returns'; 20 20 import { BlockTsDsl } from '../stmt/block'; 21 21 import { TokenTsDsl } from '../token'; 22 + import { safeAccessorName } from '../utils/name'; 22 23 23 24 const Mixed = AbstractMixin( 24 25 AsyncMixin( ··· 48 49 49 50 export class MethodTsDsl extends Mixed { 50 51 readonly '~dsl' = 'MethodTsDsl'; 52 + override readonly nameSanitizer = safeAccessorName; 51 53 52 - protected name: string; 53 - 54 - constructor(name: string, fn?: (m: MethodTsDsl) => void) { 54 + constructor(name: NodeName, fn?: (m: MethodTsDsl) => void) { 55 55 super(); 56 - this.name = name; 56 + this.name.set(name); 57 57 fn?.(this); 58 58 } 59 59 60 60 override analyze(ctx: AnalysisContext): void { 61 + ctx.analyze(this.name); 62 + 61 63 ctx.pushScope(); 62 64 try { 63 65 super.analyze(ctx); ··· 66 68 } 67 69 } 68 70 69 - override toAst(ctx: AstContext) { 71 + override toAst() { 70 72 const node = ts.factory.createMethodDeclaration( 71 - [...this.$decorators(ctx), ...this.modifiers], 73 + [...this.$decorators(), ...this.modifiers], 72 74 undefined, 73 - this.name, 74 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 75 - this.$generics(ctx), 76 - this.$params(ctx), 77 - this.$returns(ctx), 78 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 75 + this.$node(this.name) as ts.PropertyName, 76 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 77 + this.$generics(), 78 + this.$params(), 79 + this.$returns(), 80 + this.$node(new BlockTsDsl(...this._do).pretty()), 79 81 ); 80 - return this.$docs(ctx, node); 82 + return this.$docs(node); 81 83 } 82 84 }
+12 -24
packages/openapi-ts/src/ts-dsl/decl/param.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { fromRef, isSymbolRef, ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 8 2 import ts from 'typescript'; 9 3 10 4 import { TsDsl, TypeTsDsl } from '../base'; ··· 15 9 import { TokenTsDsl } from '../token'; 16 10 import { TypeExprTsDsl } from '../type/expr'; 17 11 18 - export type ParamName = Symbol | string; 19 12 export type ParamCtor = ( 20 - name: ParamName | ((p: ParamTsDsl) => void), 13 + name: NodeName | ((p: ParamTsDsl) => void), 21 14 fn?: (p: ParamTsDsl) => void, 22 15 ) => ParamTsDsl; 23 16 ··· 28 21 export class ParamTsDsl extends Mixed { 29 22 readonly '~dsl' = 'ParamTsDsl'; 30 23 31 - protected name?: Ref<ParamName>; 32 24 protected _type?: TypeTsDsl; 33 25 34 26 constructor( 35 - name: ParamName | ((p: ParamTsDsl) => void), 27 + name: NodeName | ((p: ParamTsDsl) => void), 36 28 fn?: (p: ParamTsDsl) => void, 37 29 ) { 38 30 super(); 39 31 if (typeof name === 'function') { 40 32 name(this); 41 33 } else { 42 - this.name = ref(name); 34 + this.name.set(name); 43 35 fn?.(this); 44 36 } 45 37 } ··· 56 48 return this; 57 49 } 58 50 59 - override toAst(ctx: AstContext) { 60 - let name: string | ReturnType<typeof this.$pattern> = this.$pattern(ctx); 61 - if (!name && this.name) { 62 - name = isSymbolRef(this.name) 63 - ? fromRef(this.name).finalName 64 - : (fromRef(this.name) as string); 65 - } 66 - if (!name) 51 + override toAst() { 52 + const name = this.$pattern() || this.name.toString(); 53 + if (!name) { 67 54 throw new Error( 68 55 'Param must have either a name or a destructuring pattern', 69 56 ); 57 + } 70 58 return ts.factory.createParameterDeclaration( 71 - this.$decorators(ctx), 59 + this.$decorators(), 72 60 undefined, 73 61 name, 74 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 75 - this.$type(ctx, this._type), 76 - this.$value(ctx), 62 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 63 + this.$type(this._type), 64 + this.$value(), 77 65 ); 78 66 } 79 67 }
+7 -7
packages/openapi-ts/src/ts-dsl/decl/pattern.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; ··· 53 53 return this; 54 54 } 55 55 56 - override toAst(ctx: AstContext) { 56 + override toAst() { 57 57 if (!this.pattern) { 58 58 throw new Error('PatternTsDsl requires object() or array() pattern'); 59 59 } ··· 70 70 ) 71 71 : ts.factory.createBindingElement(undefined, key, alias, undefined), 72 72 ); 73 - const spread = this.createSpread(ctx); 73 + const spread = this.createSpread(); 74 74 if (spread) elements.push(spread); 75 75 return ts.factory.createObjectBindingPattern(elements); 76 76 } ··· 79 79 const elements = this.pattern.values.map((p) => 80 80 ts.factory.createBindingElement(undefined, undefined, p, undefined), 81 81 ); 82 - const spread = this.createSpread(ctx); 82 + const spread = this.createSpread(); 83 83 if (spread) elements.push(spread); 84 84 return ts.factory.createArrayBindingPattern(elements); 85 85 } ··· 87 87 throw new Error('PatternTsDsl requires object() or array() pattern'); 88 88 } 89 89 90 - private createSpread(ctx: AstContext): ts.BindingElement | undefined { 90 + private createSpread(): ts.BindingElement | undefined { 91 91 return this._spread 92 92 ? ts.factory.createBindingElement( 93 - this.$node(ctx, new TokenTsDsl().spread()), 93 + this.$node(new TokenTsDsl().spread()), 94 94 undefined, 95 - this.$node(ctx, new IdTsDsl(this._spread)), 95 + this.$node(new IdTsDsl(this._spread)), 96 96 ) 97 97 : undefined; 98 98 }
+13 -13
packages/openapi-ts/src/ts-dsl/decl/setter.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 15 15 } from '../mixins/modifiers'; 16 16 import { ParamMixin } from '../mixins/param'; 17 17 import { BlockTsDsl } from '../stmt/block'; 18 - 19 - export type SetterName = string | ts.PropertyName; 18 + import { safeAccessorName } from '../utils/name'; 20 19 21 20 const Mixed = AbstractMixin( 22 21 AsyncMixin( ··· 38 37 39 38 export class SetterTsDsl extends Mixed { 40 39 readonly '~dsl' = 'SetterTsDsl'; 40 + override readonly nameSanitizer = safeAccessorName; 41 41 42 - protected name: SetterName; 43 - 44 - constructor(name: SetterName, fn?: (s: SetterTsDsl) => void) { 42 + constructor(name: NodeName, fn?: (s: SetterTsDsl) => void) { 45 43 super(); 46 - this.name = name; 44 + this.name.set(name); 47 45 fn?.(this); 48 46 } 49 47 50 48 override analyze(ctx: AnalysisContext): void { 49 + ctx.analyze(this.name); 50 + 51 51 ctx.pushScope(); 52 52 try { 53 53 super.analyze(ctx); ··· 56 56 } 57 57 } 58 58 59 - override toAst(ctx: AstContext) { 59 + override toAst() { 60 60 const node = ts.factory.createSetAccessorDeclaration( 61 - [...this.$decorators(ctx), ...this.modifiers], 62 - this.name, 63 - this.$params(ctx), 64 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 61 + [...this.$decorators(), ...this.modifiers], 62 + this.$node(this.name) as ts.PropertyName, 63 + this.$params(), 64 + this.$node(new BlockTsDsl(...this._do).pretty()), 65 65 ); 66 - return this.$docs(ctx, node); 66 + return this.$docs(node); 67 67 } 68 68 }
+3 -3
packages/openapi-ts/src/ts-dsl/expr/array.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 59 59 return this; 60 60 } 61 61 62 - override toAst(ctx: AstContext) { 62 + override toAst() { 63 63 const elements = this._elements.map((item) => { 64 - const node = this.$node(ctx, item.expr); 64 + const node = this.$node(item.expr); 65 65 return item.kind === 'spread' 66 66 ? ts.factory.createSpreadElement(node) 67 67 : node;
+6 -11
packages/openapi-ts/src/ts-dsl/expr/as.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 13 8 import { ExprMixin } from '../mixins/expr'; 14 9 import { f } from '../utils/factories'; 15 10 16 - export type AsExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 17 - export type AsType = Symbol | string | TypeTsDsl; 11 + export type AsExpr = NodeName | MaybeTsDsl<ts.Expression>; 12 + export type AsType = NodeName | TypeTsDsl; 18 13 export type AsCtor = (expr: AsExpr, type: AsType) => AsTsDsl; 19 14 20 15 const Mixed = AsMixin(ExprMixin(TsDsl<ts.AsExpression>)); ··· 37 32 ctx.analyze(this.type); 38 33 } 39 34 40 - override toAst(ctx: AstContext) { 35 + override toAst() { 41 36 return ts.factory.createAsExpression( 42 - this.$node(ctx, this.expr), 43 - this.$type(ctx, this.type), 37 + this.$node(this.expr), 38 + this.$type(this.type), 44 39 ); 45 40 } 46 41 }
+26 -27
packages/openapi-ts/src/ts-dsl/expr/attr.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { fromRef, ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 2 + import { fromRef, isSymbol, ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; ··· 18 13 import { regexp } from '../utils/regexp'; 19 14 import { LiteralTsDsl } from './literal'; 20 15 21 - export type AttrLeft = Symbol | string | MaybeTsDsl<ts.Expression>; 22 - export type AttrRight = Symbol | string | ts.MemberName | number; 23 - export type AttrCtor = (left: AttrLeft, right: AttrRight) => AttrTsDsl; 16 + export type AttrLeft = NodeName | MaybeTsDsl<ts.Expression>; 17 + export type AttrCtor = (left: AttrLeft, right: NodeName) => AttrTsDsl; 24 18 25 19 const Mixed = AsMixin( 26 20 ExprMixin( ··· 36 30 readonly '~dsl' = 'AttrTsDsl'; 37 31 38 32 protected left: Ref<AttrLeft>; 39 - protected right: Ref<AttrRight>; 40 33 41 - constructor(left: AttrLeft, right: AttrRight) { 34 + constructor(left: AttrLeft, right: NodeName) { 42 35 super(); 43 36 this.left = ref(left); 44 - this.right = ref(right); 37 + this.name.set(right); 45 38 } 46 39 47 40 override analyze(ctx: AnalysisContext): void { 48 41 super.analyze(ctx); 49 42 ctx.analyze(this.left); 50 - ctx.analyze(this.right); 43 + ctx.analyze(this.name); 51 44 } 52 45 53 - override toAst(ctx: AstContext) { 54 - const leftNode = this.$node(ctx, this.left); 46 + override toAst() { 47 + const leftNode = this.$node(this.left); 55 48 regexp.typeScriptIdentifier.lastIndex = 0; 56 - if ( 57 - typeof fromRef(this.right) === 'number' || 58 - (typeof fromRef(this.right) === 'string' && 59 - !regexp.typeScriptIdentifier.test(fromRef(this.right) as string)) 60 - ) { 49 + const right = fromRef(this.name); 50 + if (!regexp.typeScriptIdentifier.test(this.name.toString())) { 51 + let value = isSymbol(right) ? right.finalName : right; 52 + if (typeof value === 'string') { 53 + if ( 54 + (value.startsWith("'") && value.endsWith("'")) || 55 + (value.startsWith('"') && value.endsWith('"')) 56 + ) { 57 + value = value.slice(1, -1); 58 + } 59 + } 61 60 if (this._optional) { 62 61 return ts.factory.createElementAccessChain( 63 62 leftNode, 64 - this.$node(ctx, new TokenTsDsl().questionDot()), 65 - this.$node(ctx, new LiteralTsDsl(fromRef(this.right) as string)), 63 + this.$node(new TokenTsDsl().questionDot()), 64 + this.$node(new LiteralTsDsl(value)), 66 65 ); 67 66 } 68 67 return ts.factory.createElementAccessExpression( 69 68 leftNode, 70 - this.$node(ctx, new LiteralTsDsl(fromRef(this.right) as string)), 69 + this.$node(new LiteralTsDsl(value)), 71 70 ); 72 71 } 73 72 if (this._optional) { 74 73 return ts.factory.createPropertyAccessChain( 75 74 leftNode, 76 - this.$node(ctx, new TokenTsDsl().questionDot()), 77 - this.$node(ctx, this.right) as ts.MemberName, 75 + this.$node(new TokenTsDsl().questionDot()), 76 + this.$node(this.name) as ts.MemberName, 78 77 ); 79 78 } 80 79 return ts.factory.createPropertyAccessExpression( 81 80 leftNode, 82 - this.$node(ctx, this.right) as ts.MemberName, 81 + this.$node(this.name) as ts.MemberName, 83 82 ); 84 83 } 85 84 }
+4 -9
packages/openapi-ts/src/ts-dsl/expr/await.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 12 7 import { ExprMixin } from '../mixins/expr'; 13 8 import { f } from '../utils/factories'; 14 9 15 - export type AwaitExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 10 + export type AwaitExpr = NodeName | MaybeTsDsl<ts.Expression>; 16 11 export type AwaitCtor = (expr: AwaitExpr) => AwaitTsDsl; 17 12 18 13 const Mixed = ExprMixin(TsDsl<ts.AwaitExpression>); ··· 32 27 ctx.analyze(this._awaitExpr); 33 28 } 34 29 35 - override toAst(ctx: AstContext) { 36 - return ts.factory.createAwaitExpression(this.$node(ctx, this._awaitExpr)); 30 + override toAst() { 31 + return ts.factory.createAwaitExpression(this.$node(this._awaitExpr)); 37 32 } 38 33 } 39 34
+5 -10
packages/openapi-ts/src/ts-dsl/expr/binary.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 12 7 import { AsMixin } from '../mixins/as'; 13 8 import { ExprMixin } from '../mixins/expr'; 14 9 15 - type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 10 + type Expr = NodeName | MaybeTsDsl<ts.Expression>; 16 11 type Op = Operator | ts.BinaryOperator; 17 12 type Operator = 18 13 | '!=' ··· 140 135 return this.opAndExpr('*', expr); 141 136 } 142 137 143 - override toAst(ctx: AstContext) { 138 + override toAst() { 144 139 if (!this._op) { 145 140 throw new Error('BinaryTsDsl: missing operator'); 146 141 } 147 - const expr = this.$node(ctx, this._expr); 142 + const expr = this.$node(this._expr); 148 143 if (!expr) { 149 144 throw new Error('BinaryTsDsl: missing right-hand expression'); 150 145 } 151 - const base = this.$node(ctx, this._base); 146 + const base = this.$node(this._base); 152 147 const operator = 153 148 typeof this._op === 'string' ? this.opToToken(this._op) : this._op; 154 149 return ts.factory.createBinaryExpression(base, operator, expr);
+13 -17
packages/openapi-ts/src/ts-dsl/expr/call.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Symbol, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 2 + import { ref } from '@hey-api/codegen-core'; 6 3 import ts from 'typescript'; 7 4 8 5 import type { MaybeTsDsl } from '../base'; ··· 13 10 import { TypeArgsMixin } from '../mixins/type-args'; 14 11 import { f } from '../utils/factories'; 15 12 16 - export type CallCallee = string | MaybeTsDsl<ts.Expression>; 17 - export type CallArg = Symbol | string | MaybeTsDsl<ts.Expression>; 18 - export type CallArgs = ReadonlyArray<CallArg | undefined>; 19 - export type CallCtor = (callee: CallCallee, ...args: CallArgs) => CallTsDsl; 13 + export type CallArgs = ReadonlyArray<CallExpr | undefined>; 14 + export type CallExpr = NodeName | MaybeTsDsl<ts.Expression>; 15 + export type CallCtor = (expr: CallExpr, ...args: CallArgs) => CallTsDsl; 20 16 21 17 const Mixed = ArgsMixin( 22 18 AsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.CallExpression>))), ··· 25 21 export class CallTsDsl extends Mixed { 26 22 readonly '~dsl' = 'CallTsDsl'; 27 23 28 - protected _callee: CallCallee; 24 + protected _callExpr: Ref<CallExpr>; 29 25 30 - constructor(callee: CallCallee, ...args: CallArgs) { 26 + constructor(expr: CallExpr, ...args: CallArgs) { 31 27 super(); 32 - this._callee = callee; 28 + this._callExpr = ref(expr); 33 29 this.args(...args); 34 30 } 35 31 36 32 override analyze(ctx: AnalysisContext): void { 37 33 super.analyze(ctx); 38 - ctx.analyze(this._callee); 34 + ctx.analyze(this._callExpr); 39 35 } 40 36 41 - override toAst(ctx: AstContext) { 37 + override toAst() { 42 38 return ts.factory.createCallExpression( 43 - this.$node(ctx, this._callee), 44 - this.$generics(ctx), 45 - this.$args(ctx), 39 + this.$node(this._callExpr), 40 + this.$generics(), 41 + this.$args(), 46 42 ); 47 43 } 48 44 }
+10 -10
packages/openapi-ts/src/ts-dsl/expr/expr.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 2 + import { isNode, isSymbol, ref } from '@hey-api/codegen-core'; 8 3 import type ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; ··· 14 9 import { OperatorMixin } from '../mixins/operator'; 15 10 import { TypeExprMixin } from '../mixins/type-expr'; 16 11 17 - type Id = Symbol | string | MaybeTsDsl<ts.Expression>; 12 + type Id = NodeName | MaybeTsDsl<ts.Expression>; 18 13 19 14 const Mixed = AsMixin( 20 15 ExprMixin(OperatorMixin(TypeExprMixin(TsDsl<ts.Expression>))), ··· 28 23 constructor(id: Id) { 29 24 super(); 30 25 this._exprInput = ref(id); 26 + if (typeof id === 'string' || isSymbol(id)) { 27 + this.name.set(id); 28 + } else if (isNode(id)) { 29 + this.name.set(id.name); 30 + } 31 31 } 32 32 33 33 override analyze(ctx: AnalysisContext): void { ··· 35 35 ctx.analyze(this._exprInput); 36 36 } 37 37 38 - override toAst(ctx: AstContext) { 39 - return this.$node(ctx, this._exprInput); 38 + override toAst() { 39 + return this.$node(this._exprInput); 40 40 } 41 41 }
+2 -4
packages/openapi-ts/src/ts-dsl/expr/id.ts
··· 8 8 export class IdTsDsl extends Mixed { 9 9 readonly '~dsl' = 'IdTsDsl'; 10 10 11 - protected name: string; 12 - 13 11 constructor(name: string) { 14 12 super(); 15 - this.name = name; 13 + this.name.set(name); 16 14 } 17 15 18 16 override analyze(ctx: AnalysisContext): void { ··· 20 18 } 21 19 22 20 override toAst() { 23 - return ts.factory.createIdentifier(this.name); 21 + return ts.factory.createIdentifier(this.name.toString()); 24 22 } 25 23 }
+13 -6
packages/openapi-ts/src/ts-dsl/expr/literal.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 7 7 8 8 export type LiteralValue = string | number | boolean | bigint | null; 9 9 10 - const Mixed = AsMixin(TsDsl<ts.LiteralTypeNode['literal']>); 10 + const Mixed = AsMixin( 11 + TsDsl< 12 + | ts.BigIntLiteral 13 + | ts.BooleanLiteral 14 + | ts.NullLiteral 15 + | ts.NumericLiteral 16 + | ts.PrefixUnaryExpression 17 + | ts.StringLiteral 18 + >, 19 + ); 11 20 12 21 export class LiteralTsDsl extends Mixed { 13 22 readonly '~dsl' = 'LiteralTsDsl'; ··· 23 32 super.analyze(ctx); 24 33 } 25 34 26 - override toAst(ctx: AstContext) { 35 + override toAst() { 27 36 if (typeof this.value === 'boolean') { 28 37 return this.value ? ts.factory.createTrue() : ts.factory.createFalse(); 29 38 } 30 39 if (typeof this.value === 'number') { 31 40 const expr = ts.factory.createNumericLiteral(Math.abs(this.value)); 32 - return this.value < 0 33 - ? this.$node(ctx, new PrefixTsDsl(expr).neg()) 34 - : expr; 41 + return this.value < 0 ? this.$node(new PrefixTsDsl(expr).neg()) : expr; 35 42 } 36 43 if (typeof this.value === 'string') { 37 44 return ts.factory.createStringLiteral(this.value, true);
+19 -16
packages/openapi-ts/src/ts-dsl/expr/new.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; 11 6 import { TsDsl } from '../base'; 12 7 import { ArgsMixin } from '../mixins/args'; 8 + import { AsMixin } from '../mixins/as'; 13 9 import { ExprMixin } from '../mixins/expr'; 14 10 import { TypeArgsMixin } from '../mixins/type-args'; 11 + import { f } from '../utils/factories'; 15 12 16 - export type NewExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 13 + export type NewArgs = ReadonlyArray<NewExpr | undefined>; 14 + export type NewExpr = NodeName | MaybeTsDsl<ts.Expression>; 15 + export type NewCtor = (expr: NewExpr, ...args: NewArgs) => NewTsDsl; 17 16 18 - const Mixed = ArgsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.NewExpression>))); 17 + const Mixed = ArgsMixin( 18 + AsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.NewExpression>))), 19 + ); 19 20 20 21 export class NewTsDsl extends Mixed { 21 22 readonly '~dsl' = 'NewTsDsl'; 22 23 23 - protected classExpr: Ref<NewExpr>; 24 + protected _newExpr: Ref<NewExpr>; 24 25 25 - constructor(classExpr: NewExpr, ...args: ReadonlyArray<NewExpr>) { 26 + constructor(expr: NewExpr, ...args: NewArgs) { 26 27 super(); 27 - this.classExpr = ref(classExpr); 28 + this._newExpr = ref(expr); 28 29 this.args(...args); 29 30 } 30 31 31 32 override analyze(ctx: AnalysisContext): void { 32 33 super.analyze(ctx); 33 - ctx.analyze(this.classExpr); 34 + ctx.analyze(this._newExpr); 34 35 } 35 36 36 - override toAst(ctx: AstContext) { 37 + override toAst() { 37 38 return ts.factory.createNewExpression( 38 - this.$node(ctx, this.classExpr), 39 - this.$generics(ctx), 40 - this.$args(ctx), 39 + this.$node(this._newExpr), 40 + this.$generics(), 41 + this.$args(), 41 42 ); 42 43 } 43 44 } 45 + 46 + f.new.set((...args) => new NewTsDsl(...args));
+6 -10
packages/openapi-ts/src/ts-dsl/expr/object.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Symbol, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import type { MaybeTsDsl } from '../base'; ··· 13 9 import { LayoutMixin } from '../mixins/layout'; 14 10 import { ObjectPropTsDsl } from './prop'; 15 11 16 - type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 17 - type Stmt = Symbol | string | MaybeTsDsl<ts.Statement>; 12 + type Expr = NodeName | MaybeTsDsl<ts.Expression>; 13 + type Stmt = NodeName | MaybeTsDsl<ts.Statement>; 18 14 type ExprFn = Expr | ((p: ObjectPropTsDsl) => void); 19 15 type StmtFn = Stmt | ((p: ObjectPropTsDsl) => void); 20 16 ··· 87 83 return this; 88 84 } 89 85 90 - override toAst(ctx: AstContext) { 86 + override toAst() { 91 87 const node = ts.factory.createObjectLiteralExpression( 92 - this.$node(ctx, this._props), 88 + this.$node(this._props), 93 89 this.$multiline(this._props.length), 94 90 ); 95 - return this.$hint(ctx, node); 91 + return this.$hint(node); 96 92 } 97 93 }
+3 -3
packages/openapi-ts/src/ts-dsl/expr/prefix.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 50 50 return this; 51 51 } 52 52 53 - override toAst(ctx: AstContext) { 53 + override toAst() { 54 54 if (!this._expr) { 55 55 throw new Error('Missing expression for prefix unary expression'); 56 56 } ··· 59 59 } 60 60 return ts.factory.createPrefixUnaryExpression( 61 61 this._op, 62 - this.$node(ctx, this._expr), 62 + this.$node(this._expr), 63 63 ); 64 64 } 65 65 }
+16 -25
packages/openapi-ts/src/ts-dsl/expr/prop.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 15 10 import { safePropName } from '../utils/name'; 16 11 import { IdTsDsl } from './id'; 17 12 18 - type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 19 - type Stmt = Symbol | string | MaybeTsDsl<ts.Statement>; 13 + type Expr = NodeName | MaybeTsDsl<ts.Expression>; 14 + type Stmt = NodeName | MaybeTsDsl<ts.Statement>; 20 15 type Kind = 'computed' | 'getter' | 'prop' | 'setter' | 'spread'; 21 16 22 17 type Meta = ··· 58 53 return this; 59 54 } 60 55 61 - override toAst(ctx: AstContext) { 56 + override toAst() { 62 57 this.$validate(); 63 - const node = this.$node(ctx, this._value); 58 + const node = this.$node(this._value); 64 59 if (this.meta.kind === 'spread') { 65 60 if (ts.isStatement(node)) { 66 61 throw new Error( ··· 68 63 ); 69 64 } 70 65 const result = ts.factory.createSpreadAssignment(node); 71 - return this.$docs(ctx, result); 66 + return this.$docs(result); 72 67 } 73 68 if (this.meta.kind === 'getter') { 74 - const getter = new GetterTsDsl( 75 - this.$node(ctx, safePropName(this.meta.name)), 76 - ).do(node); 77 - const result = this.$node(ctx, getter); 78 - return this.$docs(ctx, result); 69 + const getter = new GetterTsDsl(this.meta.name).do(node); 70 + const result = this.$node(getter); 71 + return this.$docs(result); 79 72 } 80 73 if (this.meta.kind === 'setter') { 81 - const setter = new SetterTsDsl( 82 - this.$node(ctx, safePropName(this.meta.name)), 83 - ).do(node); 84 - const result = this.$node(ctx, setter); 85 - return this.$docs(ctx, result); 74 + const setter = new SetterTsDsl(this.meta.name).do(node); 75 + const result = this.$node(setter); 76 + return this.$docs(result); 86 77 } 87 78 if (ts.isIdentifier(node) && node.text === this.meta.name) { 88 79 const result = ts.factory.createShorthandPropertyAssignment( 89 80 this.meta.name, 90 81 ); 91 - return this.$docs(ctx, result); 82 + return this.$docs(result); 92 83 } 93 84 if (ts.isStatement(node)) { 94 85 throw new Error( ··· 98 89 const result = ts.factory.createPropertyAssignment( 99 90 this.meta.kind === 'computed' 100 91 ? ts.factory.createComputedPropertyName( 101 - this.$node(ctx, new IdTsDsl(this.meta.name)), 92 + this.$node(new IdTsDsl(this.meta.name)), 102 93 ) 103 - : this.$node(ctx, safePropName(this.meta.name)), 94 + : this.$node(safePropName(this.meta.name)), 104 95 node, 105 96 ); 106 - return this.$docs(ctx, result); 97 + return this.$docs(result); 107 98 } 108 99 109 100 $validate(): asserts this is this & {
+5 -9
packages/openapi-ts/src/ts-dsl/expr/template.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { fromRef, isSymbol, ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; 11 6 import { TsDsl } from '../base'; 12 7 13 - export type TemplatePart = Symbol | string | MaybeTsDsl<ts.Expression>; 8 + export type TemplatePart = NodeName | MaybeTsDsl<ts.Expression>; 14 9 15 10 const Mixed = TsDsl<ts.TemplateExpression | ts.NoSubstitutionTemplateLiteral>; 16 11 ··· 36 31 return this; 37 32 } 38 33 39 - override toAst(ctx: AstContext) { 34 + override toAst() { 40 35 const parts = this.$node( 41 - ctx, 42 36 this.parts.map((p) => { 43 37 const part = fromRef(p); 44 38 return isSymbol(part) ? part.finalName : part; ··· 59 53 index++; 60 54 } 61 55 normalized.push(merged); 56 + } else if (typeof current === 'number') { 57 + normalized.push(String(current)); 62 58 } else { 63 59 normalized.push(current); 64 60 }
+5 -5
packages/openapi-ts/src/ts-dsl/expr/ternary.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 40 40 return this; 41 41 } 42 42 43 - override toAst(ctx: AstContext) { 43 + override toAst() { 44 44 if (!this._condition) throw new Error('Missing condition in ternary'); 45 45 if (!this._then) throw new Error('Missing then expression in ternary'); 46 46 if (!this._else) throw new Error('Missing else expression in ternary'); 47 47 48 48 return ts.factory.createConditionalExpression( 49 - this.$node(ctx, this._condition), 49 + this.$node(this._condition), 50 50 undefined, 51 - this.$node(ctx, this._then), 51 + this.$node(this._then), 52 52 undefined, 53 - this.$node(ctx, this._else), 53 + this.$node(this._else), 54 54 ); 55 55 } 56 56 }
+3 -3
packages/openapi-ts/src/ts-dsl/expr/typeof.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 26 26 ctx.analyze(this._expr); 27 27 } 28 28 29 - override toAst(ctx: AstContext) { 30 - return ts.factory.createTypeOfExpression(this.$node(ctx, this._expr)); 29 + override toAst() { 30 + return ts.factory.createTypeOfExpression(this.$node(this._expr)); 31 31 } 32 32 } 33 33
+2 -1
packages/openapi-ts/src/ts-dsl/index.ts
··· 357 357 358 358 export type { MaybeTsDsl, TypeTsDsl } from './base'; 359 359 export { TsDsl } from './base'; 360 - export { TypeScriptRenderer } from './render/typescript'; 360 + export { TsDslContext } from './utils/context'; 361 361 export { keywords } from './utils/keywords'; 362 362 export { regexp } from './utils/regexp'; 363 + export { TypeScriptRenderer } from './utils/render'; 363 364 export { reserved } from './utils/reserved';
+7 -5
packages/openapi-ts/src/ts-dsl/layout/doc.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 import { IdTsDsl } from '../expr/id'; 7 + import { TsDslContext } from '../utils/context'; 7 8 8 - type DocMaybeLazy<T> = ((ctx: AstContext) => T) | T; 9 + type DocMaybeLazy<T> = ((ctx: TsDslContext) => T) | T; 9 10 export type DocFn = (d: DocTsDsl) => void; 10 11 export type DocLines = DocMaybeLazy<MaybeArray<string>>; 11 12 ··· 29 30 return this; 30 31 } 31 32 32 - apply<T extends ts.Node>(ctx: AstContext, node: T): T { 33 + apply<T extends ts.Node>(node: T): T { 34 + const ctx = new TsDslContext(); 33 35 const lines = this._lines.reduce((lines: Array<string>, line: DocLines) => { 34 36 if (typeof line === 'function') line = line(ctx); 35 37 for (const l of typeof line === 'string' ? [line] : line) { ··· 69 71 return node; 70 72 } 71 73 72 - override toAst(ctx: AstContext): ts.Node { 74 + override toAst(): ts.Node { 73 75 // this class does not build a standalone node; 74 76 // it modifies other nodes via `apply()`. 75 77 // Return a dummy comment node for compliance. 76 - return this.$node(ctx, new IdTsDsl('')); 78 + return this.$node(new IdTsDsl('')); 77 79 } 78 80 }
+7 -5
packages/openapi-ts/src/ts-dsl/layout/hint.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 import { IdTsDsl } from '../expr/id'; 7 + import { TsDslContext } from '../utils/context'; 7 8 8 - type HintMaybeLazy<T> = ((ctx: AstContext) => T) | T; 9 + type HintMaybeLazy<T> = ((ctx: TsDslContext) => T) | T; 9 10 export type HintFn = (d: HintTsDsl) => void; 10 11 export type HintLines = HintMaybeLazy<MaybeArray<string>>; 11 12 ··· 29 30 return this; 30 31 } 31 32 32 - apply<T extends ts.Node>(ctx: AstContext, node: T): T { 33 + apply<T extends ts.Node>(node: T): T { 34 + const ctx = new TsDslContext(); 33 35 const lines = this._lines.reduce( 34 36 (lines: Array<string>, line: HintLines) => { 35 37 if (typeof line === 'function') line = line(ctx); ··· 54 56 return node; 55 57 } 56 58 57 - override toAst(ctx: AstContext): ts.Node { 59 + override toAst(): ts.Node { 58 60 // this class does not build a standalone node; 59 61 // it modifies other nodes via `apply()`. 60 62 // Return a dummy comment node for compliance. 61 - return this.$node(ctx, new IdTsDsl('')); 63 + return this.$node(new IdTsDsl('')); 62 64 } 63 65 }
+3 -3
packages/openapi-ts/src/ts-dsl/layout/newline.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 11 11 super.analyze(ctx); 12 12 } 13 13 14 - override toAst(ctx: AstContext): ts.Identifier { 15 - return this.$node(ctx, new IdTsDsl('\n')); 14 + override toAst(): ts.Identifier { 15 + return this.$node(new IdTsDsl('\n')); 16 16 } 17 17 }
+7 -5
packages/openapi-ts/src/ts-dsl/layout/note.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; 5 5 import { TsDsl } from '../base'; 6 6 import { IdTsDsl } from '../expr/id'; 7 + import { TsDslContext } from '../utils/context'; 7 8 8 - type NoteMaybeLazy<T> = ((ctx: AstContext) => T) | T; 9 + type NoteMaybeLazy<T> = ((ctx: TsDslContext) => T) | T; 9 10 export type NoteFn = (d: NoteTsDsl) => void; 10 11 export type NoteLines = NoteMaybeLazy<MaybeArray<string>>; 11 12 ··· 29 30 return this; 30 31 } 31 32 32 - apply<T extends ts.Node>(ctx: AstContext, node: T): T { 33 + apply<T extends ts.Node>(node: T): T { 34 + const ctx = new TsDslContext(); 33 35 const lines = this._lines.reduce( 34 36 (lines: Array<string>, line: NoteLines) => { 35 37 if (typeof line === 'function') line = line(ctx); ··· 52 54 return node; 53 55 } 54 56 55 - override toAst(ctx: AstContext): ts.Node { 57 + override toAst(): ts.Node { 56 58 // this class does not build a standalone node; 57 59 // it modifies other nodes via `apply()`. 58 60 // Return a dummy comment node for compliance. 59 - return this.$node(ctx, new IdTsDsl('')); 61 + return this.$node(new IdTsDsl('')); 60 62 } 61 63 }
+5 -6
packages/openapi-ts/src/ts-dsl/mixins/args.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 Node, 4 + NodeName, 5 5 Ref, 6 - Symbol, 7 6 } from '@hey-api/codegen-core'; 8 7 import { ref } from '@hey-api/codegen-core'; 9 8 import type ts from 'typescript'; ··· 11 10 import type { MaybeTsDsl } from '../base'; 12 11 import type { BaseCtor, MixinCtor } from './types'; 13 12 14 - type Arg = Symbol | string | MaybeTsDsl<ts.Expression>; 13 + type Arg = NodeName | MaybeTsDsl<ts.Expression>; 15 14 16 15 export interface ArgsMethods extends Node { 17 16 /** Renders the arguments into an array of `Expression`s. */ 18 - $args(ctx: AstContext): ReadonlyArray<ts.Expression>; 17 + $args(): ReadonlyArray<ts.Expression>; 19 18 /** Adds a single expression argument. */ 20 19 arg(arg: Arg | undefined): this; 21 20 /** Adds one or more expression arguments. */ ··· 52 51 return this; 53 52 } 54 53 55 - protected $args(ctx: AstContext): ReadonlyArray<ts.Expression> { 56 - return this.$node(ctx, this._args).map((arg) => this.$node(ctx, arg)); 54 + protected $args(): ReadonlyArray<ts.Expression> { 55 + return this.$node(this._args).map((arg) => this.$node(arg)); 57 56 } 58 57 } 59 58
+6 -11
packages/openapi-ts/src/ts-dsl/mixins/decorator.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Node, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 7 2 import type ts from 'typescript'; 8 3 9 4 import type { MaybeTsDsl } from '../base'; ··· 12 7 13 8 export interface DecoratorMethods extends Node { 14 9 /** Renders the decorators into an array of `ts.Decorator`s. */ 15 - $decorators(ctx: AstContext): ReadonlyArray<ts.Decorator>; 10 + $decorators(): ReadonlyArray<ts.Decorator>; 16 11 /** Adds a decorator (e.g. `@sealed({ in: 'root' })`). */ 17 12 decorator( 18 - name: Symbol | string | MaybeTsDsl<ts.Expression>, 13 + name: NodeName | MaybeTsDsl<ts.Expression>, 19 14 ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 20 15 ): this; 21 16 } ··· 34 29 } 35 30 36 31 protected decorator( 37 - name: Symbol | string | MaybeTsDsl<ts.Expression>, 32 + name: NodeName, 38 33 ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 39 34 ): this { 40 35 this.decorators.push(new DecoratorTsDsl(name, ...args)); 41 36 return this; 42 37 } 43 38 44 - protected $decorators(ctx: AstContext): ReadonlyArray<ts.Decorator> { 45 - return this.$node(ctx, this.decorators); 39 + protected $decorators(): ReadonlyArray<ts.Decorator> { 40 + return this.$node(this.decorators); 46 41 } 47 42 } 48 43
+4 -7
packages/openapi-ts/src/ts-dsl/mixins/do.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 9 9 10 10 export interface DoMethods extends Node { 11 11 /** Renders the collected `.do()` calls into an array of `Statement` nodes. */ 12 - $do(ctx: AstContext): ReadonlyArray<ts.Statement>; 12 + $do(): ReadonlyArray<ts.Statement>; 13 13 _do: Array<DoExpr>; 14 14 /** Adds one or more expressions/statements to the body. */ 15 15 do(...items: ReadonlyArray<DoExpr>): this; ··· 41 41 return this; 42 42 } 43 43 44 - protected $do(ctx: AstContext): ReadonlyArray<ts.Statement> { 45 - return this.$node( 46 - ctx, 47 - this._do.map((item) => new StmtTsDsl(item)), 48 - ); 44 + protected $do(): ReadonlyArray<ts.Statement> { 45 + return this.$node(this._do.map((item) => new StmtTsDsl(item))); 49 46 } 50 47 } 51 48
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/doc.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { DocFn, DocLines } from '../layout/doc'; ··· 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 8 export interface DocMethods extends Node { 9 - $docs<T extends ts.Node>(ctx: AstContext, node: T): T; 9 + $docs<T extends ts.Node>(node: T): T; 10 10 doc(lines?: DocLines, fn?: DocFn): this; 11 11 } 12 12 ··· 25 25 return this; 26 26 } 27 27 28 - protected $docs<T extends ts.Node>(ctx: AstContext, node: T): T { 29 - return this._doc ? this._doc.apply(ctx, node) : node; 28 + protected $docs<T extends ts.Node>(node: T): T { 29 + return this._doc ? this._doc.apply(node) : node; 30 30 } 31 31 } 32 32
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/hint.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { HintFn, HintLines } from '../layout/hint'; ··· 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 8 export interface HintMethods extends Node { 9 - $hint<T extends ts.Node>(ctx: AstContext, node: T): T; 9 + $hint<T extends ts.Node>(node: T): T; 10 10 hint(lines?: HintLines, fn?: HintFn): this; 11 11 } 12 12 ··· 25 25 return this; 26 26 } 27 27 28 - protected $hint<T extends ts.Node>(ctx: AstContext, node: T): T { 29 - return this._hint ? this._hint.apply(ctx, node) : node; 28 + protected $hint<T extends ts.Node>(node: T): T { 29 + return this._hint ? this._hint.apply(node) : node; 30 30 } 31 31 } 32 32
+74 -16
packages/openapi-ts/src/ts-dsl/mixins/modifiers.ts
··· 4 4 import type { BaseCtor, MixinCtor } from './types'; 5 5 6 6 export type Modifiers = { 7 + /** 8 + * Checks if the specified modifier is present. 9 + * 10 + * @param modifier - The modifier to check. 11 + * @returns True if the modifier is present, false otherwise. 12 + */ 13 + hasModifier(modifier: Modifier): boolean; 7 14 modifiers: Array<ts.Modifier>; 8 15 }; 9 16 17 + type Modifier = 18 + | 'abstract' 19 + | 'async' 20 + | 'const' 21 + | 'declare' 22 + | 'default' 23 + | 'export' 24 + | 'override' 25 + | 'private' 26 + | 'protected' 27 + | 'public' 28 + | 'readonly' 29 + | 'static'; 30 + 10 31 export interface ModifierMethods extends Modifiers { 11 32 /** 12 33 * Adds a modifier of the specified kind to the modifiers list if the condition is true. 13 34 * 14 - * @param kind - The syntax kind of the modifier to add. 35 + * @param modifier - The modifier to add. 15 36 * @param condition - Whether to add the modifier. 16 37 * @returns The parent node for chaining. 17 38 */ 18 - _m(kind: ts.ModifierSyntaxKind, condition: boolean): this; 39 + _m(modifier: Modifier, condition: boolean): this; 40 + } 41 + 42 + function modifierToKind(modifier: Modifier): ts.ModifierSyntaxKind { 43 + switch (modifier) { 44 + case 'abstract': 45 + return ts.SyntaxKind.AbstractKeyword; 46 + case 'async': 47 + return ts.SyntaxKind.AsyncKeyword; 48 + case 'const': 49 + return ts.SyntaxKind.ConstKeyword; 50 + case 'declare': 51 + return ts.SyntaxKind.DeclareKeyword; 52 + case 'default': 53 + return ts.SyntaxKind.DefaultKeyword; 54 + case 'export': 55 + return ts.SyntaxKind.ExportKeyword; 56 + case 'override': 57 + return ts.SyntaxKind.OverrideKeyword; 58 + case 'private': 59 + return ts.SyntaxKind.PrivateKeyword; 60 + case 'protected': 61 + return ts.SyntaxKind.ProtectedKeyword; 62 + case 'public': 63 + return ts.SyntaxKind.PublicKeyword; 64 + case 'readonly': 65 + return ts.SyntaxKind.ReadonlyKeyword; 66 + case 'static': 67 + return ts.SyntaxKind.StaticKeyword; 68 + } 19 69 } 20 70 21 71 function ModifiersMixin<T extends ts.Node, TBase extends BaseCtor<T>>( ··· 28 78 super.analyze(ctx); 29 79 } 30 80 31 - protected _m(kind: ts.ModifierSyntaxKind, condition: boolean): this { 32 - if (condition) this.modifiers.push(ts.factory.createModifier(kind)); 81 + protected hasModifier(modifier: Modifier): boolean { 82 + const kind = modifierToKind(modifier); 83 + return Boolean(this.modifiers.find((mod) => mod.kind === kind)); 84 + } 85 + 86 + protected _m(modifier: Modifier, condition: boolean): this { 87 + if (condition) { 88 + const kind = modifierToKind(modifier); 89 + this.modifiers.push(ts.factory.createModifier(kind)); 90 + } 33 91 return this; 34 92 } 35 93 } ··· 58 116 abstract class Abstract extends Mixed { 59 117 protected abstract(condition?: boolean): this { 60 118 const cond = arguments.length === 0 ? true : Boolean(condition); 61 - return this._m(ts.SyntaxKind.AbstractKeyword, cond); 119 + return this._m('abstract', cond); 62 120 } 63 121 } 64 122 ··· 86 144 abstract class Async extends Mixed { 87 145 protected async(condition?: boolean): this { 88 146 const cond = arguments.length === 0 ? true : Boolean(condition); 89 - return this._m(ts.SyntaxKind.AsyncKeyword, cond); 147 + return this._m('async', cond); 90 148 } 91 149 } 92 150 ··· 114 172 abstract class Const extends Mixed { 115 173 protected const(condition?: boolean): this { 116 174 const cond = arguments.length === 0 ? true : Boolean(condition); 117 - return this._m(ts.SyntaxKind.ConstKeyword, cond); 175 + return this._m('const', cond); 118 176 } 119 177 } 120 178 ··· 142 200 abstract class Declare extends Mixed { 143 201 protected declare(condition?: boolean): this { 144 202 const cond = arguments.length === 0 ? true : Boolean(condition); 145 - return this._m(ts.SyntaxKind.DeclareKeyword, cond); 203 + return this._m('declare', cond); 146 204 } 147 205 } 148 206 ··· 176 234 */ 177 235 protected default(condition?: boolean): this { 178 236 const cond = arguments.length === 0 ? true : Boolean(condition); 179 - return this._m(ts.SyntaxKind.DefaultKeyword, cond); 237 + return this._m('default', cond); 180 238 } 181 239 } 182 240 ··· 213 271 this.exported = cond; 214 272 // TODO: remove this side-effect once planner handles exported flag 215 273 if (this.symbol) this.symbol.setExported(cond); 216 - return this._m(ts.SyntaxKind.ExportKeyword, cond); 274 + return this._m('export', cond); 217 275 } 218 276 } 219 277 ··· 241 299 abstract class Override extends Mixed { 242 300 protected override(condition?: boolean): this { 243 301 const cond = arguments.length === 0 ? true : Boolean(condition); 244 - return this._m(ts.SyntaxKind.OverrideKeyword, cond); 302 + return this._m('override', cond); 245 303 } 246 304 } 247 305 ··· 269 327 abstract class Private extends Mixed { 270 328 protected private(condition?: boolean): this { 271 329 const cond = arguments.length === 0 ? true : Boolean(condition); 272 - return this._m(ts.SyntaxKind.PrivateKeyword, cond); 330 + return this._m('private', cond); 273 331 } 274 332 } 275 333 ··· 297 355 abstract class Protected extends Mixed { 298 356 protected protected(condition?: boolean): this { 299 357 const cond = arguments.length === 0 ? true : Boolean(condition); 300 - return this._m(ts.SyntaxKind.ProtectedKeyword, cond); 358 + return this._m('protected', cond); 301 359 } 302 360 } 303 361 ··· 325 383 abstract class Public extends Mixed { 326 384 protected public(condition?: boolean): this { 327 385 const cond = arguments.length === 0 ? true : Boolean(condition); 328 - return this._m(ts.SyntaxKind.PublicKeyword, cond); 386 + return this._m('public', cond); 329 387 } 330 388 } 331 389 ··· 353 411 abstract class Readonly extends Mixed { 354 412 protected readonly(condition?: boolean): this { 355 413 const cond = arguments.length === 0 ? true : Boolean(condition); 356 - return this._m(ts.SyntaxKind.ReadonlyKeyword, cond); 414 + return this._m('readonly', cond); 357 415 } 358 416 } 359 417 ··· 381 439 abstract class Static extends Mixed { 382 440 protected static(condition?: boolean): this { 383 441 const cond = arguments.length === 0 ? true : Boolean(condition); 384 - return this._m(ts.SyntaxKind.StaticKeyword, cond); 442 + return this._m('static', cond); 385 443 } 386 444 } 387 445
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/note.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { NoteFn, NoteLines } from '../layout/note'; ··· 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 8 export interface NoteMethods extends Node { 9 - $note<T extends ts.Node>(ctx: AstContext, node: T): T; 9 + $note<T extends ts.Node>(node: T): T; 10 10 note(lines?: NoteLines, fn?: NoteFn): this; 11 11 } 12 12 ··· 25 25 return this; 26 26 } 27 27 28 - protected $note<T extends ts.Node>(ctx: AstContext, node: T): T { 29 - return this._note ? this._note.apply(ctx, node) : node; 28 + protected $note<T extends ts.Node>(node: T): T { 29 + return this._note ? this._note.apply(node) : node; 30 30 } 31 31 } 32 32
+2 -2
packages/openapi-ts/src/ts-dsl/mixins/operator.ts
··· 1 - import type { AnalysisContext, Node, Symbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; 5 5 import { BinaryTsDsl } from '../expr/binary'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 8 + type Expr = NodeName | MaybeTsDsl<ts.Expression>; 9 9 10 10 export interface OperatorMethods extends Node { 11 11 /** Logical AND — `this && expr` */
+6 -6
packages/openapi-ts/src/ts-dsl/mixins/param.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; 5 - import type { ParamCtor, ParamName } from '../decl/param'; 5 + import type { ParamCtor } from '../decl/param'; 6 6 import { ParamTsDsl } from '../decl/param'; 7 7 import type { BaseCtor, MixinCtor } from './types'; 8 8 9 9 export interface ParamMethods extends Node { 10 10 /** Renders the parameters into an array of `ParameterDeclaration`s. */ 11 - $params(ast: AstContext): ReadonlyArray<ts.ParameterDeclaration>; 11 + $params(): ReadonlyArray<ts.ParameterDeclaration>; 12 12 /** Adds a parameter. */ 13 13 param(...args: Parameters<ParamCtor>): this; 14 14 /** Adds multiple parameters. */ ··· 29 29 } 30 30 31 31 protected param( 32 - name: ParamName | ((p: ParamTsDsl) => void), 32 + name: NodeName | ((p: ParamTsDsl) => void), 33 33 fn?: (p: ParamTsDsl) => void, 34 34 ): this { 35 35 const p = new ParamTsDsl(name, fn); ··· 44 44 return this; 45 45 } 46 46 47 - protected $params(ctx: AstContext): ReadonlyArray<ts.ParameterDeclaration> { 48 - return this.$node(ctx, this._params); 47 + protected $params(): ReadonlyArray<ts.ParameterDeclaration> { 48 + return this.$node(this._params); 49 49 } 50 50 } 51 51
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/pattern.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeArray } from '../base'; ··· 7 7 8 8 export interface PatternMethods extends Node { 9 9 /** Renders the pattern into a `BindingName`. */ 10 - $pattern(ctx: AstContext): ts.BindingName | undefined; 10 + $pattern(): ts.BindingName | undefined; 11 11 /** Defines an array binding pattern. */ 12 12 array(...props: ReadonlyArray<string> | [ReadonlyArray<string>]): this; 13 13 /** Defines an object binding pattern. */ ··· 53 53 } 54 54 55 55 /** Renders the pattern into a `BindingName`. */ 56 - protected $pattern(ctx: AstContext): ts.BindingName | undefined { 56 + protected $pattern(): ts.BindingName | undefined { 57 57 if (!this.pattern) return; 58 - return this.$node(ctx, this.pattern); 58 + return this.$node(this.pattern); 59 59 } 60 60 } 61 61
+5 -8
packages/openapi-ts/src/ts-dsl/mixins/type-args.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 3 Node, 4 + NodeName, 5 5 Ref, 6 - Symbol, 7 6 } from '@hey-api/codegen-core'; 8 7 import { ref } from '@hey-api/codegen-core'; 9 8 import type ts from 'typescript'; ··· 11 10 import type { MaybeTsDsl, TypeTsDsl } from '../base'; 12 11 import type { BaseCtor, MixinCtor } from './types'; 13 12 14 - type Arg = Symbol | string | MaybeTsDsl<TypeTsDsl>; 13 + type Arg = NodeName | MaybeTsDsl<TypeTsDsl>; 15 14 16 15 export interface TypeArgsMethods extends Node { 17 16 /** Returns the type arguments as an array of ts.TypeNode nodes. */ 18 - $generics(ctx: AstContext): ReadonlyArray<ts.TypeNode> | undefined; 17 + $generics(): ReadonlyArray<ts.TypeNode> | undefined; 19 18 /** Adds a single type argument (e.g. `string` in `Foo<string>`). */ 20 19 generic(arg: Arg): this; 21 20 /** Adds type arguments (e.g. `Map<string, number>`). */ ··· 45 44 return this; 46 45 } 47 46 48 - protected $generics( 49 - ctx: AstContext, 50 - ): ReadonlyArray<ts.TypeNode> | undefined { 51 - return this.$type(ctx, this._generics); 47 + protected $generics(): ReadonlyArray<ts.TypeNode> | undefined { 48 + return this.$type(this._generics); 52 49 } 53 50 } 54 51
+15 -19
packages/openapi-ts/src/ts-dsl/mixins/type-params.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Node, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { isSymbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 2 + import { isRef, isSymbol } from '@hey-api/codegen-core'; 8 3 import type ts from 'typescript'; 9 4 10 5 import type { MaybeTsDsl } from '../base'; ··· 13 8 14 9 export interface TypeParamsMethods extends Node { 15 10 /** Returns the type parameters as an array of ts.TypeParameterDeclaration nodes. */ 16 - $generics( 17 - ast: AstContext, 18 - ): ReadonlyArray<ts.TypeParameterDeclaration> | undefined; 11 + $generics(): ReadonlyArray<ts.TypeParameterDeclaration> | undefined; 19 12 /** Adds a single type parameter (e.g. `T` in `Array<T>`). */ 20 13 generic(...args: ConstructorParameters<typeof TypeParamTsDsl>): this; 21 14 /** Adds type parameters (e.g. `Map<string, T>`). */ 22 - generics( 23 - ...args: ReadonlyArray<Symbol | string | MaybeTsDsl<TypeParamTsDsl>> 24 - ): this; 15 + generics(...args: ReadonlyArray<NodeName | MaybeTsDsl<TypeParamTsDsl>>): this; 25 16 } 26 17 27 18 export function TypeParamsMixin<T extends ts.Node, TBase extends BaseCtor<T>>( ··· 46 37 } 47 38 48 39 protected generics( 49 - ...args: ReadonlyArray<Symbol | string | MaybeTsDsl<TypeParamTsDsl>> 40 + ...args: ReadonlyArray<NodeName | MaybeTsDsl<TypeParamTsDsl>> 50 41 ): this { 51 42 for (let arg of args) { 52 - if (typeof arg === 'string' || isSymbol(arg)) { 43 + if ( 44 + typeof arg === 'string' || 45 + typeof arg === 'number' || 46 + isSymbol(arg) || 47 + isRef(arg) 48 + ) { 53 49 arg = new TypeParamTsDsl(arg); 54 50 } 55 51 this._generics.push(arg); ··· 57 53 return this; 58 54 } 59 55 60 - protected $generics( 61 - ctx: AstContext, 62 - ): ReadonlyArray<ts.TypeParameterDeclaration> | undefined { 63 - return this.$node(ctx, this._generics); 56 + protected $generics(): 57 + | ReadonlyArray<ts.TypeParameterDeclaration> 58 + | undefined { 59 + return this.$node(this._generics); 64 60 } 65 61 } 66 62
+6 -7
packages/openapi-ts/src/ts-dsl/mixins/type-returns.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, NodeName } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import { TypeTsDsl } from '../base'; 5 - import type { TypeExprName } from '../type/expr'; 6 5 import { TypeExprTsDsl } from '../type/expr'; 7 6 import type { BaseCtor, MixinCtor } from './types'; 8 7 9 8 export interface TypeReturnsMethods extends Node { 10 9 /** Returns the return type node. */ 11 - $returns(ctx: AstContext): ts.TypeNode | undefined; 10 + $returns(): ts.TypeNode | undefined; 12 11 /** Sets the return type. */ 13 - returns(type: TypeExprName | TypeTsDsl): this; 12 + returns(type: NodeName | TypeTsDsl): this; 14 13 } 15 14 16 15 export function TypeReturnsMixin<T extends ts.Node, TBase extends BaseCtor<T>>( ··· 24 23 ctx.analyze(this._returns); 25 24 } 26 25 27 - protected returns(type: TypeExprName | TypeTsDsl): this { 26 + protected returns(type: NodeName | TypeTsDsl): this { 28 27 this._returns = 29 28 type instanceof TypeTsDsl ? type : new TypeExprTsDsl(type); 30 29 return this; 31 30 } 32 31 33 - protected $returns(ctx: AstContext): ts.TypeNode | undefined { 34 - return this.$type(ctx, this._returns); 32 + protected $returns(): ts.TypeNode | undefined { 33 + return this.$type(this._returns); 35 34 } 36 35 } 37 36
+4 -4
packages/openapi-ts/src/ts-dsl/mixins/value.ts
··· 1 - import type { AnalysisContext, AstContext, Node } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 7 7 export type ValueExpr = string | MaybeTsDsl<ts.Expression>; 8 8 9 9 export interface ValueMethods extends Node { 10 - $value(ctx: AstContext): ts.Expression | undefined; 10 + $value(): ts.Expression | undefined; 11 11 /** Sets the initializer expression (e.g. `= expr`). */ 12 12 assign(expr: ValueExpr): this; 13 13 } ··· 28 28 return this; 29 29 } 30 30 31 - protected $value(ctx: AstContext): ts.Expression | undefined { 32 - return this.$node(ctx, this.value); 31 + protected $value(): ts.Expression | undefined { 32 + return this.$node(this.value); 33 33 } 34 34 } 35 35
+9 -22
packages/openapi-ts/src/ts-dsl/render/__tests__/typescript.test.ts packages/openapi-ts/src/ts-dsl/utils/__tests__/render.test.ts
··· 3 3 import ts from 'typescript'; 4 4 import { describe, expect, it } from 'vitest'; 5 5 6 - import { TypeScriptRenderer } from '../typescript'; 7 - import type { ModuleExport, ModuleImport } from '../utils'; 6 + import type { TsDsl } from '~/ts-dsl'; 7 + 8 + import { TypeScriptRenderer } from '../render'; 9 + import type { ModuleExport, ModuleImport } from '../render-utils'; 8 10 9 11 describe('TypeScriptRenderer', () => { 10 12 const renderer = new TypeScriptRenderer(); ··· 20 22 const mockCtx = ( 21 23 fileOverrides = {}, 22 24 projectOverrides = {}, 23 - ): RenderContext => ({ 24 - astContext: { 25 - getAccess(node) { 26 - return node; 27 - }, 28 - }, 25 + ): RenderContext<TsDsl> => ({ 29 26 file: mockFile(fileOverrides), 30 27 project: new Project({ 31 28 root: '/root', ··· 38 35 expect(renderer.supports(mockCtx({ language: 'javascript' }))).toBe(false); 39 36 }); 40 37 41 - it('render() produces header even with empty file', () => { 42 - const ctx = mockCtx(); 43 - const result = renderer.render(ctx); 44 - expect(result).toContain( 45 - '// This file is auto-generated by @hey-api/openapi-ts', 46 - ); 47 - }); 48 - 49 38 it('renderImport() generates named and namespace imports correctly', () => { 50 - const ctx = mockCtx(); 51 39 const group: ModuleImport = { 52 40 imports: [ 53 41 { 54 42 isTypeOnly: false, 55 - kind: 'named', 56 43 localName: 'A', 57 44 sourceName: 'A', 58 45 }, 59 46 ], 60 47 isTypeOnly: false, 48 + kind: 'named', 49 + localName: undefined, 61 50 modulePath: 'foo', 62 - namespaceImport: undefined, 63 51 }; 64 - const node = renderer['renderImport'](ctx, group); 52 + const node = TypeScriptRenderer.toImportAst(group); 65 53 expect(ts.isImportDeclaration(node)).toBe(true); 66 54 }); 67 55 68 56 it('renderExport() generates named and namespace exports correctly', () => { 69 - const ctx = mockCtx(); 70 57 const group: ModuleExport = { 71 58 canExportAll: false, 72 59 exports: [ ··· 81 68 modulePath: 'bar', 82 69 namespaceExport: undefined, 83 70 }; 84 - const node = renderer['renderExport'](ctx, group); 71 + const node = TypeScriptRenderer.toExportAst(group); 85 72 expect(ts.isExportDeclaration(node)).toBe(true); 86 73 }); 87 74 });
+147 -88
packages/openapi-ts/src/ts-dsl/render/typescript.ts packages/openapi-ts/src/ts-dsl/utils/render.ts
··· 1 1 import type { RenderContext, Renderer } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 + import type { TsDsl } from '~/ts-dsl'; 4 5 import { $ } from '~/ts-dsl'; 5 6 7 + import type { MaybeArray, MaybeFunc } from '../base'; 6 8 import type { 7 9 ModuleExport, 8 10 ModuleImport, 9 11 SortGroup, 10 12 SortKey, 11 13 SortModule, 12 - } from './utils'; 13 - import { moduleSortKey, nodeToString } from './utils'; 14 + } from './render-utils'; 15 + import { astToString, moduleSortKey } from './render-utils'; 16 + 17 + type Exports = ReadonlyArray<ReadonlyArray<ModuleExport>>; 18 + type ExportsOptions = { 19 + preferExportAll?: boolean; 20 + }; 21 + type Header = MaybeArray<string> | null | undefined; 22 + type Imports = ReadonlyArray<ReadonlyArray<ModuleImport>>; 23 + 24 + function headerToLines(header: Header): ReadonlyArray<string> { 25 + if (!header) return []; 26 + const lines: Array<string> = []; 27 + if (typeof header === 'string') { 28 + lines.push(...header.split(/\r?\n/)); 29 + return lines; 30 + } 31 + for (const line of header) { 32 + lines.push(...line.split(/\r?\n/)); 33 + } 34 + return lines; 35 + } 14 36 15 37 export class TypeScriptRenderer implements Renderer { 16 38 /** 39 + * Function to generate a file header. 40 + * 41 + * @private 42 + */ 43 + private _header?: MaybeFunc<(ctx: RenderContext<TsDsl>) => Header>; 44 + /** 17 45 * Whether `export * from 'module'` should be used when possible instead of named exports. 18 46 * 19 47 * @private 20 48 */ 21 - private preferExportAll: boolean; 49 + private _preferExportAll: boolean; 22 50 /** 23 51 * Controls whether imports/exports include a file extension (e.g., '.ts' or '.js'). 24 52 * 25 53 * @private 26 54 */ 27 - private preferFileExtension: string; 55 + private _preferFileExtension: string; 28 56 /** 29 57 * Optional function to transform module specifiers. 30 58 * 31 59 * @private 32 60 */ 33 - private resolveModuleName?: (moduleName: string) => string | undefined; 61 + private _resolveModuleName?: (moduleName: string) => string | undefined; 34 62 35 63 constructor( 36 64 args: { 65 + header?: MaybeFunc<(ctx: RenderContext<TsDsl>) => Header>; 37 66 preferExportAll?: boolean; 38 67 preferFileExtension?: string; 39 68 resolveModuleName?: (moduleName: string) => string | undefined; 40 69 } = {}, 41 70 ) { 42 - this.preferExportAll = args.preferExportAll ?? false; 43 - this.preferFileExtension = args.preferFileExtension ?? ''; 44 - this.resolveModuleName = args.resolveModuleName; 71 + this._header = args.header; 72 + this._preferExportAll = args.preferExportAll ?? false; 73 + this._preferFileExtension = args.preferFileExtension ?? ''; 74 + this._resolveModuleName = args.resolveModuleName; 45 75 } 46 76 47 - render(ctx: RenderContext): string { 77 + render(ctx: RenderContext<TsDsl>): string { 78 + const header = 79 + typeof this._header === 'function' ? this._header(ctx) : this._header; 80 + return TypeScriptRenderer.astToString({ 81 + exports: this.getExports(ctx), 82 + exportsOptions: { 83 + preferExportAll: this._preferExportAll, 84 + }, 85 + header, 86 + imports: this.getImports(ctx), 87 + nodes: ctx.file.nodes, 88 + }); 89 + } 90 + 91 + supports(ctx: RenderContext): boolean { 92 + return ctx.file.language === 'typescript'; 93 + } 94 + 95 + static astToString(args: { 96 + exports?: Exports; 97 + exportsOptions?: ExportsOptions; 98 + header?: Header; 99 + imports?: Imports; 100 + nodes?: ReadonlyArray<TsDsl>; 101 + /** 102 + * Whether to include a trailing newline at the end of the file. 103 + * 104 + * @default true 105 + */ 106 + trailingNewline?: boolean; 107 + }): string { 48 108 let text = ''; 49 - const header = '// This file is auto-generated by @hey-api/openapi-ts'; 50 - text += `${header}\n`; 109 + for (const header of headerToLines(args.header)) { 110 + text += `${header}\n`; 111 + } 51 112 52 113 let imports = ''; 53 - for (const group of this.getImports(ctx)) { 114 + for (const group of args.imports ?? []) { 54 115 if (imports) imports += '\n'; 55 116 for (const imp of group) { 56 - imports += `${nodeToString(this.renderImport(ctx, imp))}\n`; 117 + imports += `${astToString(TypeScriptRenderer.toImportAst(imp))}\n`; 57 118 } 58 119 } 59 120 text = `${text}${text && imports ? '\n' : ''}${imports}`; 60 121 61 122 let nodes = ''; 62 - for (const node of ctx.file.nodes) { 123 + for (const node of args.nodes ?? []) { 63 124 if (nodes) nodes += '\n'; 64 - // @ts-expect-error 65 - nodes += `${nodeToString(node.toAst(ctx.astContext))}\n`; 125 + nodes += `${astToString(node.toAst())}\n`; 66 126 } 67 127 text = `${text}${text && nodes ? '\n' : ''}${nodes}`; 68 128 69 129 let exports = ''; 70 - for (const group of this.getExports(ctx)) { 130 + for (const group of args.exports ?? []) { 71 131 if ((!exports && nodes) || exports) exports += '\n'; 72 132 for (const exp of group) { 73 - exports += `${nodeToString(this.renderExport(ctx, exp))}\n`; 133 + exports += `${astToString(TypeScriptRenderer.toExportAst(exp, args.exportsOptions))}\n`; 74 134 } 75 135 } 76 136 text = `${text}${text && exports ? '\n' : ''}${exports}`; 77 137 138 + if (args.trailingNewline === false && text.endsWith('\n')) { 139 + text = text.slice(0, -1); 140 + } 141 + 78 142 return text; 79 143 } 80 144 81 - supports(ctx: RenderContext): boolean { 82 - return ctx.file.language === 'typescript'; 145 + static toExportAst( 146 + group: ModuleExport, 147 + options?: ExportsOptions, 148 + ): ts.ExportDeclaration { 149 + const specifiers = group.exports.map((exp) => { 150 + const specifier = ts.factory.createExportSpecifier( 151 + exp.isTypeOnly, 152 + exp.sourceName !== exp.exportedName 153 + ? $.id(exp.sourceName).toAst() 154 + : undefined, 155 + $.id(exp.exportedName).toAst(), 156 + ); 157 + return specifier; 158 + }); 159 + const exportClause = group.namespaceExport 160 + ? ts.factory.createNamespaceExport($.id(group.namespaceExport).toAst()) 161 + : (!group.canExportAll || !options?.preferExportAll) && specifiers.length 162 + ? ts.factory.createNamedExports(specifiers) 163 + : undefined; 164 + return ts.factory.createExportDeclaration( 165 + undefined, 166 + group.isTypeOnly, 167 + exportClause, 168 + $.literal(group.modulePath).toAst(), 169 + ); 170 + } 171 + 172 + static toImportAst(group: ModuleImport): ts.ImportDeclaration { 173 + const specifiers = group.imports.map((imp) => { 174 + const specifier = ts.factory.createImportSpecifier( 175 + imp.isTypeOnly, 176 + imp.sourceName !== imp.localName 177 + ? $.id(imp.sourceName).toAst() 178 + : undefined, 179 + $.id(imp.localName).toAst(), 180 + ); 181 + return specifier; 182 + }); 183 + const importClause = ts.factory.createImportClause( 184 + group.isTypeOnly, 185 + group.kind === 'default' 186 + ? $.id(group.localName ?? '').toAst() 187 + : undefined, 188 + group.kind === 'namespace' 189 + ? ts.factory.createNamespaceImport($.id(group.localName ?? '').toAst()) 190 + : specifiers.length > 0 191 + ? ts.factory.createNamedImports(specifiers) 192 + : undefined, 193 + ); 194 + return ts.factory.createImportDeclaration( 195 + undefined, 196 + importClause, 197 + $.literal(group.modulePath).toAst(), 198 + ); 83 199 } 84 200 85 - private getExports( 86 - ctx: RenderContext, 87 - ): ReadonlyArray<ReadonlyArray<ModuleExport>> { 201 + private getExports(ctx: RenderContext): Exports { 88 202 type ModuleEntry = { 89 203 group: ModuleExport; 90 204 sortKey: SortKey; ··· 96 210 const sortKey = moduleSortKey({ 97 211 file: ctx.file, 98 212 fromFile: exp.from, 99 - preferFileExtension: this.preferFileExtension, 213 + preferFileExtension: this._preferFileExtension, 100 214 root: ctx.project.root, 101 215 }); 102 - const modulePath = this.resolveModuleName?.(sortKey[2]) ?? sortKey[2]; 216 + const modulePath = this._resolveModuleName?.(sortKey[2]) ?? sortKey[2]; 103 217 const [groupIndex] = sortKey; 104 218 105 219 if (!groups.has(groupIndex)) groups.set(groupIndex, new Map()); ··· 154 268 return exports; 155 269 } 156 270 157 - private getImports( 158 - ctx: RenderContext, 159 - ): ReadonlyArray<ReadonlyArray<ModuleImport>> { 271 + private getImports(ctx: RenderContext): Imports { 160 272 type ModuleEntry = { 161 273 group: ModuleImport; 162 274 sortKey: SortKey; ··· 168 280 const sortKey = moduleSortKey({ 169 281 file: ctx.file, 170 282 fromFile: imp.from, 171 - preferFileExtension: this.preferFileExtension, 283 + preferFileExtension: this._preferFileExtension, 172 284 root: ctx.project.root, 173 285 }); 174 - const modulePath = this.resolveModuleName?.(sortKey[2]) ?? sortKey[2]; 286 + const modulePath = this._resolveModuleName?.(sortKey[2]) ?? sortKey[2]; 175 287 const [groupIndex] = sortKey; 176 288 177 289 if (!groups.has(groupIndex)) groups.set(groupIndex, new Map()); ··· 182 294 group: { 183 295 imports: [], 184 296 isTypeOnly: false, 297 + kind: imp.kind, 185 298 modulePath, 186 299 }, 187 300 sortKey, ··· 191 304 const entry = moduleMap.get(modulePath)!; 192 305 const group = entry.group; 193 306 194 - if (imp.namespaceImport) { 307 + if (imp.kind !== 'named') { 195 308 group.isTypeOnly = imp.isTypeOnly; 196 - group.namespaceImport = imp.namespaceImport; 309 + group.kind = imp.kind; 310 + group.localName = imp.localName; 197 311 } else { 198 312 group.imports.push(...imp.imports); 199 313 } ··· 213 327 214 328 return entries.map((e) => { 215 329 const group = e.group; 216 - if (group.namespaceImport) { 330 + if (group.kind === 'namespace') { 217 331 group.imports = []; 218 332 } else { 219 333 const isTypeOnly = !group.imports.find((imp) => !imp.isTypeOnly); ··· 232 346 }); 233 347 234 348 return imports; 235 - } 236 - 237 - private renderExport( 238 - ctx: RenderContext, 239 - group: ModuleExport, 240 - ): ts.ExportDeclaration { 241 - const specifiers = group.exports.map((exp) => { 242 - const specifier = ts.factory.createExportSpecifier( 243 - exp.isTypeOnly, 244 - exp.sourceName !== exp.exportedName 245 - ? $.id(exp.sourceName).toAst() 246 - : undefined, 247 - $.id(exp.exportedName).toAst(), 248 - ); 249 - return specifier; 250 - }); 251 - const exportClause = group.namespaceExport 252 - ? ts.factory.createNamespaceExport($.id(group.namespaceExport).toAst()) 253 - : (!group.canExportAll || !this.preferExportAll) && specifiers.length 254 - ? ts.factory.createNamedExports(specifiers) 255 - : undefined; 256 - return ts.factory.createExportDeclaration( 257 - undefined, 258 - group.isTypeOnly, 259 - exportClause, 260 - $.literal(group.modulePath).toAst(ctx.astContext), 261 - ); 262 - } 263 - 264 - private renderImport( 265 - ctx: RenderContext, 266 - group: ModuleImport, 267 - ): ts.ImportDeclaration { 268 - const specifiers = group.imports.map((imp) => { 269 - const specifier = ts.factory.createImportSpecifier( 270 - imp.isTypeOnly, 271 - imp.sourceName !== imp.localName 272 - ? $.id(imp.sourceName).toAst() 273 - : undefined, 274 - $.id(imp.localName).toAst(), 275 - ); 276 - return specifier; 277 - }); 278 - const importClause = ts.factory.createImportClause( 279 - group.isTypeOnly, 280 - undefined, // TODO: default imports 281 - group.namespaceImport 282 - ? ts.factory.createNamespaceImport($.id(group.namespaceImport).toAst()) 283 - : ts.factory.createNamedImports(specifiers), 284 - ); 285 - return ts.factory.createImportDeclaration( 286 - undefined, 287 - importClause, 288 - $.literal(group.modulePath).toAst(ctx.astContext), 289 - ); 290 349 } 291 350 }
+1 -1
packages/openapi-ts/src/ts-dsl/render/utils.ts packages/openapi-ts/src/ts-dsl/utils/render-utils.ts
··· 17 17 ); 18 18 19 19 /** Print a TypeScript node to a string. */ 20 - export function nodeToString(node: ts.Node): string { 20 + export function astToString(node: ts.Node): string { 21 21 const result = printer.printNode(ts.EmitHint.Unspecified, node, blankFile); 22 22 23 23 try {
+3 -3
packages/openapi-ts/src/ts-dsl/stmt/block.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 20 20 super.analyze(ctx); 21 21 } 22 22 23 - override toAst(ctx: AstContext) { 24 - const statements = this.$do(ctx); 23 + override toAst() { 24 + const statements = this.$do(); 25 25 return ts.factory.createBlock( 26 26 statements, 27 27 this.$multiline(statements.length),
+5 -5
packages/openapi-ts/src/ts-dsl/stmt/if.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 47 47 return this; 48 48 } 49 49 50 - override toAst(ctx: AstContext) { 50 + override toAst() { 51 51 if (!this._condition) throw new Error('Missing condition in if'); 52 52 if (!this._do) throw new Error('Missing then block in if'); 53 53 54 54 return ts.factory.createIfStatement( 55 - this.$node(ctx, this._condition), 56 - this.$node(ctx, new BlockTsDsl(...this._do).pretty()), 55 + this.$node(this._condition), 56 + this.$node(new BlockTsDsl(...this._do).pretty()), 57 57 this._else 58 - ? this.$node(ctx, new BlockTsDsl(...this._else).pretty()) 58 + ? this.$node(new BlockTsDsl(...this._else).pretty()) 59 59 : undefined, 60 60 ); 61 61 }
+4 -9
packages/openapi-ts/src/ts-dsl/stmt/return.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName, Ref } from '@hey-api/codegen-core'; 7 2 import { ref } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 ··· 11 6 import { TsDsl } from '../base'; 12 7 import { f } from '../utils/factories'; 13 8 14 - export type ReturnExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 9 + export type ReturnExpr = NodeName | MaybeTsDsl<ts.Expression>; 15 10 export type ReturnCtor = (expr?: ReturnExpr) => ReturnTsDsl; 16 11 17 12 const Mixed = TsDsl<ts.ReturnStatement>; ··· 31 26 ctx.analyze(this._returnExpr); 32 27 } 33 28 34 - override toAst(ctx: AstContext) { 35 - return ts.factory.createReturnStatement(this.$node(ctx, this._returnExpr)); 29 + override toAst() { 30 + return ts.factory.createReturnStatement(this.$node(this._returnExpr)); 36 31 } 37 32 } 38 33
+3 -3
packages/openapi-ts/src/ts-dsl/stmt/stmt.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 20 20 ctx.analyze(this._inner); 21 21 } 22 22 23 - override toAst(ctx: AstContext) { 24 - const node = this.$node(ctx, this._inner); 23 + override toAst() { 24 + const node = this.$node(this._inner); 25 25 return ts.isStatement(node) 26 26 ? node 27 27 : ts.factory.createExpressionStatement(node);
+5 -8
packages/openapi-ts/src/ts-dsl/stmt/throw.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 31 31 return this; 32 32 } 33 33 34 - override toAst(ctx: AstContext) { 35 - const errorNode = this.$node(ctx, this.error); 36 - const messageNode = this.$node(ctx, this.msg ? [this.msg] : []).map( 37 - (expr) => 38 - typeof expr === 'string' 39 - ? this.$node(ctx, new LiteralTsDsl(expr)) 40 - : expr, 34 + override toAst() { 35 + const errorNode = this.$node(this.error); 36 + const messageNode = this.$node(this.msg ? [this.msg] : []).map((expr) => 37 + typeof expr === 'string' ? this.$node(new LiteralTsDsl(expr)) : expr, 41 38 ); 42 39 if (this.useNew) { 43 40 return ts.factory.createThrowStatement(
+8 -14
packages/openapi-ts/src/ts-dsl/stmt/try.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Symbol, 5 - } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 6 2 import ts from 'typescript'; 7 3 8 4 import { TsDsl } from '../base'; ··· 11 7 12 8 const Mixed = TsDsl<ts.TryStatement>; 13 9 14 - type CatchParam = Symbol | string; 15 - 16 10 export class TryTsDsl extends Mixed { 17 11 readonly '~dsl' = 'TryTsDsl'; 18 12 19 13 protected _catch?: Array<DoExpr>; 20 - protected _catchArg?: CatchParam; 14 + protected _catchArg?: NodeName; 21 15 protected _finally?: Array<DoExpr>; 22 16 protected _try?: Array<DoExpr>; 23 17 ··· 65 59 return this; 66 60 } 67 61 68 - catchArg(arg: CatchParam): this { 62 + catchArg(arg: NodeName): this { 69 63 this._catchArg = arg; 70 64 return this; 71 65 } ··· 80 74 return this; 81 75 } 82 76 83 - override toAst(ctx: AstContext) { 77 + override toAst() { 84 78 if (!this._try?.length) throw new Error('Missing try block'); 85 79 86 80 const catchParam = this._catchArg 87 - ? (this.$node(ctx, this._catchArg) as ts.BindingName) 81 + ? (this.$node(this._catchArg) as ts.BindingName) 88 82 : undefined; 89 83 90 84 return ts.factory.createTryStatement( 91 - this.$node(ctx, new BlockTsDsl(...this._try).pretty()), 85 + this.$node(new BlockTsDsl(...this._try).pretty()), 92 86 ts.factory.createCatchClause( 93 87 catchParam 94 88 ? ts.factory.createVariableDeclaration(catchParam) 95 89 : undefined, 96 - this.$node(ctx, new BlockTsDsl(...(this._catch ?? [])).pretty()), 90 + this.$node(new BlockTsDsl(...(this._catch ?? [])).pretty()), 97 91 ), 98 92 this._finally 99 - ? this.$node(ctx, new BlockTsDsl(...this._finally).pretty()) 93 + ? this.$node(new BlockTsDsl(...this._finally).pretty()) 100 94 : undefined, 101 95 ); 102 96 }
+10 -19
packages/openapi-ts/src/ts-dsl/stmt/var.ts
··· 1 - import type { 2 - AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 6 - } from '@hey-api/codegen-core'; 7 - import { isSymbol, ref } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeName } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 8 3 import ts from 'typescript'; 9 4 10 5 import { TsDsl, TypeTsDsl } from '../base'; ··· 16 11 import { TypeExprTsDsl } from '../type/expr'; 17 12 import { safeRuntimeName } from '../utils/name'; 18 13 19 - export type VarName = Symbol | string; 20 - 21 14 const Mixed = DefaultMixin( 22 15 DocMixin( 23 16 ExportMixin( ··· 28 21 29 22 export class VarTsDsl extends Mixed { 30 23 readonly '~dsl' = 'VarTsDsl'; 24 + override readonly nameSanitizer = safeRuntimeName; 31 25 32 26 protected kind: ts.NodeFlags = ts.NodeFlags.None; 33 - protected name?: Ref<VarName>; 34 27 protected _type?: TypeTsDsl; 35 28 36 - constructor(name?: VarName) { 29 + constructor(name?: NodeName) { 37 30 super(); 38 - if (name) this.name = ref(name); 31 + if (name) this.name.set(name); 39 32 if (isSymbol(name)) { 40 33 name.setKind('var'); 41 - name.setNameSanitizer(safeRuntimeName); 42 - name.setNode(this); 43 34 } 44 35 } 45 36 ··· 70 61 return this; 71 62 } 72 63 73 - override toAst(ctx: AstContext) { 74 - const name = this.$pattern(ctx) ?? this.$node(ctx, this.name); 64 + override toAst() { 65 + const name = this.$pattern() ?? this.$node(this.name); 75 66 if (!name) 76 67 throw new Error('Var must have either a name or a destructuring pattern'); 77 68 const node = ts.factory.createVariableStatement( ··· 81 72 ts.factory.createVariableDeclaration( 82 73 name as ts.BindingName, 83 74 undefined, 84 - this.$type(ctx, this._type), 85 - this.$value(ctx), 75 + this.$type(this._type), 76 + this.$value(), 86 77 ), 87 78 ], 88 79 this.kind, 89 80 ), 90 81 ); 91 - return this.$docs(ctx, this.$hint(ctx, node)); 82 + return this.$docs(this.$hint(node)); 92 83 } 93 84 }
+13 -16
packages/openapi-ts/src/ts-dsl/type/alias.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 4 - Ref, 5 - Symbol, 3 + NodeName, 4 + NodeScope, 6 5 } from '@hey-api/codegen-core'; 7 - import { fromRef, isSymbol, ref } from '@hey-api/codegen-core'; 6 + import { isSymbol } from '@hey-api/codegen-core'; 8 7 import ts from 'typescript'; 9 8 10 9 import type { MaybeTsDsl } from '../base'; ··· 14 13 import { TypeParamsMixin } from '../mixins/type-params'; 15 14 import { safeTypeName } from '../utils/name'; 16 15 17 - type Name = Symbol | string; 18 16 type Value = MaybeTsDsl<ts.TypeNode>; 19 17 20 18 const Mixed = DocMixin( ··· 23 21 24 22 export class TypeAliasTsDsl extends Mixed { 25 23 readonly '~dsl' = 'TypeAliasTsDsl'; 24 + override readonly nameSanitizer = safeTypeName; 25 + override scope: NodeScope = 'type'; 26 26 27 - protected name: Ref<Name>; 28 27 protected value?: Value; 29 28 30 - constructor(name: Name, fn?: (t: TypeAliasTsDsl) => void) { 29 + constructor(name: NodeName, fn?: (t: TypeAliasTsDsl) => void) { 31 30 super(); 32 - this.name = ref(name); 31 + this.name.set(name); 33 32 if (isSymbol(name)) { 34 33 name.setKind('type'); 35 - name.setNameSanitizer(safeTypeName); 36 - name.setNode(this); 37 34 } 38 35 fn?.(this); 39 36 } ··· 50 47 return this; 51 48 } 52 49 53 - override toAst(ctx: AstContext) { 50 + override toAst() { 54 51 if (!this.value) 55 52 throw new Error( 56 - `Type alias '${fromRef(this.name)}' is missing a type definition`, 53 + `Type alias '${this.name.toString()}' is missing a type definition`, 57 54 ); 58 55 const node = ts.factory.createTypeAliasDeclaration( 59 56 this.modifiers, 60 - this.$node(ctx, this.name) as ts.Identifier, 61 - this.$generics(ctx), 62 - this.$type(ctx, this.value), 57 + this.$node(this.name) as ts.Identifier, 58 + this.$generics(), 59 + this.$type(this.value), 63 60 ); 64 - return this.$docs(ctx, node); 61 + return this.$docs(node); 65 62 } 66 63 }
+6 -5
packages/openapi-ts/src/ts-dsl/type/and.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 3 + NodeName, 4 + NodeScope, 4 5 Ref, 5 - Symbol, 6 6 } from '@hey-api/codegen-core'; 7 7 import { ref } from '@hey-api/codegen-core'; 8 8 import ts from 'typescript'; ··· 10 10 import type { TypeTsDsl } from '../base'; 11 11 import { TsDsl } from '../base'; 12 12 13 - type Type = Symbol | string | ts.TypeNode | TypeTsDsl; 13 + type Type = NodeName | ts.TypeNode | TypeTsDsl; 14 14 15 15 const Mixed = TsDsl<ts.IntersectionTypeNode>; 16 16 17 17 export class TypeAndTsDsl extends Mixed { 18 18 readonly '~dsl' = 'TypeAndTsDsl'; 19 + override scope: NodeScope = 'type'; 19 20 20 21 protected _types: Array<Ref<Type>> = []; 21 22 ··· 36 37 return this; 37 38 } 38 39 39 - override toAst(ctx: AstContext) { 40 + override toAst() { 40 41 const flat: Array<ts.TypeNode> = []; 41 42 42 43 for (const node of this._types) { 43 - const type = this.$type(ctx, node); 44 + const type = this.$type(node); 44 45 if (ts.isIntersectionTypeNode(type)) { 45 46 flat.push(...type.types); 46 47 } else {
+8 -7
packages/openapi-ts/src/ts-dsl/type/attr.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 3 + NodeName, 4 + NodeScope, 4 5 Ref, 5 - Symbol, 6 6 } from '@hey-api/codegen-core'; 7 7 import { isRef, ref } from '@hey-api/codegen-core'; 8 8 import ts from 'typescript'; ··· 11 11 import { TsDsl } from '../base'; 12 12 import { TypeExprMixin } from '../mixins/type-expr'; 13 13 14 - type Base = Symbol | string | MaybeTsDsl<ts.EntityName>; 15 - type Right = Symbol | string | ts.Identifier; 14 + type Base = NodeName | MaybeTsDsl<ts.EntityName>; 15 + type Right = NodeName | ts.Identifier; 16 16 17 17 const Mixed = TypeExprMixin(TsDsl<ts.QualifiedName>); 18 18 19 19 export class TypeAttrTsDsl extends Mixed { 20 20 readonly '~dsl' = 'TypeAttrTsDsl'; 21 + override scope: NodeScope = 'type'; 21 22 22 23 protected _base?: Ref<Base>; 23 24 protected _right!: Ref<Right>; ··· 55 56 return this; 56 57 } 57 58 58 - override toAst(ctx: AstContext) { 59 + override toAst() { 59 60 if (!this._base) { 60 61 throw new Error('TypeAttrTsDsl: missing base for qualified name'); 61 62 } 62 - const left = this.$node(ctx, this._base); 63 + const left = this.$node(this._base); 63 64 if (!ts.isEntityName(left)) { 64 65 throw new Error('TypeAttrTsDsl: base must be an EntityName'); 65 66 } 66 67 return ts.factory.createQualifiedName( 67 68 left, 68 - this.$node(ctx, this._right) as ts.Identifier, 69 + this.$node(this._right) as ts.Identifier, 69 70 ); 70 71 } 71 72 }
+11 -11
packages/openapi-ts/src/ts-dsl/type/expr.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 3 + NodeName, 4 + NodeScope, 4 5 Ref, 5 - Symbol, 6 6 } from '@hey-api/codegen-core'; 7 7 import { isNode, ref } from '@hey-api/codegen-core'; 8 8 import ts from 'typescript'; ··· 13 13 import { f } from '../utils/factories'; 14 14 import { TypeAttrTsDsl } from './attr'; 15 15 16 - export type TypeExprName = Symbol | string; 17 - export type TypeExprExpr = TypeExprName | TypeAttrTsDsl; 16 + export type TypeExprExpr = NodeName | TypeAttrTsDsl; 18 17 export type TypeExprFn = (t: TypeExprTsDsl) => void; 19 18 export type TypeExprCtor = ( 20 - nameOrFn?: TypeExprName | TypeExprFn, 19 + nameOrFn?: NodeName | TypeExprFn, 21 20 fn?: TypeExprFn, 22 21 ) => TypeExprTsDsl; 23 22 ··· 25 24 26 25 export class TypeExprTsDsl extends Mixed { 27 26 readonly '~dsl' = 'TypeExprTsDsl'; 27 + override scope: NodeScope = 'type'; 28 28 29 29 protected _exprInput?: Ref<TypeExprExpr>; 30 30 31 31 constructor(); 32 32 constructor(fn: TypeExprFn); 33 - constructor(name: TypeExprName); 34 - constructor(name: TypeExprName, fn?: TypeExprFn); 35 - constructor(name?: TypeExprName | TypeExprFn, fn?: TypeExprFn) { 33 + constructor(name: NodeName); 34 + constructor(name: NodeName, fn?: TypeExprFn); 35 + constructor(name?: NodeName | TypeExprFn, fn?: TypeExprFn) { 36 36 super(); 37 37 if (typeof name === 'function') { 38 38 name(this); ··· 55 55 return this; 56 56 } 57 57 58 - override toAst(ctx: AstContext) { 58 + override toAst() { 59 59 if (!this._exprInput) throw new Error('TypeExpr must have an expression'); 60 60 return ts.factory.createTypeReferenceNode( 61 - this.$type(ctx, this._exprInput) as ts.EntityName, 62 - this.$generics(ctx), 61 + this.$type(this._exprInput) as ts.EntityName, 62 + this.$generics(), 63 63 ); 64 64 } 65 65 }
+7 -6
packages/openapi-ts/src/ts-dsl/type/func.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 13 13 14 14 export class TypeFuncTsDsl extends Mixed { 15 15 readonly '~dsl' = 'TypeFuncTsDsl'; 16 + override scope: NodeScope = 'type'; 16 17 17 18 override analyze(ctx: AnalysisContext): void { 18 19 super.analyze(ctx); 19 20 } 20 21 21 - override toAst(ctx: AstContext) { 22 - const returns = this.$returns(ctx); 22 + override toAst() { 23 + const returns = this.$returns(); 23 24 if (returns === undefined) { 24 25 throw new Error('Missing return type in function type DSL'); 25 26 } 26 27 const node = ts.factory.createFunctionTypeNode( 27 - this.$generics(ctx), 28 - this.$params(ctx), 28 + this.$generics(), 29 + this.$params(), 29 30 returns, 30 31 ); 31 - return this.$docs(ctx, node); 32 + return this.$docs(node); 32 33 } 33 34 }
+15 -13
packages/openapi-ts/src/ts-dsl/type/idx-sig.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { 2 + AnalysisContext, 3 + NodeName, 4 + NodeScope, 5 + } from '@hey-api/codegen-core'; 2 6 import ts from 'typescript'; 3 7 4 8 import type { MaybeTsDsl } from '../base'; ··· 6 10 import { DocMixin } from '../mixins/doc'; 7 11 import { ReadonlyMixin } from '../mixins/modifiers'; 8 12 9 - export type TypeIdxSigName = string; 10 13 export type TypeIdxSigType = string | MaybeTsDsl<ts.TypeNode>; 11 14 12 15 const Mixed = DocMixin(ReadonlyMixin(TsDsl<ts.IndexSignatureDeclaration>)); 13 16 14 17 export class TypeIdxSigTsDsl extends Mixed { 15 18 readonly '~dsl' = 'TypeIdxSigTsDsl'; 19 + override scope: NodeScope = 'type'; 16 20 17 21 protected _key?: TypeIdxSigType; 18 - protected _name: TypeIdxSigName; 19 22 protected _type?: TypeIdxSigType; 20 23 21 - constructor(name: TypeIdxSigName, fn?: (i: TypeIdxSigTsDsl) => void) { 24 + constructor(name: NodeName, fn?: (i: TypeIdxSigTsDsl) => void) { 22 25 super(); 23 - this._name = name; 26 + this.name.set(name); 24 27 fn?.(this); 25 28 } 26 29 ··· 47 50 return this; 48 51 } 49 52 50 - override toAst(ctx: AstContext) { 53 + override toAst() { 51 54 this.$validate(); 52 55 const node = ts.factory.createIndexSignature( 53 56 this.modifiers, ··· 55 58 ts.factory.createParameterDeclaration( 56 59 undefined, 57 60 undefined, 58 - this._name, 61 + this.$node(this.name) as ts.BindingName, 59 62 undefined, 60 - this.$type(ctx, this._key), 63 + this.$type(this._key), 61 64 ), 62 65 ], 63 - this.$type(ctx, this._type), 66 + this.$type(this._type), 64 67 ); 65 - return this.$docs(ctx, node); 68 + return this.$docs(node); 66 69 } 67 70 68 71 $validate(): asserts this is this & { 69 72 _key: TypeIdxSigType; 70 - _name: TypeIdxSigName; 71 73 _type: TypeIdxSigType; 72 74 } { 73 75 const missing = this.missingRequiredCalls(); 74 76 if (missing.length === 0) return; 77 + const name = this.name.toString(); 75 78 throw new Error( 76 - `Index signature${this._name ? ` "${this._name}"` : ''} missing ${missing.join(' and ')}`, 79 + `Index signature${name ? ` "${name}"` : ''} missing ${missing.join(' and ')}`, 77 80 ); 78 81 } 79 82 80 83 private missingRequiredCalls(): ReadonlyArray<string> { 81 84 const missing: Array<string> = []; 82 85 if (!this._key) missing.push('.key()'); 83 - if (!this._name) missing.push('.name()'); 84 86 if (!this._type) missing.push('.\u200Btype()'); 85 87 return missing; 86 88 }
+5 -4
packages/openapi-ts/src/ts-dsl/type/idx.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 14 14 15 15 export class TypeIdxTsDsl extends Mixed { 16 16 readonly '~dsl' = 'TypeIdxTsDsl'; 17 + override scope: NodeScope = 'type'; 17 18 18 19 protected _base!: Base; 19 20 protected _index!: Index; ··· 40 41 return this; 41 42 } 42 43 43 - override toAst(ctx: AstContext) { 44 + override toAst() { 44 45 return ts.factory.createIndexedAccessTypeNode( 45 - this.$type(ctx, this._base), 46 - this.$type(ctx, this._index), 46 + this.$type(this._base), 47 + this.$type(this._index), 47 48 ); 48 49 } 49 50 }
+4 -3
packages/openapi-ts/src/ts-dsl/type/literal.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 8 8 9 9 export class TypeLiteralTsDsl extends Mixed { 10 10 readonly '~dsl' = 'TypeLiteralTsDsl'; 11 + override scope: NodeScope = 'type'; 11 12 12 13 protected value: string | number | boolean | null; 13 14 ··· 20 21 super.analyze(ctx); 21 22 } 22 23 23 - override toAst(ctx: AstContext) { 24 + override toAst() { 24 25 return ts.factory.createLiteralTypeNode( 25 - this.$node(ctx, new LiteralTsDsl(this.value)), 26 + this.$node(new LiteralTsDsl(this.value)), 26 27 ); 27 28 } 28 29 }
+16 -19
packages/openapi-ts/src/ts-dsl/type/mapped.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { 2 + AnalysisContext, 3 + NodeName, 4 + NodeScope, 5 + } from '@hey-api/codegen-core'; 2 6 import ts from 'typescript'; 3 7 4 8 import type { MaybeTsDsl } from '../base'; ··· 9 13 10 14 export class TypeMappedTsDsl extends Mixed { 11 15 readonly '~dsl' = 'TypeMappedTsDsl'; 16 + override scope: NodeScope = 'type'; 12 17 13 18 protected questionToken?: TokenTsDsl< 14 19 | ts.SyntaxKind.QuestionToken ··· 21 26 | ts.SyntaxKind.PlusToken 22 27 >; 23 28 protected _key?: string | MaybeTsDsl<ts.TypeNode>; 24 - protected _name?: string; 25 29 protected _type?: string | MaybeTsDsl<ts.TypeNode>; 26 30 27 - constructor(name?: string) { 31 + constructor(name?: NodeName) { 28 32 super(); 29 - this.name(name); 33 + if (name) this.name.set(name); 30 34 } 31 35 32 36 override analyze(ctx: AnalysisContext): void { ··· 51 55 /** Removes `readonly` from the mapped members (`[K in X]-readonly`). */ 52 56 mutable(): this { 53 57 this.readonlyToken = new TokenTsDsl().minus(); 54 - return this; 55 - } 56 - 57 - /** Sets the parameter name: `{ [Name in keyof T]: U }` */ 58 - name(name?: string): this { 59 - this._name = name; 60 58 return this; 61 59 } 62 60 ··· 84 82 return this; 85 83 } 86 84 87 - override toAst(ctx: AstContext) { 85 + override toAst() { 88 86 this.$validate(); 89 87 return ts.factory.createMappedTypeNode( 90 - this.$node(ctx, this.readonlyToken), 88 + this.$node(this.readonlyToken), 91 89 ts.factory.createTypeParameterDeclaration( 92 90 undefined, 93 - this._name, 94 - this.$type(ctx, this._key), 91 + this.$node(this.name) as ts.Identifier, 92 + this.$type(this._key), 95 93 undefined, 96 94 ), 97 95 undefined, 98 - this.$node(ctx, this.questionToken), 99 - this.$type(ctx, this._type), 96 + this.$node(this.questionToken), 97 + this.$type(this._type), 100 98 undefined, 101 99 ); 102 100 } 103 101 104 102 $validate(): asserts this is this & { 105 103 _key: string | MaybeTsDsl<ts.TypeNode>; 106 - _name: string; 107 104 _type: string | MaybeTsDsl<ts.TypeNode>; 108 105 } { 109 106 const missing = this.missingRequiredCalls(); 110 107 if (missing.length === 0) return; 108 + const name = this.name.toString(); 111 109 throw new Error( 112 - `Mapped type${this._name ? ` "${this._name}"` : ''} missing ${missing.join(' and ')}`, 110 + `Mapped type${name ? ` "${name}"` : ''} missing ${missing.join(' and ')}`, 113 111 ); 114 112 } 115 113 116 114 private missingRequiredCalls(): ReadonlyArray<string> { 117 115 const missing: Array<string> = []; 118 116 if (!this._key) missing.push('.key()'); 119 - if (!this._name) missing.push('.name()'); 120 117 if (!this._type) missing.push('.\u200Btype()'); 121 118 return missing; 122 119 }
+4 -3
packages/openapi-ts/src/ts-dsl/type/object.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; ··· 9 9 10 10 export class TypeObjectTsDsl extends Mixed { 11 11 readonly '~dsl' = 'TypeObjectTsDsl'; 12 + override scope: NodeScope = 'type'; 12 13 13 14 protected props: Array<TypePropTsDsl | TypeIdxSigTsDsl> = []; 14 15 ··· 43 44 return this; 44 45 } 45 46 46 - override toAst(ctx: AstContext) { 47 - return ts.factory.createTypeLiteralNode(this.$node(ctx, this.props)); 47 + override toAst() { 48 + return ts.factory.createTypeLiteralNode(this.$node(this.props)); 48 49 } 49 50 }
+4 -6
packages/openapi-ts/src/ts-dsl/type/operator.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 28 28 */ 29 29 export class TypeOperatorTsDsl extends Mixed { 30 30 readonly '~dsl' = 'TypeOperatorTsDsl'; 31 + override scope: NodeScope = 'type'; 31 32 32 33 protected _op?: Op; 33 34 protected _type?: Type; ··· 70 71 return this; 71 72 } 72 73 73 - override toAst(ctx: AstContext) { 74 + override toAst() { 74 75 this.$validate(); 75 - return ts.factory.createTypeOperatorNode( 76 - this._op, 77 - this.$type(ctx, this._type), 78 - ); 76 + return ts.factory.createTypeOperatorNode(this._op, this.$type(this._type)); 79 77 } 80 78 81 79 /** Throws if required fields are not set. */
+6 -5
packages/openapi-ts/src/ts-dsl/type/or.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 3 + NodeName, 4 + NodeScope, 4 5 Ref, 5 - Symbol, 6 6 } from '@hey-api/codegen-core'; 7 7 import { ref } from '@hey-api/codegen-core'; 8 8 import ts from 'typescript'; ··· 10 10 import type { TypeTsDsl } from '../base'; 11 11 import { TsDsl } from '../base'; 12 12 13 - type Type = Symbol | string | ts.TypeNode | TypeTsDsl; 13 + type Type = NodeName | ts.TypeNode | TypeTsDsl; 14 14 15 15 const Mixed = TsDsl<ts.UnionTypeNode>; 16 16 17 17 export class TypeOrTsDsl extends Mixed { 18 18 readonly '~dsl' = 'TypeOrTsDsl'; 19 + override scope: NodeScope = 'type'; 19 20 20 21 protected _types: Array<Ref<Type>> = []; 21 22 ··· 36 37 return this; 37 38 } 38 39 39 - override toAst(ctx: AstContext) { 40 + override toAst() { 40 41 const flat: Array<ts.TypeNode> = []; 41 42 42 43 for (const node of this._types) { 43 - const type = this.$type(ctx, node); 44 + const type = this.$type(node); 44 45 if (ts.isUnionTypeNode(type)) { 45 46 flat.push(...type.types); 46 47 } else {
+12 -12
packages/openapi-ts/src/ts-dsl/type/param.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 3 + NodeName, 4 + NodeScope, 4 5 Ref, 5 - Symbol, 6 6 } from '@hey-api/codegen-core'; 7 7 import { ref } from '@hey-api/codegen-core'; 8 8 import ts from 'typescript'; ··· 10 10 import type { MaybeTsDsl, TypeTsDsl } from '../base'; 11 11 import { TsDsl } from '../base'; 12 12 13 - export type TypeParamName = Symbol | string; 14 - export type TypeParamExpr = Symbol | string | boolean | MaybeTsDsl<TypeTsDsl>; 13 + export type TypeParamExpr = NodeName | boolean | MaybeTsDsl<TypeTsDsl>; 15 14 16 15 const Mixed = TsDsl<ts.TypeParameterDeclaration>; 17 16 18 17 export class TypeParamTsDsl extends Mixed { 19 18 readonly '~dsl' = 'TypeParamTsDsl'; 19 + override scope: NodeScope = 'type'; 20 20 21 21 protected constraint?: Ref<TypeParamExpr>; 22 22 protected defaultValue?: Ref<TypeParamExpr>; 23 - protected name?: Ref<TypeParamName>; 24 23 25 - constructor(name?: TypeParamName, fn?: (name: TypeParamTsDsl) => void) { 24 + constructor(name?: NodeName, fn?: (name: TypeParamTsDsl) => void) { 26 25 super(); 27 - if (name) this.name = ref(name); 26 + if (name) this.name.set(name); 28 27 fn?.(this); 29 28 } 30 29 ··· 45 44 return this; 46 45 } 47 46 48 - override toAst(ctx: AstContext) { 49 - if (!this.name) throw new Error('Missing type name'); 47 + override toAst() { 48 + const name = this.name.toString(); 49 + if (!name) throw new Error('Missing type name'); 50 50 return ts.factory.createTypeParameterDeclaration( 51 51 undefined, 52 - this.$node(ctx, this.name) as ts.Identifier, 53 - this.$type(ctx, this.constraint), 54 - this.$type(ctx, this.defaultValue), 52 + this.$node(this.name) as ts.Identifier, 53 + this.$type(this.constraint), 54 + this.$type(this.defaultValue), 55 55 ); 56 56 } 57 57 }
+14 -14
packages/openapi-ts/src/ts-dsl/type/prop.ts
··· 1 1 import type { 2 2 AnalysisContext, 3 - AstContext, 3 + NodeName, 4 + NodeScope, 4 5 Ref, 5 - Symbol, 6 6 } from '@hey-api/codegen-core'; 7 7 import { ref } from '@hey-api/codegen-core'; 8 8 import ts from 'typescript'; ··· 15 15 import { TokenTsDsl } from '../token'; 16 16 import { safePropName } from '../utils/name'; 17 17 18 - export type TypePropName = string; 19 - export type TypePropType = Symbol | string | MaybeTsDsl<ts.TypeNode>; 18 + export type TypePropType = NodeName | MaybeTsDsl<ts.TypeNode>; 20 19 21 20 const Mixed = DocMixin(OptionalMixin(ReadonlyMixin(TsDsl<ts.TypeElement>))); 22 21 23 22 export class TypePropTsDsl extends Mixed { 24 23 readonly '~dsl' = 'TypePropTsDsl'; 24 + override scope: NodeScope = 'type'; 25 25 26 - protected name: TypePropName; 27 26 protected _type?: Ref<TypePropType>; 28 27 29 - constructor(name: TypePropName, fn: (p: TypePropTsDsl) => void) { 28 + constructor(name: NodeName, fn: (p: TypePropTsDsl) => void) { 30 29 super(); 31 - this.name = name; 30 + this.name.set(name); 32 31 fn(this); 33 32 } 34 33 ··· 43 42 return this; 44 43 } 45 44 46 - override toAst(ctx: AstContext) { 47 - if (!this._type) { 48 - throw new Error(`Type not specified for property '${this.name}'`); 45 + override toAst() { 46 + const name = this.name.toString(); 47 + if (!this._type || !name) { 48 + throw new Error(`Type not specified for property '${name}'`); 49 49 } 50 50 const node = ts.factory.createPropertySignature( 51 51 this.modifiers, 52 - this.$node(ctx, safePropName(this.name)), 53 - this._optional ? this.$node(ctx, new TokenTsDsl().optional()) : undefined, 54 - this.$type(ctx, this._type), 52 + this.$node(safePropName(name)), 53 + this._optional ? this.$node(new TokenTsDsl().optional()) : undefined, 54 + this.$type(this._type), 55 55 ); 56 - return this.$docs(ctx, node); 56 + return this.$docs(node); 57 57 } 58 58 }
+4 -3
packages/openapi-ts/src/ts-dsl/type/query.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl, TypeTsDsl } from '../base'; ··· 13 13 14 14 export class TypeQueryTsDsl extends Mixed { 15 15 readonly '~dsl' = 'TypeQueryTsDsl'; 16 + override scope: NodeScope = 'type'; 16 17 17 18 protected _expr: TypeQueryExpr; 18 19 ··· 26 27 ctx.analyze(this._expr); 27 28 } 28 29 29 - override toAst(ctx: AstContext) { 30 - const expr = this.$node(ctx, this._expr); 30 + override toAst() { 31 + const expr = this.$node(this._expr); 31 32 return ts.factory.createTypeQueryNode(expr as unknown as ts.EntityName); 32 33 } 33 34 }
+4 -3
packages/openapi-ts/src/ts-dsl/type/template.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 8 8 9 9 export class TypeTemplateTsDsl extends Mixed { 10 10 readonly '~dsl' = 'TypeTemplateTsDsl'; 11 + override scope: NodeScope = 'type'; 11 12 12 13 protected parts: Array<string | MaybeTsDsl<ts.TypeNode>> = []; 13 14 ··· 29 30 return this; 30 31 } 31 32 32 - override toAst(ctx: AstContext) { 33 - const parts = this.$node(ctx, this.parts); 33 + override toAst() { 34 + const parts = this.$node(this.parts); 34 35 35 36 const normalized: Array<string | ts.TypeNode> = []; 36 37 // merge consecutive string parts
+4 -3
packages/openapi-ts/src/ts-dsl/type/tuple.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, NodeScope } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { TypeTsDsl } from '../base'; ··· 8 8 9 9 export class TypeTupleTsDsl extends Mixed { 10 10 readonly '~dsl' = 'TypeTupleTsDsl'; 11 + override scope: NodeScope = 'type'; 11 12 12 13 protected _elements: Array<string | ts.TypeNode | TypeTsDsl> = []; 13 14 ··· 28 29 return this; 29 30 } 30 31 31 - override toAst(ctx: AstContext) { 32 + override toAst() { 32 33 return ts.factory.createTupleTypeNode( 33 - this._elements.map((t) => this.$type(ctx, t)), 34 + this._elements.map((t) => this.$type(t)), 34 35 ); 35 36 } 36 37 }
+267
packages/openapi-ts/src/ts-dsl/utils/context.ts
··· 1 + import type { BindingKind, NodeScope, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 + import type ts from 'typescript'; 4 + 5 + import { $, TypeScriptRenderer } from '~/ts-dsl'; 6 + 7 + import type { TsDsl } from '../base'; 8 + import type { CallArgs } from '../expr/call'; 9 + 10 + export type NodeChain = ReadonlyArray<TsDsl>; 11 + 12 + export interface AccessOptions { 13 + /** The access context. */ 14 + context?: 'example'; 15 + /** Enable debug mode. */ 16 + debug?: boolean; 17 + /** Transform function for each node in the access chain. */ 18 + transform?: (node: TsDsl, index: number, chain: NodeChain) => TsDsl; 19 + } 20 + 21 + export type AccessResult = ReturnType< 22 + typeof $.expr | typeof $.attr | typeof $.call | typeof $.new 23 + >; 24 + 25 + export interface ExampleOptions { 26 + /** Import kind for the root node. */ 27 + importKind?: BindingKind; 28 + /** Import name for the root node. */ 29 + importName?: string; 30 + /** Setup to run before calling the example. */ 31 + importSetup?: 32 + | TsDsl<ts.Expression> 33 + | ((imp: TsDsl<ts.Expression>) => TsDsl<ts.Expression>); 34 + /** Module to import from. */ 35 + moduleName: string; 36 + /** Example request payload. */ 37 + payload?: CallArgs | CallArgs[number]; 38 + /** Variable name for setup node. */ 39 + setupName?: string; 40 + } 41 + 42 + function accessChainToNode<T = AccessResult>(accessChain: NodeChain): T { 43 + let result!: AccessResult; 44 + accessChain.forEach((node, index) => { 45 + if (index === 0) { 46 + // assume correct node 47 + result = node as typeof result; 48 + } else { 49 + result = result.attr(node.name); 50 + } 51 + }); 52 + return result as T; 53 + } 54 + 55 + function getAccessChainForNode(node: TsDsl): NodeChain { 56 + const structuralChain = [...getStructuralChainForNode(node, new Set())]; 57 + const accessChain = structuralToAccessChain(structuralChain); 58 + if (accessChain.length === 0) { 59 + // I _think_ this should not happen, but it does and this fix works for now. 60 + // I assume this will cause issues with imports in some cases, investigate 61 + // when it actually happens. 62 + return [node.clone()]; 63 + } 64 + return accessChain.map((node) => node.clone()); 65 + } 66 + 67 + function getScope(node: TsDsl): NodeScope { 68 + return node.scope ?? 'value'; 69 + } 70 + 71 + function getStructuralChainForNode( 72 + node: TsDsl, 73 + visited: Set<TsDsl>, 74 + ): NodeChain { 75 + if (visited.has(node)) return []; 76 + visited.add(node); 77 + 78 + if (isStopNode(node)) return []; 79 + 80 + if (node.structuralParents) { 81 + for (const [parent] of node.structuralParents) { 82 + if (getScope(parent) !== getScope(node)) continue; 83 + 84 + const chain = getStructuralChainForNode(parent, visited); 85 + if (chain.length > 0) return [...chain, node]; 86 + } 87 + } 88 + 89 + if (!node.root) return []; 90 + 91 + return [node]; 92 + } 93 + 94 + function isAccessorNode(node: TsDsl): boolean { 95 + return ( 96 + node['~dsl'] === 'FieldTsDsl' || 97 + node['~dsl'] === 'GetterTsDsl' || 98 + node['~dsl'] === 'MethodTsDsl' 99 + ); 100 + } 101 + 102 + function isStopNode(node: TsDsl): boolean { 103 + return node['~dsl'] === 'FuncTsDsl' || node['~dsl'] === 'TemplateTsDsl'; 104 + } 105 + 106 + /** 107 + * Fold a structural chain to an access chain by removing 108 + * non-accessor nodes. 109 + */ 110 + function structuralToAccessChain(structuralChain: NodeChain): NodeChain { 111 + const accessChain: Array<TsDsl> = []; 112 + structuralChain.forEach((node, index) => { 113 + // assume first node is always included 114 + if (index === 0) { 115 + accessChain.push(node); 116 + } else if (isAccessorNode(node)) { 117 + accessChain.push(node); 118 + } 119 + }); 120 + return accessChain; 121 + } 122 + 123 + function transformAccessChain( 124 + accessChain: NodeChain, 125 + options: AccessOptions = {}, 126 + ): NodeChain { 127 + return accessChain.map((node, index) => { 128 + const transformedNode = options.transform?.(node, index, accessChain); 129 + if (transformedNode) return transformedNode; 130 + const accessNode = node.toAccessNode?.(node, options, { 131 + chain: accessChain, 132 + index, 133 + isLeaf: index === accessChain.length - 1, 134 + isRoot: index === 0, 135 + length: accessChain.length, 136 + }); 137 + if (accessNode) return accessNode; 138 + if (index === 0) { 139 + if (node['~dsl'] === 'ClassTsDsl') { 140 + const nextNode = accessChain[index + 1]; 141 + if (nextNode && isAccessorNode(nextNode)) { 142 + if ((nextNode as ReturnType<typeof $.field>).hasModifier('static')) { 143 + return $(node.name); 144 + } 145 + } 146 + return $.new(node.name).args(); 147 + } 148 + return $(node.name); 149 + } 150 + return node; 151 + }); 152 + } 153 + 154 + export class TsDslContext { 155 + /** 156 + * Build an expression for accessing the node. 157 + * 158 + * @param node - The node or symbol to build access for 159 + * @param options - Access options 160 + * @returns Expression for accessing the node 161 + * 162 + * @example 163 + * ```ts 164 + * ctx.access(node); // → Expression for accessing the node 165 + * ``` 166 + */ 167 + access<T = AccessResult>( 168 + node: TsDsl | Symbol<TsDsl>, 169 + options?: AccessOptions, 170 + ): T { 171 + const n = isSymbol(node) ? node.node : node; 172 + if (!n) { 173 + throw new Error(`Symbol ${node.name} is not resolved to a node.`); 174 + } 175 + const accessChain = getAccessChainForNode(n); 176 + const finalChain = transformAccessChain(accessChain, options); 177 + return accessChainToNode<T>(finalChain); 178 + } 179 + 180 + /** 181 + * Build an example. 182 + * 183 + * @param node - The node to generate an example for 184 + * @param options - Example options 185 + * @returns Full example string 186 + * 187 + * @example 188 + * ```ts 189 + * ctx.example(node, { moduleName: 'my-sdk' }); // → Full example string 190 + * ``` 191 + */ 192 + example( 193 + node: TsDsl, 194 + options: ExampleOptions | undefined, 195 + astOptions?: Parameters<typeof TypeScriptRenderer.astToString>[0], 196 + ): string { 197 + if (astOptions) { 198 + return TypeScriptRenderer.astToString(astOptions); 199 + } 200 + 201 + if (!options) { 202 + throw new Error('Example options are required.'); 203 + } 204 + 205 + const accessChain = getAccessChainForNode(node); 206 + if (options.importName) { 207 + accessChain[0]!.name.set(options.importName); 208 + } 209 + const importNode = $(accessChain[0]!.name.toString()); // must store name before transform 210 + const finalChain = transformAccessChain(accessChain, { 211 + context: 'example', 212 + }); 213 + 214 + const setupNode = options.importSetup 215 + ? typeof options.importSetup === 'function' 216 + ? options.importSetup(importNode) 217 + : options.importSetup 218 + : (finalChain[0]! as TsDsl<ts.Expression>); 219 + const setupName = options.setupName; 220 + const payload = 221 + options.payload instanceof Array 222 + ? options.payload 223 + : options.payload 224 + ? [options.payload] 225 + : []; 226 + 227 + let nodes: Array<TsDsl> = []; 228 + if (setupName) { 229 + nodes = [ 230 + $.const(setupName).assign(setupNode), 231 + accessChainToNode([$(setupName), ...finalChain.slice(1)]).call( 232 + ...payload, 233 + ), 234 + ]; 235 + } else { 236 + nodes = [ 237 + accessChainToNode([setupNode, ...finalChain.slice(1)]).call(...payload), 238 + ]; 239 + } 240 + 241 + const localName = importNode.name.toString(); 242 + return TypeScriptRenderer.astToString({ 243 + imports: [ 244 + [ 245 + { 246 + imports: 247 + !options.importKind || options.importKind === 'named' 248 + ? [ 249 + { 250 + isTypeOnly: false, 251 + localName, 252 + sourceName: localName, 253 + }, 254 + ] 255 + : [], 256 + isTypeOnly: false, 257 + kind: options.importKind ?? 'named', 258 + localName: options.importKind !== 'named' ? localName : undefined, 259 + modulePath: options.moduleName, 260 + }, 261 + ], 262 + ], 263 + nodes, 264 + trailingNewline: false, 265 + }); 266 + } 267 + }
+4
packages/openapi-ts/src/ts-dsl/utils/factories.ts
··· 2 2 import type { AttrCtor } from '../expr/attr'; 3 3 import type { AwaitCtor } from '../expr/await'; 4 4 import type { CallCtor } from '../expr/call'; 5 + import type { NewCtor } from '../expr/new'; 5 6 import type { TypeOfExprCtor } from '../expr/typeof'; 6 7 import type { ReturnCtor } from '../stmt/return'; 7 8 import type { TypeExprCtor } from '../type/expr'; ··· 44 45 45 46 /** Factory for creating function or method call expressions (e.g. `fn(arg)`). */ 46 47 call: createFactory<CallCtor>('call'), 48 + 49 + /** Factory for creating new expressions (e.g. `new ClassName()`). */ 50 + new: createFactory<NewCtor>('new'), 47 51 48 52 /** Factory for creating return statements. */ 49 53 return: createFactory<ReturnCtor>('return'),
+10 -10
packages/openapi-ts/src/ts-dsl/utils/lazy.ts
··· 1 - import type { AnalysisContext, AstContext } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 4 import { TsDsl } from '../base'; 5 + import { TsDslContext } from './context'; 5 6 6 - export type LazyThunk<T extends ts.Node> = (ctx: AstContext) => TsDsl<T>; 7 + export type LazyThunk<T extends ts.Node> = (ctx: TsDslContext) => TsDsl<T>; 7 8 8 9 export class LazyTsDsl<T extends ts.Node = ts.Node> extends TsDsl<T> { 9 10 readonly '~dsl' = 'LazyTsDsl'; ··· 17 18 18 19 override analyze(ctx: AnalysisContext): void { 19 20 super.analyze(ctx); 20 - const astContext: AstContext = { 21 - getAccess(node) { 22 - return node; 23 - }, 24 - }; 25 - ctx.analyze(this._thunk(astContext)); 21 + ctx.analyze(this.toResult()); 26 22 } 27 23 28 - override toAst(ctx: AstContext) { 29 - return this._thunk(ctx).toAst(ctx); 24 + toResult(): TsDsl<T> { 25 + return this._thunk(new TsDslContext()); 26 + } 27 + 28 + override toAst(): T { 29 + return this.toResult().toAst(); 30 30 } 31 31 }
+26 -8
packages/openapi-ts/src/ts-dsl/utils/name.ts
··· 7 7 import type { ReservedList } from './reserved'; 8 8 import { reserved } from './reserved'; 9 9 10 - export const safeMemberName = (name: string): TsDsl<ts.PropertyName> => { 10 + export const safeAccessorName = (name: string): string => { 11 + regexp.number.lastIndex = 0; 12 + if (regexp.number.test(name)) { 13 + return name.startsWith('-') ? `'${name}'` : name; 14 + } 15 + 16 + regexp.typeScriptIdentifier.lastIndex = 0; 17 + if (regexp.typeScriptIdentifier.test(name)) { 18 + return name; 19 + } 20 + return `'${name}'`; 21 + }; 22 + 23 + export const safeMemberName = ( 24 + name: string, 25 + ): TsDsl<ts.StringLiteral> | IdTsDsl => { 11 26 regexp.typeScriptIdentifier.lastIndex = 0; 12 27 if (regexp.typeScriptIdentifier.test(name)) { 13 28 return new IdTsDsl(name); 14 29 } 15 - return new LiteralTsDsl(name) as TsDsl<ts.PropertyName>; 30 + return new LiteralTsDsl(name) as TsDsl<ts.StringLiteral>; 16 31 }; 17 32 18 - export const safePropName = (name: string): TsDsl<ts.PropertyName> => { 33 + export const safePropName = ( 34 + name: string, 35 + ): TsDsl<ts.StringLiteral | ts.NumericLiteral> | IdTsDsl => { 19 36 regexp.number.lastIndex = 0; 20 37 if (regexp.number.test(name)) { 21 38 return name.startsWith('-') 22 - ? (new LiteralTsDsl(name) as TsDsl<ts.PropertyName>) 23 - : (new LiteralTsDsl(Number(name)) as TsDsl<ts.PropertyName>); 39 + ? (new LiteralTsDsl(name) as TsDsl<ts.StringLiteral>) 40 + : (new LiteralTsDsl(Number(name)) as TsDsl<ts.NumericLiteral>); 24 41 } 25 42 26 43 regexp.typeScriptIdentifier.lastIndex = 0; ··· 28 45 return new IdTsDsl(name); 29 46 } 30 47 31 - return new LiteralTsDsl(name) as TsDsl<ts.PropertyName>; 48 + return new LiteralTsDsl(name) as TsDsl<ts.StringLiteral>; 32 49 }; 33 50 34 51 const safeName = (name: string, reserved: ReservedList): string => { ··· 58 75 return sanitized || '_'; 59 76 }; 60 77 61 - export const safeRuntimeName = (name: string) => 78 + export const safeRuntimeName = (name: string): string => 62 79 safeName(name, reserved.runtime); 63 80 64 - export const safeTypeName = (name: string) => safeName(name, reserved.type); 81 + export const safeTypeName = (name: string): string => 82 + safeName(name, reserved.type);
-8
packages/openapi-ts/src/types/case.d.ts
··· 1 - export type StringCase = 2 - | 'camelCase' 3 - | 'PascalCase' 4 - | 'preserve' 5 - | 'snake_case' 6 - | 'SCREAMING_SNAKE_CASE'; 7 - 8 - export type StringName = string | ((name: string) => string);
+26 -9
packages/openapi-ts/src/types/output.d.ts
··· 1 - import type { NameConflictResolver } from '@hey-api/codegen-core'; 1 + import type { 2 + NameConflictResolver, 3 + RenderContext, 4 + } from '@hey-api/codegen-core'; 2 5 import type ts from 'typescript'; 3 6 4 - import type { StringCase, StringName } from './case'; 7 + import type { Casing, NameTransformer } from '~/utils/naming'; 8 + 9 + import type { MaybeArray, MaybeFunc } from './utils'; 5 10 6 11 export type Formatters = 'biome' | 'prettier'; 7 12 ··· 9 14 10 15 type ImportFileExtensions = '.js' | '.ts'; 11 16 17 + type Header = MaybeFunc< 18 + (ctx: RenderContext) => MaybeArray<string> | null | undefined 19 + >; 20 + 12 21 export type UserOutput = { 13 22 /** 14 23 * Defines casing of the output fields. By default, we preserve `input` ··· 16 25 * 17 26 * @default undefined 18 27 */ 19 - case?: StringCase; 28 + case?: Casing; 20 29 /** 21 30 * Clean the `output` folder on every run? If disabled, this folder may 22 31 * be used to store additional files. The default option is `true` to ··· 34 43 * @default '{{name}}' 35 44 */ 36 45 fileName?: 37 - | StringName 46 + | NameTransformer 38 47 | { 39 48 /** 40 49 * The casing convention to use for generated file names. 41 50 * 42 51 * @default 'preserve' 43 52 */ 44 - case?: StringCase; 53 + case?: Casing; 45 54 /** 46 55 * Custom naming pattern for generated file names. 47 56 * 48 57 * @default '{{name}}' 49 58 */ 50 - name?: StringName; 59 + name?: NameTransformer; 51 60 /** 52 61 * Suffix to append to file names (before the extension). For example, 53 62 * with a suffix of `.gen`, `example.ts` becomes `example.gen.ts`. ··· 67 76 * @default null 68 77 */ 69 78 format?: Formatters | null; 79 + /** 80 + * Text to include at the top of every generated file. 81 + */ 82 + header?: Header; 70 83 /** 71 84 * If specified, this will be the file extension used when importing 72 85 * other modules. By default, we don't add a file extension and let the ··· 128 141 * Defines casing of the output fields. By default, we preserve `input` 129 142 * values as data transforms incur a performance penalty at runtime. 130 143 */ 131 - case: StringCase | undefined; 144 + case: Casing | undefined; 132 145 /** 133 146 * Clean the `output` folder on every run? If disabled, this folder may 134 147 * be used to store additional files. The default option is `true` to ··· 146 159 /** 147 160 * The casing convention to use for generated file names. 148 161 */ 149 - case: StringCase; 162 + case: Casing; 150 163 /** 151 164 * Custom naming pattern for generated file names. 152 165 */ 153 - name: StringName; 166 + name: NameTransformer; 154 167 /** 155 168 * Suffix to append to file names (before the extension). For example, 156 169 * with a suffix of `.gen`, `example.ts` becomes `example.gen.ts`. ··· 167 180 * Which formatter to use to process output folder? 168 181 */ 169 182 format: Formatters | null; 183 + /** 184 + * Text to include at the top of every generated file. 185 + */ 186 + header: Header; 170 187 /** 171 188 * If specified, this will be the file extension used when importing 172 189 * other modules. By default, we don't add a file extension and let the
+15 -16
packages/openapi-ts/src/types/parser.d.ts
··· 7 7 OpenApiSchemaObject, 8 8 } from '~/openApi/types'; 9 9 import type { Hooks } from '~/parser/types/hooks'; 10 - 11 - import type { StringCase, StringName } from './case'; 10 + import type { Casing, NameTransformer } from '~/utils/naming'; 12 11 13 12 type EnumsMode = 'inline' | 'root'; 14 13 ··· 73 72 * 74 73 * @default 'PascalCase' 75 74 */ 76 - case?: StringCase; 75 + case?: Casing; 77 76 /** 78 77 * Whether to transform all enums. 79 78 * ··· 92 91 * 93 92 * @default '{{name}}Enum' 94 93 */ 95 - name?: StringName; 94 + name?: NameTransformer; 96 95 }; 97 96 /** 98 97 * By default, any object schema with a missing `required` keyword is ··· 137 136 * @default '{{name}}Writable' 138 137 */ 139 138 requests?: 140 - | StringName 139 + | NameTransformer 141 140 | { 142 141 /** 143 142 * The casing convention to use for generated names. 144 143 * 145 144 * @default 'preserve' 146 145 */ 147 - case?: StringCase; 146 + case?: Casing; 148 147 /** 149 148 * Customize the generated name of schemas used in requests or 150 149 * containing write-only fields. 151 150 * 152 151 * @default '{{name}}Writable' 153 152 */ 154 - name?: StringName; 153 + name?: NameTransformer; 155 154 }; 156 155 /** 157 156 * Configuration for generated response-specific schemas. ··· 163 162 * @default '{{name}}' 164 163 */ 165 164 responses?: 166 - | StringName 165 + | NameTransformer 167 166 | { 168 167 /** 169 168 * The casing convention to use for generated names. 170 169 * 171 170 * @default 'preserve' 172 171 */ 173 - case?: StringCase; 172 + case?: Casing; 174 173 /** 175 174 * Customize the generated name of schemas used in responses or 176 175 * containing read-only fields. We default to the original name ··· 178 177 * 179 178 * @default '{{name}}' 180 179 */ 181 - name?: StringName; 180 + name?: NameTransformer; 182 181 }; 183 182 }; 184 183 }; ··· 250 249 * 251 250 * @default 'PascalCase' 252 251 */ 253 - case: StringCase; 252 + case: Casing; 254 253 /** 255 254 * Whether to transform all enums. 256 255 * ··· 269 268 * 270 269 * @default '{{name}}Enum' 271 270 */ 272 - name: StringName; 271 + name: NameTransformer; 273 272 }; 274 273 /** 275 274 * By default, any object schema with a missing `required` keyword is ··· 309 308 * 310 309 * @default 'preserve' 311 310 */ 312 - case: StringCase; 311 + case: Casing; 313 312 /** 314 313 * Customize the generated name of schemas used in requests or 315 314 * containing write-only fields. 316 315 * 317 316 * @default '{{name}}Writable' 318 317 */ 319 - name: StringName; 318 + name: NameTransformer; 320 319 }; 321 320 /** 322 321 * Configuration for generated response-specific schemas. ··· 327 326 * 328 327 * @default 'preserve' 329 328 */ 330 - case: StringCase; 329 + case: Casing; 331 330 /** 332 331 * Customize the generated name of schemas used in responses or 333 332 * containing read-only fields. We default to the original name ··· 335 334 * 336 335 * @default '{{name}}' 337 336 */ 338 - name: StringName; 337 + name: NameTransformer; 339 338 }; 340 339 }; 341 340 };
+8 -1
packages/openapi-ts/src/types/utils.d.ts
··· 9 9 * Recursively makes all non-function properties optional. 10 10 */ 11 11 export type DeepPartial<T> = { 12 - [K in keyof T]?: T[K] extends (...args: any[]) => any 12 + [K in keyof T]?: T[K] extends (...args: Array<any>) => any 13 13 ? T[K] 14 14 : T[K] extends object 15 15 ? DeepPartial<T[K]> ··· 25 25 * Accepts a value or a readonly array of values of type T. 26 26 */ 27 27 export type MaybeArray<T> = T | ReadonlyArray<T>; 28 + 29 + /** 30 + * Accepts a value or a function returning a value. 31 + */ 32 + export type MaybeFunc<T extends (...args: Array<any>) => any> = 33 + | T 34 + | ReturnType<T>; 28 35 29 36 /** 30 37 * Converts all top-level Array properties to ReadonlyArray (shallow).
+18 -19
packages/openapi-ts/src/utils/__tests__/stringCase.test.ts packages/openapi-ts/src/utils/naming/__tests__/naming.test.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 3 - import type { StringCase } from '~/types/case'; 3 + import { toCase } from '../naming'; 4 + import type { Casing } from '../types'; 4 5 5 - import { stringCase } from '../stringCase'; 6 - 7 - const cases: ReadonlyArray<StringCase> = [ 6 + const cases: ReadonlyArray<Casing> = [ 8 7 'camelCase', 9 8 'PascalCase', 10 9 'SCREAMING_SNAKE_CASE', ··· 178 177 }, 179 178 ]; 180 179 181 - describe('stringCase', () => { 182 - describe.each(cases)('%s', (style) => { 183 - switch (style) { 180 + describe('toCase', () => { 181 + describe.each(cases)('%s', (casing) => { 182 + switch (casing) { 184 183 case 'PascalCase': 185 184 it.each(scenarios)( 186 185 '$value -> $PascalCase', 187 186 ({ PascalCase, stripLeadingSeparators, value }) => { 188 - expect( 189 - stringCase({ case: style, stripLeadingSeparators, value }), 190 - ).toBe(PascalCase); 187 + expect(toCase(value, casing, { stripLeadingSeparators })).toBe( 188 + PascalCase, 189 + ); 191 190 }, 192 191 ); 193 192 break; ··· 195 194 it.each(scenarios)( 196 195 '$value -> $camelCase', 197 196 ({ camelCase, stripLeadingSeparators, value }) => { 198 - expect( 199 - stringCase({ case: style, stripLeadingSeparators, value }), 200 - ).toBe(camelCase); 197 + expect(toCase(value, casing, { stripLeadingSeparators })).toBe( 198 + camelCase, 199 + ); 201 200 }, 202 201 ); 203 202 break; ··· 205 204 it.each(scenarios)( 206 205 '$value -> $SCREAMING_SNAKE_CASE', 207 206 ({ SCREAMING_SNAKE_CASE, stripLeadingSeparators, value }) => { 208 - expect( 209 - stringCase({ case: style, stripLeadingSeparators, value }), 210 - ).toBe(SCREAMING_SNAKE_CASE); 207 + expect(toCase(value, casing, { stripLeadingSeparators })).toBe( 208 + SCREAMING_SNAKE_CASE, 209 + ); 211 210 }, 212 211 ); 213 212 break; ··· 215 214 it.each(scenarios)( 216 215 '$value -> $snake_case', 217 216 ({ snake_case, stripLeadingSeparators, value }) => { 218 - expect( 219 - stringCase({ case: style, stripLeadingSeparators, value }), 220 - ).toBe(snake_case); 217 + expect(toCase(value, casing, { stripLeadingSeparators })).toBe( 218 + snake_case, 219 + ); 221 220 }, 222 221 ); 223 222 break;
+27 -3
packages/openapi-ts/src/utils/exports.ts
··· 1 - import { stringCase } from './stringCase'; 1 + import type { Casing } from './naming'; 2 + import { toCase } from './naming'; 2 3 3 - // publicly exposed utils 4 + /** 5 + * Utilities shared across the package. 6 + */ 4 7 export const utils = { 5 - stringCase, 8 + /** 9 + * @deprecated use `toCase` instead 10 + */ 11 + stringCase({ 12 + case: casing, 13 + stripLeadingSeparators, 14 + value, 15 + }: { 16 + readonly case: Casing | undefined; 17 + /** 18 + * If leading separators have a semantic meaning, we might not want to 19 + * remove them. 20 + */ 21 + stripLeadingSeparators?: boolean; 22 + value: string; 23 + }) { 24 + return toCase(value, casing, { stripLeadingSeparators }); 25 + }, 26 + /** 27 + * Converts the given string to the specified casing. 28 + */ 29 + toCase, 6 30 };
+7
packages/openapi-ts/src/utils/naming/index.ts
··· 1 + export { applyNaming, resolveNaming, toCase } from './naming'; 2 + export type { 3 + Casing, 4 + NameTransformer, 5 + NamingConfig, 6 + NamingRule, 7 + } from './types';
+45
packages/openapi-ts/src/utils/naming/types.d.ts
··· 1 + import type { MaybeFunc } from '~/types/utils'; 2 + 3 + /** 4 + * Available casing strategies. 5 + */ 6 + export type Casing = 7 + | 'camelCase' 8 + | 'PascalCase' 9 + | 'preserve' 10 + | 'snake_case' 11 + | 'SCREAMING_SNAKE_CASE'; 12 + 13 + /** 14 + * Name transformer: template string or function. 15 + * 16 + * Template supports `{{name}}` variable. 17 + */ 18 + export type NameTransformer = MaybeFunc<(name: string) => string>; 19 + 20 + /** 21 + * Full naming configuration. 22 + */ 23 + export interface NamingConfig { 24 + /** 25 + * Casing strategy applied after transformation. 26 + * 27 + * @deprecated Use `casing` instead. 28 + */ 29 + case?: Casing; 30 + /** 31 + * Casing strategy applied after transformation. 32 + */ 33 + casing?: Casing; 34 + /** 35 + * Name template or transformer function. 36 + * 37 + * Applied before `casing` transformation. 38 + */ 39 + name?: NameTransformer; 40 + } 41 + 42 + /** 43 + * Name customization: shorthand or full configuration. 44 + */ 45 + export type NamingRule = NameTransformer | NamingConfig;
+79 -42
packages/openapi-ts/src/utils/stringCase.ts packages/openapi-ts/src/utils/naming/naming.ts
··· 1 - import type { StringCase } from '~/types/case'; 1 + import type { Casing, NamingConfig, NamingRule } from './types'; 2 2 3 3 const uppercaseRegExp = /[\p{Lu}]/u; 4 4 const lowercaseRegExp = /[\p{Ll}]/u; 5 5 const identifierRegExp = /([\p{Alpha}\p{N}_]|$)/u; 6 - const separatorsRegExp = /[_.:\- `\\[\]{}\\/]+/; 6 + const separatorsRegExp = /[_.$+:\- `\\[\](){}\\/]+/; 7 7 8 8 const leadingSeparatorsRegExp = new RegExp(`^${separatorsRegExp.source}`); 9 9 const separatorsAndIdentifierRegExp = new RegExp( ··· 15 15 'gu', 16 16 ); 17 17 18 - const preserveCase = ({ 19 - case: _case, 20 - string, 21 - }: { 22 - readonly case: StringCase; 23 - string: string; 24 - }) => { 18 + const preserveCase = (value: string, casing: Casing) => { 25 19 let isLastCharLower = false; 26 20 let isLastCharUpper = false; 27 21 let isLastLastCharUpper = false; 28 22 let isLastLastCharPreserved = false; 29 23 30 24 const separator = 31 - _case === 'snake_case' || _case === 'SCREAMING_SNAKE_CASE' ? '_' : '-'; 25 + casing === 'snake_case' || casing === 'SCREAMING_SNAKE_CASE' ? '_' : '-'; 32 26 33 - for (let index = 0; index < string.length; index++) { 34 - const character = string[index]!; 35 - isLastLastCharPreserved = 36 - index > 2 ? string[index - 3] === separator : true; 27 + for (let index = 0; index < value.length; index++) { 28 + const character = value[index]!; 29 + isLastLastCharPreserved = index > 2 ? value[index - 3] === separator : true; 37 30 38 31 let nextIndex = index + 1; 39 - let nextCharacter = string[nextIndex]; 32 + let nextCharacter = value[nextIndex]; 40 33 separatorsRegExp.lastIndex = 0; 41 34 while (nextCharacter && separatorsRegExp.test(nextCharacter)) { 42 35 nextIndex += 1; 43 - nextCharacter = string[nextIndex]; 36 + nextCharacter = value[nextIndex]; 44 37 } 45 38 const isSeparatorBeforeNextCharacter = nextIndex !== index + 1; 46 39 ··· 55 48 lowercaseRegExp.test(nextCharacter))) 56 49 ) { 57 50 // insert separator behind character 58 - string = `${string.slice(0, index)}${separator}${string.slice(index)}`; 51 + value = `${value.slice(0, index)}${separator}${value.slice(index)}`; 59 52 index++; 60 53 isLastLastCharUpper = isLastCharUpper; 61 54 isLastCharLower = false; ··· 72 65 ) 73 66 ) { 74 67 // insert separator 2 characters behind 75 - string = `${string.slice(0, index - 1)}${separator}${string.slice(index - 1)}`; 68 + value = `${value.slice(0, index - 1)}${separator}${value.slice(index - 1)}`; 76 69 isLastLastCharUpper = isLastCharUpper; 77 70 isLastCharLower = true; 78 71 isLastCharUpper = false; ··· 87 80 } 88 81 } 89 82 90 - return string; 83 + return value; 91 84 }; 92 85 93 - export const stringCase = ({ 94 - case: _case, 95 - stripLeadingSeparators = true, 96 - value, 97 - }: { 98 - readonly case: StringCase | undefined; 99 - /** 100 - * If leading separators have a semantic meaning, we might not want to 101 - * remove them. 102 - */ 103 - stripLeadingSeparators?: boolean; 104 - value: string; 105 - }): string => { 86 + /** 87 + * Convert a string to the specified casing. 88 + * 89 + * @param value - The string to convert 90 + * @param casing - The target casing 91 + * @param options - Additional options 92 + * @returns The converted string 93 + */ 94 + export const toCase = ( 95 + value: string, 96 + casing: Casing | undefined, 97 + options: { 98 + /** 99 + * If leading separators have a semantic meaning, we might not want to 100 + * remove them. 101 + */ 102 + stripLeadingSeparators?: boolean; 103 + } = {}, 104 + ) => { 105 + const stripLeadingSeparators = options.stripLeadingSeparators ?? true; 106 + 106 107 let result = value.trim(); 107 108 108 - if (!result.length) { 109 - return ''; 110 - } 111 - 112 - if (!_case || _case === 'preserve') { 109 + if (!result.length || !casing || casing === 'preserve') { 113 110 return result; 114 111 } 115 112 ··· 119 116 return ''; 120 117 } 121 118 122 - return _case === 'PascalCase' || _case === 'SCREAMING_SNAKE_CASE' 119 + return casing === 'PascalCase' || casing === 'SCREAMING_SNAKE_CASE' 123 120 ? result.toLocaleUpperCase() 124 121 : result.toLocaleLowerCase(); 125 122 } ··· 127 124 const hasUpperCase = result !== result.toLocaleLowerCase(); 128 125 129 126 if (hasUpperCase) { 130 - result = preserveCase({ case: _case, string: result }); 127 + result = preserveCase(result, casing); 131 128 } 132 129 133 130 if (stripLeadingSeparators || result[0] !== value[0]) { ··· 135 132 } 136 133 137 134 result = 138 - _case === 'SCREAMING_SNAKE_CASE' 135 + casing === 'SCREAMING_SNAKE_CASE' 139 136 ? result.toLocaleUpperCase() 140 137 : result.toLocaleLowerCase(); 141 138 142 - if (_case === 'PascalCase') { 139 + if (casing === 'PascalCase') { 143 140 result = `${result.charAt(0).toLocaleUpperCase()}${result.slice(1)}`; 144 141 } 145 142 146 - if (_case === 'snake_case' || _case === 'SCREAMING_SNAKE_CASE') { 143 + if (casing === 'snake_case' || casing === 'SCREAMING_SNAKE_CASE') { 147 144 result = result.replaceAll( 148 145 separatorsAndIdentifierRegExp, 149 146 (match, identifier, offset) => { ··· 191 188 192 189 return result; 193 190 }; 191 + 192 + /** 193 + * Normalize a NamingRule to NamingConfig. 194 + */ 195 + export function resolveNaming(rule: NamingRule | undefined): NamingConfig { 196 + if (!rule) { 197 + return {}; 198 + } 199 + if (typeof rule === 'string' || typeof rule === 'function') { 200 + return { name: rule }; 201 + } 202 + return rule; 203 + } 204 + 205 + /** 206 + * Apply naming configuration to a value. 207 + * 208 + * Casing is applied first, then transformation. 209 + */ 210 + export function applyNaming(value: string, config: NamingConfig): string { 211 + let result = value; 212 + 213 + const casing = config.casing ?? config.case; 214 + 215 + if (config.name) { 216 + if (typeof config.name === 'function') { 217 + result = config.name(result); 218 + } else { 219 + // TODO: refactor so there's no need for separators? 220 + const separator = !casing || casing === 'preserve' ? '' : '-'; 221 + result = config.name.replace( 222 + '{{name}}', 223 + `${separator}${result}${separator}`, 224 + ); 225 + } 226 + } 227 + 228 + // TODO: apply case before name? 229 + return toCase(result, casing); 230 + }
+258 -92
pnpm-lock.yaml
··· 112 112 113 113 dev: 114 114 devDependencies: 115 + '@angular/common': 116 + specifier: 19.2.17 117 + version: 19.2.17(@angular/core@19.2.17(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) 118 + '@angular/core': 119 + specifier: 19.2.17 120 + version: 19.2.17(rxjs@7.8.2)(zone.js@0.16.0) 115 121 '@hey-api/codegen-core': 116 122 specifier: workspace:* 117 123 version: link:../packages/codegen-core 118 124 '@hey-api/openapi-ts': 119 125 specifier: workspace:* 120 126 version: link:../packages/openapi-ts 127 + '@opencode-ai/sdk': 128 + specifier: 1.0.170 129 + version: 1.0.170 121 130 '@pinia/colada': 122 131 specifier: 0.19.1 123 132 version: 0.19.1(pinia@3.0.3(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) ··· 139 148 arktype: 140 149 specifier: 2.1.29 141 150 version: 2.1.29 151 + nuxt: 152 + specifier: 3.14.1592 153 + 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.39.1(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.57)(rollup@4.54.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@5.4.19(@types/node@22.10.5)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)) 142 154 swr: 143 155 specifier: 2.3.8 144 156 version: 2.3.8(react@19.0.0) ··· 148 160 valibot: 149 161 specifier: 1.2.0 150 162 version: 1.2.0(typescript@5.9.3) 163 + vue: 164 + specifier: 3.5.25 165 + version: 3.5.25(typescript@5.9.3) 151 166 zod: 152 167 specifier: 4.1.12 153 168 version: 4.1.12 ··· 1385 1400 version: 1.14.2 1386 1401 nuxt: 1387 1402 specifier: 3.14.1592 1388 - 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.39.1(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.57)(rollup@4.54.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@5.4.19(@types/node@22.10.5)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)) 1403 + 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.39.1(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.57)(rollup@4.54.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3) 1389 1404 ofetch: 1390 1405 specifier: 1.5.1 1391 1406 version: 1.5.1 ··· 2800 2815 resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} 2801 2816 engines: {node: '>=6.9.0'} 2802 2817 2803 - '@babel/types@7.28.4': 2804 - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} 2805 - engines: {node: '>=6.9.0'} 2806 - 2807 2818 '@babel/types@7.28.5': 2808 2819 resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} 2809 2820 engines: {node: '>=6.9.0'} ··· 5040 5051 '@one-ini/wasm@0.1.1': 5041 5052 resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} 5042 5053 5054 + '@opencode-ai/sdk@1.0.170': 5055 + resolution: {integrity: sha512-A44hiQib9cB/hAfeS9fAr5sTlz7B9teimfcz8dfIgpMLeT+ZAF5slf32vejhzOy4DoLGeLlJswN0UB+XzFPMrA==} 5056 + 5043 5057 '@oxc-project/types@0.103.0': 5044 5058 resolution: {integrity: sha512-bkiYX5kaXWwUessFRSoXFkGIQTmc6dLGdxuRTrC+h8PSnIdZyuXHHlLAeTmOue5Br/a0/a7dHH0Gca6eXn9MKg==} 5045 5059 ··· 7211 7225 7212 7226 '@vue/shared@3.5.13': 7213 7227 resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} 7214 - 7215 - '@vue/shared@3.5.21': 7216 - resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==} 7217 7228 7218 7229 '@vue/shared@3.5.24': 7219 7230 resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} ··· 10556 10567 magic-string@0.30.18: 10557 10568 resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} 10558 10569 10559 - magic-string@0.30.19: 10560 - resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} 10561 - 10562 10570 magic-string@0.30.21: 10563 10571 resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} 10564 10572 ··· 14313 14321 whatwg-encoding@3.1.1: 14314 14322 resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} 14315 14323 engines: {node: '>=18'} 14316 - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation 14317 14324 14318 14325 whatwg-mimetype@4.0.0: 14319 14326 resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} ··· 14584 14591 dependencies: 14585 14592 '@ampproject/remapping': 2.3.0 14586 14593 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) 14587 - '@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(esbuild@0.25.0)) 14594 + '@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)) 14588 14595 '@angular-devkit/core': 19.2.0(chokidar@4.0.3) 14589 14596 '@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.2) 14590 14597 '@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) ··· 14602 14609 '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.2.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.2)) 14603 14610 ansi-colors: 4.1.3 14604 14611 autoprefixer: 10.4.20(postcss@8.5.2) 14605 - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) 14612 + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) 14606 14613 browserslist: 4.25.4 14607 14614 copy-webpack-plugin: 12.0.2(webpack@5.98.0) 14608 14615 css-loader: 7.1.2(webpack@5.98.0) ··· 14622 14629 picomatch: 4.0.2 14623 14630 piscina: 4.8.0 14624 14631 postcss: 8.5.2 14625 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) 14632 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) 14626 14633 resolve-url-loader: 5.0.0 14627 14634 rxjs: 7.8.1 14628 14635 sass: 1.85.0 ··· 14635 14642 tslib: 2.8.1 14636 14643 typescript: 5.8.3 14637 14644 webpack: 5.98.0(esbuild@0.25.0) 14638 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) 14639 - webpack-dev-server: 5.2.0(webpack@5.98.0) 14645 + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) 14646 + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) 14640 14647 webpack-merge: 6.0.1 14641 14648 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) 14642 14649 optionalDependencies: ··· 14672 14679 dependencies: 14673 14680 '@ampproject/remapping': 2.3.0 14674 14681 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) 14675 - '@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(esbuild@0.25.0)) 14682 + '@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)) 14676 14683 '@angular-devkit/core': 19.2.0(chokidar@4.0.3) 14677 14684 '@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.2) 14678 14685 '@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) ··· 14690 14697 '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2)) 14691 14698 ansi-colors: 4.1.3 14692 14699 autoprefixer: 10.4.20(postcss@8.5.2) 14693 - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) 14700 + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) 14694 14701 browserslist: 4.25.4 14695 14702 copy-webpack-plugin: 12.0.2(webpack@5.98.0) 14696 14703 css-loader: 7.1.2(webpack@5.98.0) ··· 14710 14717 picomatch: 4.0.2 14711 14718 piscina: 4.8.0 14712 14719 postcss: 8.5.2 14713 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) 14720 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) 14714 14721 resolve-url-loader: 5.0.0 14715 14722 rxjs: 7.8.1 14716 14723 sass: 1.85.0 ··· 14723 14730 tslib: 2.8.1 14724 14731 typescript: 5.8.3 14725 14732 webpack: 5.98.0(esbuild@0.25.0) 14726 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) 14727 - webpack-dev-server: 5.2.0(webpack@5.98.0) 14733 + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) 14734 + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) 14728 14735 webpack-merge: 6.0.1 14729 14736 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) 14730 14737 optionalDependencies: ··· 14798 14805 picomatch: 4.0.2 14799 14806 piscina: 4.8.0 14800 14807 postcss: 8.5.2 14801 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) 14808 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) 14802 14809 resolve-url-loader: 5.0.0 14803 14810 rxjs: 7.8.1 14804 14811 sass: 1.85.0 ··· 14811 14818 tslib: 2.8.1 14812 14819 typescript: 5.8.3 14813 14820 webpack: 5.98.0(esbuild@0.25.4) 14814 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) 14821 + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) 14815 14822 webpack-dev-server: 5.2.2(webpack@5.98.0) 14816 14823 webpack-merge: 6.0.1 14817 14824 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) ··· 14899 14906 tslib: 2.8.1 14900 14907 typescript: 5.9.3 14901 14908 webpack: 5.98.0(esbuild@0.25.4) 14902 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) 14909 + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) 14903 14910 webpack-dev-server: 5.2.2(webpack@5.98.0) 14904 14911 webpack-merge: 6.0.1 14905 14912 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) ··· 14931 14938 - webpack-cli 14932 14939 - yaml 14933 14940 14934 - '@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(esbuild@0.25.0))': 14941 + '@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))': 14935 14942 dependencies: 14936 14943 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) 14937 14944 rxjs: 7.8.1 14938 14945 webpack: 5.98.0(esbuild@0.25.0) 14939 - webpack-dev-server: 5.2.0(webpack@5.98.0) 14946 + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) 14940 14947 transitivePeerDependencies: 14941 14948 - chokidar 14942 14949 ··· 16918 16925 '@babel/helper-validator-identifier': 7.28.5 16919 16926 16920 16927 '@babel/types@7.28.2': 16921 - dependencies: 16922 - '@babel/helper-string-parser': 7.27.1 16923 - '@babel/helper-validator-identifier': 7.27.1 16924 - 16925 - '@babel/types@7.28.4': 16926 16928 dependencies: 16927 16929 '@babel/helper-string-parser': 7.27.1 16928 16930 '@babel/helper-validator-identifier': 7.27.1 ··· 18283 18285 '@mapbox/node-pre-gyp@2.0.0(encoding@0.1.13)': 18284 18286 dependencies: 18285 18287 consola: 3.4.2 18286 - detect-libc: 2.0.4 18288 + detect-libc: 2.1.2 18287 18289 https-proxy-agent: 7.0.6 18288 18290 node-fetch: 2.7.0(encoding@0.1.13) 18289 18291 nopt: 8.1.0 ··· 18841 18843 - utf-8-validate 18842 18844 - vue 18843 18845 18846 + '@nuxt/devtools@1.7.0(rollup@4.54.0)(vue@3.5.25(typescript@5.9.3))': 18847 + dependencies: 18848 + '@antfu/utils': 0.7.10 18849 + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2)) 18850 + '@nuxt/devtools-wizard': 1.7.0 18851 + '@nuxt/kit': 3.15.4(magicast@0.3.5) 18852 + '@vue/devtools-core': 7.6.8(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3)) 18853 + '@vue/devtools-kit': 7.6.8 18854 + birpc: 0.2.19 18855 + consola: 3.4.2 18856 + cronstrue: 2.59.0 18857 + destr: 2.0.5 18858 + error-stack-parser-es: 0.1.5 18859 + execa: 7.2.0 18860 + fast-npm-meta: 0.2.2 18861 + flatted: 3.3.3 18862 + get-port-please: 3.2.0 18863 + hookable: 5.5.3 18864 + image-meta: 0.2.1 18865 + is-installed-globally: 1.0.0 18866 + launch-editor: 2.11.1 18867 + local-pkg: 0.5.1 18868 + magicast: 0.3.5 18869 + nypm: 0.4.1 18870 + ohash: 1.1.6 18871 + pathe: 1.1.2 18872 + perfect-debounce: 1.0.0 18873 + pkg-types: 1.3.1 18874 + rc9: 2.1.2 18875 + scule: 1.3.0 18876 + semver: 7.7.3 18877 + simple-git: 3.28.0 18878 + sirv: 3.0.2 18879 + tinyglobby: 0.2.15 18880 + unimport: 3.14.6(rollup@4.54.0) 18881 + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.54.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.2)) 18882 + vite-plugin-vue-inspector: 5.3.2(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2)) 18883 + which: 3.0.1 18884 + ws: 8.18.3 18885 + transitivePeerDependencies: 18886 + - bufferutil 18887 + - rollup 18888 + - supports-color 18889 + - utf-8-validate 18890 + - vue 18891 + 18844 18892 '@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@3.29.5)': 18845 18893 dependencies: 18846 18894 '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@3.29.5) ··· 18853 18901 ignore: 6.0.2 18854 18902 jiti: 2.6.1 18855 18903 klona: 2.0.6 18856 - knitwork: 1.2.0 18904 + knitwork: 1.3.0 18857 18905 mlly: 1.8.0 18858 18906 pathe: 1.1.2 18859 18907 pkg-types: 1.3.1 ··· 18880 18928 ignore: 6.0.2 18881 18929 jiti: 2.6.1 18882 18930 klona: 2.0.6 18883 - knitwork: 1.2.0 18931 + knitwork: 1.3.0 18884 18932 mlly: 1.8.0 18885 18933 pathe: 1.1.2 18886 18934 pkg-types: 1.3.1 ··· 18976 19024 pathe: 1.1.2 18977 19025 pkg-types: 1.3.1 18978 19026 scule: 1.3.0 18979 - std-env: 3.9.0 19027 + std-env: 3.10.0 18980 19028 ufo: 1.6.1 18981 19029 uncrypto: 0.1.3 18982 19030 unimport: 3.14.6(rollup@3.29.5) ··· 18996 19044 pathe: 1.1.2 18997 19045 pkg-types: 1.3.1 18998 19046 scule: 1.3.0 18999 - std-env: 3.9.0 19047 + std-env: 3.10.0 19000 19048 ufo: 1.6.1 19001 19049 uncrypto: 0.1.3 19002 19050 unimport: 3.14.6(rollup@4.54.0) ··· 19026 19074 package-manager-detector: 1.3.0 19027 19075 pathe: 2.0.3 19028 19076 rc9: 2.1.2 19029 - std-env: 3.9.0 19077 + std-env: 3.10.0 19030 19078 transitivePeerDependencies: 19031 19079 - magicast 19032 19080 - supports-color ··· 19084 19132 get-port-please: 3.2.0 19085 19133 h3: 1.15.4 19086 19134 jiti: 2.6.1 19087 - knitwork: 1.2.0 19135 + knitwork: 1.3.0 19088 19136 magic-string: 0.30.21 19089 19137 mlly: 1.8.0 19090 19138 ohash: 1.1.6 ··· 19093 19141 pkg-types: 1.3.1 19094 19142 postcss: 8.5.6 19095 19143 rollup-plugin-visualizer: 5.14.0(rolldown@1.0.0-beta.57)(rollup@3.29.5) 19096 - std-env: 3.9.0 19144 + std-env: 3.10.0 19097 19145 strip-literal: 2.1.1 19098 19146 ufo: 1.6.1 19099 19147 unenv: 1.10.0 ··· 19144 19192 get-port-please: 3.2.0 19145 19193 h3: 1.15.4 19146 19194 jiti: 2.6.1 19147 - knitwork: 1.2.0 19195 + knitwork: 1.3.0 19148 19196 magic-string: 0.30.21 19149 19197 mlly: 1.8.0 19150 19198 ohash: 1.1.6 ··· 19153 19201 pkg-types: 1.3.1 19154 19202 postcss: 8.5.6 19155 19203 rollup-plugin-visualizer: 5.14.0(rolldown@1.0.0-beta.57)(rollup@4.54.0) 19156 - std-env: 3.9.0 19204 + std-env: 3.10.0 19157 19205 strip-literal: 2.1.1 19158 19206 ufo: 1.6.1 19159 19207 unenv: 1.10.0 ··· 19204 19252 get-port-please: 3.2.0 19205 19253 h3: 1.15.4 19206 19254 jiti: 2.6.1 19207 - knitwork: 1.2.0 19255 + knitwork: 1.3.0 19208 19256 magic-string: 0.30.21 19209 19257 mlly: 1.8.0 19210 19258 ohash: 1.1.6 ··· 19213 19261 pkg-types: 1.3.1 19214 19262 postcss: 8.5.6 19215 19263 rollup-plugin-visualizer: 5.14.0(rolldown@1.0.0-beta.57)(rollup@4.54.0) 19216 - std-env: 3.9.0 19264 + std-env: 3.10.0 19217 19265 strip-literal: 2.1.1 19218 19266 ufo: 1.6.1 19219 19267 unenv: 1.10.0 ··· 19247 19295 - vue-tsc 19248 19296 19249 19297 '@one-ini/wasm@0.1.1': {} 19298 + 19299 + '@opencode-ai/sdk@1.0.170': {} 19250 19300 19251 19301 '@oxc-project/types@0.103.0': {} 19252 19302 ··· 21743 21793 21744 21794 '@vue/shared@3.5.13': {} 21745 21795 21746 - '@vue/shared@3.5.21': {} 21747 - 21748 21796 '@vue/shared@3.5.24': {} 21749 21797 21750 21798 '@vue/shared@3.5.25': {} ··· 22238 22286 schema-utils: 4.3.2 22239 22287 webpack: 5.98.0(esbuild@0.25.0) 22240 22288 22241 - babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)): 22289 + babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0): 22242 22290 dependencies: 22243 22291 '@babel/core': 7.26.9 22244 22292 find-cache-dir: 4.0.0 ··· 23112 23160 23113 23161 detect-libc@1.0.3: {} 23114 23162 23115 - detect-libc@2.0.4: {} 23163 + detect-libc@2.0.4: 23164 + optional: true 23116 23165 23117 23166 detect-libc@2.1.2: {} 23118 23167 ··· 23678 23727 '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) 23679 23728 eslint: 9.17.0(jiti@2.6.1) 23680 23729 eslint-import-resolver-node: 0.3.9 23681 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) 23682 - 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)) 23730 + 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)) 23731 + 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)) 23683 23732 eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@2.6.1)) 23684 23733 eslint-plugin-react: 7.37.5(eslint@9.17.0(jiti@2.6.1)) 23685 23734 eslint-plugin-react-hooks: 5.2.0(eslint@9.17.0(jiti@2.6.1)) ··· 23706 23755 transitivePeerDependencies: 23707 23756 - supports-color 23708 23757 23709 - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)): 23758 + 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)): 23710 23759 dependencies: 23711 23760 '@nolyfill/is-core-module': 1.0.39 23712 23761 debug: 4.4.3 ··· 23717 23766 tinyglobby: 0.2.15 23718 23767 unrs-resolver: 1.11.1 23719 23768 optionalDependencies: 23720 - 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)) 23769 + 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)) 23721 23770 transitivePeerDependencies: 23722 23771 - supports-color 23723 23772 23724 - 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)): 23773 + 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)): 23725 23774 dependencies: 23726 23775 debug: 3.2.7 23727 23776 optionalDependencies: 23728 23777 '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) 23729 23778 eslint: 9.17.0(jiti@2.6.1) 23730 23779 eslint-import-resolver-node: 0.3.9 23731 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) 23780 + 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)) 23732 23781 transitivePeerDependencies: 23733 23782 - supports-color 23734 23783 23735 - 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)): 23784 + 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)): 23736 23785 dependencies: 23737 23786 '@rtsao/scc': 1.1.0 23738 23787 array-includes: 3.1.9 ··· 23743 23792 doctrine: 2.1.0 23744 23793 eslint: 9.17.0(jiti@2.6.1) 23745 23794 eslint-import-resolver-node: 0.3.9 23746 - 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)) 23795 + 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)) 23747 23796 hasown: 2.0.2 23748 23797 is-core-module: 2.16.1 23749 23798 is-glob: 4.0.3 ··· 24928 24977 mlly: 1.8.0 24929 24978 mocked-exports: 0.1.1 24930 24979 pathe: 2.0.3 24931 - unplugin: 2.3.10 24980 + unplugin: 2.3.11 24932 24981 transitivePeerDependencies: 24933 24982 - rollup 24934 24983 ··· 24938 24987 mlly: 1.8.0 24939 24988 mocked-exports: 0.1.1 24940 24989 pathe: 2.0.3 24941 - unplugin: 2.3.10 24990 + unplugin: 2.3.11 24942 24991 transitivePeerDependencies: 24943 24992 - rollup 24944 24993 ··· 25763 25812 dependencies: 25764 25813 '@jridgewell/sourcemap-codec': 1.5.5 25765 25814 25766 - magic-string@0.30.19: 25767 - dependencies: 25768 - '@jridgewell/sourcemap-codec': 1.5.5 25769 - 25770 25815 magic-string@0.30.21: 25771 25816 dependencies: 25772 25817 '@jridgewell/sourcemap-codec': 1.5.5 ··· 26361 26406 ioredis: 5.7.0 26362 26407 jiti: 2.6.1 26363 26408 klona: 2.0.6 26364 - knitwork: 1.2.0 26409 + knitwork: 1.3.0 26365 26410 listhen: 1.9.0 26366 26411 magic-string: 0.30.21 26367 26412 magicast: 0.3.5 26368 26413 mime: 4.0.7 26369 26414 mlly: 1.8.0 26370 26415 node-fetch-native: 1.6.7 26371 - node-mock-http: 1.0.2 26416 + node-mock-http: 1.0.4 26372 26417 ofetch: 1.5.1 26373 26418 ohash: 2.0.11 26374 26419 pathe: 2.0.3 ··· 26383 26428 serve-placeholder: 2.0.2 26384 26429 serve-static: 2.2.0 26385 26430 source-map: 0.7.6 26386 - std-env: 3.9.0 26431 + std-env: 3.10.0 26387 26432 ufo: 1.6.1 26388 26433 ultrahtml: 1.6.0 26389 26434 uncrypto: 0.1.3 ··· 26592 26637 '@unhead/shared': 1.11.20 26593 26638 '@unhead/ssr': 1.11.20 26594 26639 '@unhead/vue': 1.11.20(vue@3.5.25(typescript@5.9.3)) 26595 - '@vue/shared': 3.5.21 26640 + '@vue/shared': 3.5.25 26596 26641 acorn: 8.14.0 26597 26642 c12: 2.0.1(magicast@0.3.5) 26598 26643 chokidar: 4.0.3 ··· 26613 26658 impound: 0.2.2(rollup@3.29.5) 26614 26659 jiti: 2.6.1 26615 26660 klona: 2.0.6 26616 - knitwork: 1.2.0 26617 - magic-string: 0.30.19 26661 + knitwork: 1.3.0 26662 + magic-string: 0.30.21 26618 26663 mlly: 1.8.0 26619 26664 nanotar: 0.1.1 26620 26665 nitropack: 2.12.4(@netlify/blobs@9.1.2)(encoding@0.1.13)(rolldown@1.0.0-beta.57) ··· 26628 26673 radix3: 1.1.2 26629 26674 scule: 1.3.0 26630 26675 semver: 7.7.3 26631 - std-env: 3.9.0 26676 + std-env: 3.10.0 26632 26677 strip-literal: 2.1.1 26633 26678 tinyglobby: 0.2.10 26634 26679 ufo: 1.6.1 ··· 26713 26758 '@unhead/shared': 1.11.20 26714 26759 '@unhead/ssr': 1.11.20 26715 26760 '@unhead/vue': 1.11.20(vue@3.5.25(typescript@5.9.3)) 26716 - '@vue/shared': 3.5.21 26761 + '@vue/shared': 3.5.25 26762 + acorn: 8.14.0 26763 + c12: 2.0.1(magicast@0.3.5) 26764 + chokidar: 4.0.3 26765 + compatx: 0.1.8 26766 + consola: 3.4.2 26767 + cookie-es: 1.2.2 26768 + defu: 6.1.4 26769 + destr: 2.0.5 26770 + devalue: 5.3.2 26771 + errx: 0.1.0 26772 + esbuild: 0.24.2 26773 + escape-string-regexp: 5.0.0 26774 + estree-walker: 3.0.3 26775 + globby: 14.1.0 26776 + h3: 1.15.4 26777 + hookable: 5.5.3 26778 + ignore: 6.0.2 26779 + impound: 0.2.2(rollup@4.54.0) 26780 + jiti: 2.6.1 26781 + klona: 2.0.6 26782 + knitwork: 1.3.0 26783 + magic-string: 0.30.21 26784 + mlly: 1.8.0 26785 + nanotar: 0.1.1 26786 + nitropack: 2.12.4(@netlify/blobs@9.1.2)(encoding@0.1.13)(rolldown@1.0.0-beta.57) 26787 + nuxi: 3.28.0 26788 + nypm: 0.3.12 26789 + ofetch: 1.5.1 26790 + ohash: 1.1.6 26791 + pathe: 1.1.2 26792 + perfect-debounce: 1.0.0 26793 + pkg-types: 1.3.1 26794 + radix3: 1.1.2 26795 + scule: 1.3.0 26796 + semver: 7.7.3 26797 + std-env: 3.10.0 26798 + strip-literal: 2.1.1 26799 + tinyglobby: 0.2.10 26800 + ufo: 1.6.1 26801 + ultrahtml: 1.6.0 26802 + uncrypto: 0.1.3 26803 + unctx: 2.4.1 26804 + unenv: 1.10.0 26805 + unhead: 1.11.20 26806 + unimport: 3.14.6(rollup@4.54.0) 26807 + unplugin: 1.16.1 26808 + unplugin-vue-router: 0.10.9(rollup@4.54.0)(vue-router@4.5.0(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) 26809 + unstorage: 1.17.0(@netlify/blobs@9.1.2)(db0@0.3.2)(ioredis@5.7.0) 26810 + untyped: 1.5.2 26811 + vue: 3.5.25(typescript@5.9.3) 26812 + vue-bundle-renderer: 2.1.2 26813 + vue-devtools-stub: 0.1.0 26814 + vue-router: 4.5.0(vue@3.5.25(typescript@5.9.3)) 26815 + optionalDependencies: 26816 + '@parcel/watcher': 2.5.1 26817 + '@types/node': 22.10.5 26818 + transitivePeerDependencies: 26819 + - '@azure/app-configuration' 26820 + - '@azure/cosmos' 26821 + - '@azure/data-tables' 26822 + - '@azure/identity' 26823 + - '@azure/keyvault-secrets' 26824 + - '@azure/storage-blob' 26825 + - '@biomejs/biome' 26826 + - '@capacitor/preferences' 26827 + - '@deno/kv' 26828 + - '@electric-sql/pglite' 26829 + - '@libsql/client' 26830 + - '@netlify/blobs' 26831 + - '@planetscale/database' 26832 + - '@upstash/redis' 26833 + - '@vercel/blob' 26834 + - '@vercel/functions' 26835 + - '@vercel/kv' 26836 + - aws4fetch 26837 + - better-sqlite3 26838 + - bufferutil 26839 + - db0 26840 + - drizzle-orm 26841 + - encoding 26842 + - eslint 26843 + - idb-keyval 26844 + - ioredis 26845 + - less 26846 + - lightningcss 26847 + - magicast 26848 + - meow 26849 + - mysql2 26850 + - optionator 26851 + - rolldown 26852 + - rollup 26853 + - sass 26854 + - sass-embedded 26855 + - sqlite3 26856 + - stylelint 26857 + - stylus 26858 + - sugarss 26859 + - supports-color 26860 + - terser 26861 + - typescript 26862 + - uploadthing 26863 + - utf-8-validate 26864 + - vite 26865 + - vls 26866 + - vti 26867 + - vue-tsc 26868 + - xml2js 26869 + 26870 + 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.39.1(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.57)(rollup@4.54.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3): 26871 + dependencies: 26872 + '@nuxt/devalue': 2.0.2 26873 + '@nuxt/devtools': 1.7.0(rollup@4.54.0)(vue@3.5.25(typescript@5.9.3)) 26874 + '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.54.0) 26875 + '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.54.0) 26876 + '@nuxt/telemetry': 2.6.6(magicast@0.3.5) 26877 + '@nuxt/vite-builder': 3.14.1592(@types/node@22.10.5)(eslint@9.39.1(jiti@2.6.1))(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.57)(rollup@4.54.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) 26878 + '@unhead/dom': 1.11.20 26879 + '@unhead/shared': 1.11.20 26880 + '@unhead/ssr': 1.11.20 26881 + '@unhead/vue': 1.11.20(vue@3.5.25(typescript@5.9.3)) 26882 + '@vue/shared': 3.5.25 26717 26883 acorn: 8.14.0 26718 26884 c12: 2.0.1(magicast@0.3.5) 26719 26885 chokidar: 4.0.3 ··· 26734 26900 impound: 0.2.2(rollup@4.54.0) 26735 26901 jiti: 2.6.1 26736 26902 klona: 2.0.6 26737 - knitwork: 1.2.0 26738 - magic-string: 0.30.19 26903 + knitwork: 1.3.0 26904 + magic-string: 0.30.21 26739 26905 mlly: 1.8.0 26740 26906 nanotar: 0.1.1 26741 26907 nitropack: 2.12.4(@netlify/blobs@9.1.2)(encoding@0.1.13)(rolldown@1.0.0-beta.57) ··· 26749 26915 radix3: 1.1.2 26750 26916 scule: 1.3.0 26751 26917 semver: 7.7.3 26752 - std-env: 3.9.0 26918 + std-env: 3.10.0 26753 26919 strip-literal: 2.1.1 26754 26920 tinyglobby: 0.2.10 26755 26921 ufo: 1.6.1 ··· 26834 27000 '@unhead/shared': 1.11.20 26835 27001 '@unhead/ssr': 1.11.20 26836 27002 '@unhead/vue': 1.11.20(vue@3.5.25(typescript@5.9.3)) 26837 - '@vue/shared': 3.5.21 27003 + '@vue/shared': 3.5.25 26838 27004 acorn: 8.14.0 26839 27005 c12: 2.0.1(magicast@0.3.5) 26840 27006 chokidar: 4.0.3 ··· 26855 27021 impound: 0.2.2(rollup@4.54.0) 26856 27022 jiti: 2.6.1 26857 27023 klona: 2.0.6 26858 - knitwork: 1.2.0 26859 - magic-string: 0.30.19 27024 + knitwork: 1.3.0 27025 + magic-string: 0.30.21 26860 27026 mlly: 1.8.0 26861 27027 nanotar: 0.1.1 26862 27028 nitropack: 2.12.4(@netlify/blobs@9.1.2)(encoding@0.1.13)(rolldown@1.0.0-beta.57) ··· 26870 27036 radix3: 1.1.2 26871 27037 scule: 1.3.0 26872 27038 semver: 7.7.3 26873 - std-env: 3.9.0 27039 + std-env: 3.10.0 26874 27040 strip-literal: 2.1.1 26875 27041 tinyglobby: 0.2.10 26876 27042 ufo: 1.6.1 ··· 26955 27121 '@unhead/shared': 1.11.20 26956 27122 '@unhead/ssr': 1.11.20 26957 27123 '@unhead/vue': 1.11.20(vue@3.5.25(typescript@5.9.3)) 26958 - '@vue/shared': 3.5.21 27124 + '@vue/shared': 3.5.25 26959 27125 acorn: 8.14.0 26960 27126 c12: 2.0.1(magicast@0.3.5) 26961 27127 chokidar: 4.0.3 ··· 26976 27142 impound: 0.2.2(rollup@4.54.0) 26977 27143 jiti: 2.6.1 26978 27144 klona: 2.0.6 26979 - knitwork: 1.2.0 26980 - magic-string: 0.30.19 27145 + knitwork: 1.3.0 27146 + magic-string: 0.30.21 26981 27147 mlly: 1.8.0 26982 27148 nanotar: 0.1.1 26983 27149 nitropack: 2.12.4(@netlify/blobs@9.1.2)(encoding@0.1.13)(rolldown@1.0.0-beta.57) ··· 26991 27157 radix3: 1.1.2 26992 27158 scule: 1.3.0 26993 27159 semver: 7.7.3 26994 - std-env: 3.9.0 27160 + std-env: 3.10.0 26995 27161 strip-literal: 2.1.1 26996 27162 tinyglobby: 0.2.10 26997 27163 ufo: 1.6.1 ··· 27610 27776 ts-node: 10.9.2(@types/node@22.10.5)(typescript@5.9.3) 27611 27777 optional: true 27612 27778 27613 - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)): 27779 + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0): 27614 27780 dependencies: 27615 27781 cosmiconfig: 9.0.0(typescript@5.8.3) 27616 27782 jiti: 1.21.7 ··· 29831 29997 scule: 1.3.0 29832 29998 strip-literal: 3.0.0 29833 29999 tinyglobby: 0.2.15 29834 - unplugin: 2.3.10 30000 + unplugin: 2.3.11 29835 30001 unplugin-utils: 0.2.5 29836 30002 29837 30003 unique-filename@4.0.0: ··· 29895 30061 29896 30062 unplugin-vue-router@0.10.9(rollup@3.29.5)(vue-router@4.5.0(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)): 29897 30063 dependencies: 29898 - '@babel/types': 7.28.4 30064 + '@babel/types': 7.28.5 29899 30065 '@rollup/pluginutils': 5.2.0(rollup@3.29.5) 29900 30066 '@vue-macros/common': 1.16.1(vue@3.5.25(typescript@5.9.3)) 29901 30067 ast-walker-scope: 0.6.2 ··· 29917 30083 29918 30084 unplugin-vue-router@0.10.9(rollup@4.54.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)): 29919 30085 dependencies: 29920 - '@babel/types': 7.28.4 30086 + '@babel/types': 7.28.5 29921 30087 '@rollup/pluginutils': 5.2.0(rollup@4.54.0) 29922 30088 '@vue-macros/common': 1.16.1(vue@3.5.25(typescript@5.9.3)) 29923 30089 ast-walker-scope: 0.6.2 ··· 29939 30105 29940 30106 unplugin-vue-router@0.10.9(rollup@4.54.0)(vue-router@4.5.0(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)): 29941 30107 dependencies: 29942 - '@babel/types': 7.28.4 30108 + '@babel/types': 7.28.5 29943 30109 '@rollup/pluginutils': 5.2.0(rollup@4.54.0) 29944 30110 '@vue-macros/common': 1.16.1(vue@3.5.25(typescript@5.9.3)) 29945 30111 ast-walker-scope: 0.6.2 ··· 30057 30223 30058 30224 unwasm@0.3.11: 30059 30225 dependencies: 30060 - knitwork: 1.2.0 30226 + knitwork: 1.3.0 30061 30227 magic-string: 0.30.21 30062 30228 mlly: 1.8.0 30063 30229 pathe: 2.0.3 30064 30230 pkg-types: 2.3.0 30065 - unplugin: 2.3.10 30231 + unplugin: 2.3.11 30066 30232 30067 30233 update-browserslist-db@1.1.3(browserslist@4.25.4): 30068 30234 dependencies: ··· 30899 31065 30900 31066 webidl-conversions@7.0.0: {} 30901 31067 30902 - webpack-dev-middleware@7.4.2(webpack@5.98.0): 31068 + webpack-dev-middleware@7.4.2(webpack@5.98.0(esbuild@0.25.0)): 30903 31069 dependencies: 30904 31070 colorette: 2.0.20 30905 31071 memfs: 4.38.2 ··· 30910 31076 optionalDependencies: 30911 31077 webpack: 5.98.0(esbuild@0.25.0) 30912 31078 30913 - webpack-dev-server@5.2.0(webpack@5.98.0): 31079 + webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)): 30914 31080 dependencies: 30915 31081 '@types/bonjour': 3.5.13 30916 31082 '@types/connect-history-api-fallback': 1.5.4 ··· 30937 31103 serve-index: 1.9.1 30938 31104 sockjs: 0.3.24 30939 31105 spdy: 4.0.2 30940 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) 31106 + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) 30941 31107 ws: 8.18.3 30942 31108 optionalDependencies: 30943 31109 webpack: 5.98.0(esbuild@0.25.0) ··· 30975 31141 serve-index: 1.9.1 30976 31142 sockjs: 0.3.24 30977 31143 spdy: 4.0.2 30978 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) 31144 + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) 30979 31145 ws: 8.18.3 30980 31146 optionalDependencies: 30981 31147 webpack: 5.98.0(esbuild@0.25.0)