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

chore: clean up renderer

Lubos d1ea0bae 0b345867

+2297 -1810
+1 -1
.vscode/launch.json
··· 18 18 "runtimeArgs": ["-r", "ts-node/register/transpile-only"], 19 19 "program": "${workspaceFolder}/packages/openapi-ts/src/run.ts", 20 20 "env": { 21 - "DEBUG": "heyapi:*" 21 + "DEBUG": "false" 22 22 } 23 23 } 24 24 ]
+15 -20
dev/openapi-ts.config.ts
··· 43 43 // 'invalid', 44 44 // 'full.yaml', 45 45 // 'object-property-names.yaml', 46 - // 'openai.yaml', 46 + 'openai.yaml', 47 47 // 'opencode.yaml', 48 48 // 'pagination-ref.yaml', 49 49 // 'sdk-instance.yaml', ··· 53 53 // 'validators.yaml', 54 54 // 'validators-circular-ref.json', 55 55 // 'validators-circular-ref-2.yaml', 56 - 'zoom-video-sdk.json', 56 + // 'zoom-video-sdk.json', 57 57 ), 58 58 // path: 'https://get.heyapi.dev/hey-api/backend?branch=main&version=1.0.0', 59 59 // path: 'http://localhost:4000/', ··· 237 237 // error: '他們_error_{{name}}', 238 238 // name: '你們_errors_{{name}}', 239 239 // }, 240 + // exportFromIndex: false, 240 241 name: '@hey-api/typescript', 241 242 // requests: '我們_data_{{name}}', 242 243 // responses: { ··· 263 264 // }, 264 265 // include... 265 266 // instance: true, 266 - // name: '@hey-api/sdk', 267 + name: '@hey-api/sdk', 267 268 // operationId: false, 268 269 // paramsStructure: 'flat', 269 270 // responseStyle: 'data', ··· 272 273 // signature: 'object', 273 274 // transformer: '@hey-api/transformers', 274 275 // transformer: true, 275 - validator: 'valibot', 276 + validator: 'zod', 276 277 // validator: { 277 278 // request: 'zod', 278 279 // response: 'zod', ··· 371 372 { 372 373 // case: 'SCREAMING_SNAKE_CASE', 373 374 // comments: false, 374 - // definitions: 'z{{name}}Definition', 375 + definitions: 'z{{name}}', 375 376 exportFromIndex: true, 376 377 // metadata: true, 377 - // name: 'valibot', 378 + name: 'valibot', 378 379 // requests: { 379 380 // case: 'PascalCase', 380 381 // name: '{{name}}Data', 381 382 // }, 382 - // responses: { 383 - // // case: 'snake_case', 384 - // name: 'z{{name}}TestResponse', 385 - // }, 383 + responses: { 384 + // case: 'snake_case', 385 + name: 'z{{name}}TestResponse', 386 + }, 386 387 // webhooks: { 387 388 // name: 'q{{name}}CoolWebhook', 388 389 // }, ··· 425 426 validator({ $, schema, v }) { 426 427 return [ 427 428 $.const('parsed').assign( 428 - $(v.placeholder) 429 - .attr('safeParseAsync') 430 - .call(schema.placeholder, 'data') 431 - .await(), 429 + $(v).attr('safeParseAsync').call(schema, 'data').await(), 432 430 ), 433 431 $('parsed').return(), 434 432 ]; ··· 449 447 // infer: 'D{{name}}ZodType', 450 448 // }, 451 449 }, 452 - // exportFromIndex: true, 450 + exportFromIndex: true, 453 451 metadata: true, 454 - // name: 'zod', 452 + name: 'zod', 455 453 // requests: { 456 454 // // case: 'SCREAMING_SNAKE_CASE', 457 455 // // name: 'z{{name}}TestData', ··· 505 503 validator({ $, schema }) { 506 504 return [ 507 505 $.const('parsed').assign( 508 - $(schema.placeholder) 509 - .attr('safeParseAsync') 510 - .call('data') 511 - .await(), 506 + $(schema).attr('safeParseAsync').call('data').await(), 512 507 ), 513 508 $('parsed').return(), 514 509 ];
+85
packages/codegen-core/src/__tests__/symbols.test.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 3 3 import { SymbolRegistry } from '../symbols/registry'; 4 + import { isSymbol } from '../symbols/symbol'; 4 5 5 6 describe('SymbolRegistry', () => { 6 7 it('covers the full public interface', () => { ··· 259 260 // The new stub isn't indexed, so query returns nothing yet 260 261 const newQuery = registry.query({ something: 'else' }); 261 262 expect(newQuery).toEqual([]); 263 + }); 264 + 265 + it('isSymbol covers various inputs', () => { 266 + const registry = new SymbolRegistry(); 267 + 268 + // real registered symbol 269 + const sym = registry.register({ meta: { a: 1 }, name: 'A' }); 270 + expect(isSymbol(sym)).toBe(true); 271 + 272 + // stub reference (unregistered) 273 + const stub = registry.reference({ b: 2 }); 274 + expect(isSymbol(stub)).toBe(true); 275 + 276 + // primitives 277 + expect(isSymbol(null)).toBe(false); 278 + expect(isSymbol(undefined)).toBe(false); 279 + expect(isSymbol(123)).toBe(false); 280 + expect(isSymbol('foo')).toBe(false); 281 + expect(isSymbol(true)).toBe(false); 282 + 283 + // arrays and plain objects 284 + expect(isSymbol([])).toBe(false); 285 + expect(isSymbol({})).toBe(false); 286 + 287 + // object with different tag 288 + expect(isSymbol({ '~tag': 'not-a-symbol' })).toBe(false); 289 + 290 + // object masquerading as a symbol (matches tag) 291 + expect(isSymbol({ '~tag': 'heyapi.symbol' })).toBe(true); 292 + 293 + // Date, Map, Set should be false 294 + expect(isSymbol(new Date())).toBe(false); 295 + expect(isSymbol(new Map())).toBe(false); 296 + expect(isSymbol(new Set())).toBe(false); 297 + 298 + // Typed arrays and ArrayBuffer should be false 299 + expect(isSymbol(new Uint8Array())).toBe(false); 300 + expect(isSymbol(new ArrayBuffer(8))).toBe(false); 301 + 302 + // Functions without tag should be false 303 + const fn = () => {}; 304 + expect(isSymbol(fn)).toBe(false); 305 + 306 + // Class instance without tag should be false 307 + class Foo {} 308 + const foo = new Foo(); 309 + expect(isSymbol(foo)).toBe(false); 310 + 311 + // Proxy with tag should be true if own property is present 312 + const target = {} as Record<string, unknown>; 313 + const proxied = new Proxy(target, { 314 + get(_, prop) { 315 + if (prop === '~tag') return 'heyapi.symbol'; 316 + return undefined; 317 + }, 318 + getOwnPropertyDescriptor(_, prop) { 319 + if (prop === '~tag') 320 + return { 321 + configurable: true, 322 + enumerable: true, 323 + value: 'heyapi.symbol', 324 + writable: false, 325 + }; 326 + return undefined; 327 + }, 328 + has(_, prop) { 329 + return prop === '~tag'; 330 + }, 331 + }); 332 + // Define as own property to satisfy hasOwn 333 + Object.defineProperty(target, '~tag', { 334 + configurable: true, 335 + value: 'heyapi.symbol', 336 + }); 337 + expect(isSymbol(proxied)).toBe(true); 338 + 339 + // Inherited tag should be false (not own property) 340 + const proto = { '~tag': 'heyapi.symbol' }; 341 + const objWithProto = Object.create(proto); 342 + expect(isSymbol(objWithProto)).toBe(false); 343 + 344 + // Primitive edge cases 345 + expect(isSymbol(Symbol('x'))).toBe(false); 346 + expect(isSymbol(0n)).toBe(false); 262 347 }); 263 348 });
+76
packages/codegen-core/src/bindings/plan.d.ts
··· 1 + import type { SymbolImportKind } from '../symbols/types'; 2 + 3 + export interface PlannedImport { 4 + /** ID of the file where the symbol lives */ 5 + from: number; 6 + /** The final exported name of the symbol in its source file */ 7 + importedName: string; 8 + /** Whether this import is type-only. */ 9 + isTypeOnly: boolean; 10 + /** Import flavor. */ 11 + kind: SymbolImportKind; 12 + /** 13 + * The name this symbol will have locally in this file. 14 + * This is where aliasing is applied: 15 + * 16 + * import { Foo as Foo$2 } from "./x" 17 + * 18 + * localName === "Foo$2" 19 + */ 20 + localName: string; 21 + /** ID of the symbol being imported */ 22 + symbolId: number; 23 + } 24 + 25 + export interface PlannedExport { 26 + /** 27 + * Whether the export was explicitly requested by the plugin/DSL 28 + * (e.g. symbol.exported = true) vs implicitly required (e.g. re-export). 29 + */ 30 + explicit: boolean; 31 + /** 32 + * The name this symbol will be exported under from this file. 33 + * 34 + * This may differ from the symbol's finalName if aliasing is needed: 35 + * 36 + * export { Foo as Foo2 } 37 + * 38 + * exportedName === "Foo2" 39 + */ 40 + exportedName: string; 41 + /** Whether this export is type-only. */ 42 + isTypeOnly: boolean; 43 + /** Export flavor. */ 44 + kind: SymbolImportKind; 45 + /** ID of the symbol being exported */ 46 + symbolId: number; 47 + } 48 + 49 + export interface PlannedReexport { 50 + /** 51 + * Name under which the symbol is exported in this file. 52 + * 53 + * export { Foo as Bar } from "./models" 54 + * 55 + * exportedName === "Bar" 56 + */ 57 + exportedName: string; 58 + /** ID of the source file containing the symbol */ 59 + from: number; 60 + /** 61 + * The name the symbol has in the source file’s exports. 62 + * 63 + * export { Foo as Bar } from "./models" 64 + * 65 + * importedName === "Foo" 66 + * 67 + * This handles aliasing in the source file's export list. 68 + */ 69 + importedName: string; 70 + /** Whether this re-export is type-only. */ 71 + isTypeOnly: boolean; 72 + /** Export flavor. */ 73 + kind: SymbolImportKind; 74 + /** ID of the symbol being re-exported */ 75 + symbolId: number; 76 + }
+1
packages/codegen-core/src/debug.ts
··· 5 5 colors.enabled = colorSupport().hasBasic; 6 6 7 7 const DEBUG_GROUPS = { 8 + analyzer: colors.greenBright, 8 9 dsl: colors.cyanBright, 9 10 registry: colors.blueBright, 10 11 symbol: colors.magentaBright,
+5 -6
packages/codegen-core/src/files/registry.ts
··· 99 99 result = { 100 100 ...result, 101 101 ...file, // clone to avoid mutation 102 + exports: result?.exports ?? [], 102 103 id, 103 - localNames: new Set(), 104 + imports: result?.imports ?? [], 105 + reexports: result?.reexports ?? [], 106 + reservedNames: result?.reservedNames ?? new Map(), 104 107 resolvedNames: result?.resolvedNames ?? new BiMap(), 105 - symbols: result?.symbols ?? { 106 - body: [], 107 - exports: [], 108 - imports: [], 109 - }, 108 + symbols: result?.symbols ?? [], 110 109 }; 111 110 this.values.set(id, result); 112 111
+14
packages/codegen-core/src/files/rules.d.ts
··· 1 + export interface Rules { 2 + /** Whether two exported names may collide. */ 3 + allowExportNameShadowing: boolean; 4 + /** Whether a local symbol can shadow another local name without error. */ 5 + allowLocalNameShadowing: boolean; 6 + /** Whether the language requires file-scoped name uniqueness. */ 7 + fileScopedNamesMustBeUnique: boolean; 8 + /** Whether `import { X } from "mod"` introduces a local binding `X`. */ 9 + importCreatesLocalBinding: boolean; 10 + /** Whether `export { X } from "mod"` introduces a local binding `X`. */ 11 + reexportCreatesLocalBinding: boolean; 12 + /** Whether the language distinguishes type-only imports. */ 13 + supportsTypeImports: boolean; 14 + }
+24 -33
packages/codegen-core/src/files/types.d.ts
··· 1 1 import type { IBiMap } from '../bimap/types'; 2 + import type { 3 + PlannedExport, 4 + PlannedImport, 5 + PlannedReexport, 6 + } from '../bindings/plan'; 7 + import type { Symbol } from '../symbols/symbol'; 8 + import type { SymbolKind } from '../symbols/types'; 9 + import type { Rules } from './rules'; 2 10 3 11 /** 4 12 * Selector array used to reference files. ··· 10 18 export type IFileIdentifier = number | IFileSelector; 11 19 12 20 export type IFileIn = { 13 - /** 14 - * File extension, if any. 15 - */ 21 + /** File extension, if any. */ 16 22 extension?: string; 17 23 /** 18 24 * Indicates whether the file is external, meaning it is not generated ··· 22 28 * @example true 23 29 */ 24 30 external?: boolean; 25 - /** 26 - * Unique file ID. If one is not provided, it will be auto-generated. 27 - */ 31 + /** Unique file ID. If one is not provided, it will be auto-generated. */ 28 32 readonly id?: number; 29 33 /** 30 34 * The desired name for the file within the project. If there are multiple files ··· 48 52 }; 49 53 50 54 export type IFileOut = IFileIn & { 51 - /** 52 - * Unique file ID. 53 - */ 55 + /** Named exports originating from this file */ 56 + readonly exports: Array<PlannedExport>; 57 + /** Unique file ID. */ 54 58 readonly id: number; 55 - /** 56 - * Names declared in local (non‑top‑level) scopes within this file. 57 - */ 58 - readonly localNames: Set<string>; 59 - /** 60 - * Map holding resolved names for symbols in this file. 61 - */ 59 + /** Fully resolved imports this file needs */ 60 + readonly imports: Array<PlannedImport>; 61 + /** Re-exports (“export { X } from …”) */ 62 + readonly reexports: Array<PlannedReexport>; 63 + /** Top-level names in this file that cannot be reused (imports, exports, declarations, symbol names). */ 64 + readonly reservedNames: Map<string, Set<SymbolKind>>; 65 + /** Map holding resolved names for symbols in this file. */ 62 66 readonly resolvedNames: IBiMap<number, string>; 63 - /** 64 - * Symbols in this file, categorized by their role. 65 - */ 66 - readonly symbols: { 67 - /** 68 - * Symbols declared in the body of this file. 69 - */ 70 - body: Array<number>; 71 - /** 72 - * Symbols re-exported from other files. 73 - */ 74 - exports: Array<number>; 75 - /** 76 - * Symbols imported from other files. 77 - */ 78 - imports: Array<number>; 79 - }; 67 + /** Rules that control naming, imports, reexports, and scope behavior for this file. */ 68 + rules?: Rules; 69 + /** Symbols defined inside this file */ 70 + readonly symbols: Array<Symbol>; 80 71 }; 81 72 82 73 export interface IFileRegistry {
+8 -2
packages/codegen-core/src/index.ts
··· 1 1 export type { IBiMap as BiMap } from './bimap/types'; 2 + export type { 3 + PlannedExport, 4 + PlannedImport, 5 + PlannedReexport, 6 + } from './bindings/plan'; 2 7 export type { IBinding as Binding } from './bindings/types'; 3 8 export { createBinding, mergeBindings } from './bindings/utils'; 4 9 export { debug } from './debug'; ··· 11 16 IFileIdentifier as FileIdentifier, 12 17 IFileIn as FileIn, 13 18 } from './files/types'; 19 + export type { INode as Node } from './nodes/node'; 14 20 export type { IOutput as Output } from './output'; 15 21 export { Project } from './project/project'; 16 22 export type { IProject } from './project/types'; 17 23 export type { IRenderer as Renderer } from './renderer/types'; 18 24 export { renderIds } from './renderer/utils'; 19 - export { Symbol } from './symbols/symbol'; 25 + export { AnalysisContext, Analyzer } from './symbols/analyzer'; 26 + export { isSymbol, Symbol, symbolBrand } from './symbols/symbol'; 20 27 export type { 21 28 ISymbolIdentifier as SymbolIdentifier, 22 29 ISymbolIn as SymbolIn, 23 30 } from './symbols/types'; 24 - export type { ISyntaxNode as SyntaxNode } from './syntax-node';
+13
packages/codegen-core/src/nodes/node.d.ts
··· 1 + import type { IAnalysisContext } from '../symbols/analyzer'; 2 + import type { Symbol } from '../symbols/symbol'; 3 + 4 + export interface INode { 5 + /** Perform semantic analysis. */ 6 + analyze(ctx: IAnalysisContext): void; 7 + /** Parent node in the constructed syntax tree. */ 8 + parent?: INode; 9 + /** The symbol associated with this node, if it defines a top‑level symbol. */ 10 + symbol?: Symbol; 11 + /** Brand used for renderer dispatch. */ 12 + readonly '~brand': symbol; 13 + }
+26
packages/codegen-core/src/nodes/registry.ts
··· 1 + import type { INode } from './node'; 2 + import type { INodeRegistry } from './types'; 3 + 4 + export class NodeRegistry implements INodeRegistry { 5 + private brands: Map<symbol, Array<INode>> = new Map(); 6 + private list: Array<INode> = []; 7 + 8 + add(node: INode): void { 9 + this.list.push(node); 10 + 11 + let group = this.brands.get(node['~brand']); 12 + if (!group) { 13 + group = []; 14 + this.brands.set(node['~brand'], group); 15 + } 16 + group.push(node); 17 + } 18 + 19 + all(): ReadonlyArray<INode> { 20 + return this.list; 21 + } 22 + 23 + byBrand(brand: symbol): ReadonlyArray<INode> { 24 + return this.brands.get(brand) ?? []; 25 + } 26 + }
+16
packages/codegen-core/src/nodes/types.d.ts
··· 1 + import type { INode } from './node'; 2 + 3 + export interface INodeRegistry { 4 + /** 5 + * Register a syntax node. 6 + */ 7 + add(node: INode): void; 8 + /** 9 + * All nodes in insertion order. 10 + */ 11 + all(): ReadonlyArray<INode>; 12 + /** 13 + * Nodes by backend brand, so planner doesn't need to filter repeatedly. 14 + */ 15 + byBrand(brand: symbol): ReadonlyArray<INode>; 16 + }
+77
packages/codegen-core/src/project/namespace.ts
··· 1 + import type { SymbolKind } from '../symbols/types'; 2 + 3 + /** 4 + * Returns true if two declarations of given kinds 5 + * are allowed to share the same identifier in TypeScript. 6 + */ 7 + export function canShareName(a: SymbolKind, b: SymbolKind): boolean { 8 + // same-kind always valid for interfaces (merging) 9 + if (a === 'interface' && b === 'interface') return true; 10 + 11 + // type vs interface merges 12 + if ( 13 + (a === 'interface' && b === 'type') || 14 + (a === 'type' && b === 'interface') 15 + ) { 16 + return false; // TypeScript does NOT merge type-alias with interface. 17 + } 18 + 19 + // type vs type = conflict 20 + if (a === 'type' && b === 'type') return false; 21 + 22 + // interface vs class = allowed (declare-merge) 23 + if ( 24 + (a === 'interface' && b === 'class') || 25 + (a === 'class' && b === 'interface') 26 + ) { 27 + return true; 28 + } 29 + 30 + // enum vs namespace = allowed (merges into value+type) 31 + if ( 32 + (a === 'enum' && b === 'namespace') || 33 + (a === 'namespace' && b === 'enum') 34 + ) { 35 + return true; 36 + } 37 + 38 + // class vs namespace = allowed 39 + if ( 40 + (a === 'class' && b === 'namespace') || 41 + (a === 'namespace' && b === 'class') 42 + ) { 43 + return true; 44 + } 45 + 46 + // namespace vs namespace = allowed (merging) 47 + if (a === 'namespace' && b === 'namespace') return true; 48 + 49 + // enum vs enum = conflict IF values conflict (TypeScript flags duplicates) 50 + if (a === 'enum' && b === 'enum') return false; 51 + 52 + // function and namespace merge (namespace can augment function) 53 + if ( 54 + (a === 'function' && b === 'namespace') || 55 + (a === 'namespace' && b === 'function') 56 + ) { 57 + return true; 58 + } 59 + 60 + // these collide with each other in the value namespace 61 + const valueKinds = new Set<SymbolKind>(['class', 'enum', 'function', 'var']); 62 + 63 + const aInValue = valueKinds.has(a); 64 + const bInValue = valueKinds.has(b); 65 + 66 + if (aInValue && bInValue) return false; 67 + 68 + // type-only declarations do not collide with value-only declarations 69 + const typeKinds = new Set<SymbolKind>(['interface', 'type']); 70 + const aInType = typeKinds.has(a); 71 + const bInType = typeKinds.has(b); 72 + 73 + // if one is type-only and the other is value-only, they do NOT collide 74 + if (aInType !== bInType) return true; 75 + 76 + return true; 77 + }
+191 -72
packages/codegen-core/src/project/project.ts
··· 2 2 3 3 import type { IProjectRenderMeta } from '../extensions'; 4 4 import { FileRegistry } from '../files/registry'; 5 - import type { IFileOut, IFileSelector } from '../files/types'; 5 + import type { IFileSelector } from '../files/types'; 6 + import { NodeRegistry } from '../nodes/registry'; 6 7 import type { IOutput } from '../output'; 7 8 import type { IRenderer } from '../renderer/types'; 9 + import { Analyzer } from '../symbols/analyzer'; 8 10 import { SymbolRegistry } from '../symbols/registry'; 9 11 import type { Symbol } from '../symbols/symbol'; 12 + import type { SymbolKind } from '../symbols/types'; 13 + import { canShareName } from './namespace'; 10 14 import type { IProject } from './types'; 11 15 12 16 const externalSourceSymbol = '@'; 13 17 14 18 export class Project implements IProject { 15 - private symbolIdToFileIds: Map<number, Set<number>> = new Map(); 16 - 19 + readonly analyzer = new Analyzer(); 17 20 readonly defaultFileName: string; 18 21 readonly files = new FileRegistry(); 19 22 readonly fileName?: (name: string) => string; 23 + readonly nodes = new NodeRegistry(); 20 24 readonly renderers: Record<string, IRenderer> = {}; 21 25 readonly root: string; 22 26 readonly symbols = new SymbolRegistry(); ··· 33 37 this.root = root; 34 38 } 35 39 36 - private getRenderer(file: IFileOut): IRenderer | undefined { 37 - return file.extension ? this.renderers[file.extension] : undefined; 40 + render(meta?: IProjectRenderMeta): ReadonlyArray<IOutput> { 41 + this.prepareFiles(); 42 + return this.renderFiles(meta); 38 43 } 39 44 40 45 private prepareFiles(): void { 41 - // TODO: infer extension from symbols 42 - const extension = '.ts'; 43 - for (const symbol of this.symbols.registered()) { 46 + this.assignFiles(); 47 + this.registerFiles(); 48 + this.resolveFinalSymbolNames(); 49 + this.planImports(); 50 + this.planExports(); 51 + } 52 + 53 + /** 54 + * Creates a file for every symbol so all files exist before planning. 55 + */ 56 + private assignFiles(): void { 57 + this.analyzer.analyze(this.nodes.all(), (ctx) => { 58 + const symbol = ctx.root; 44 59 const selector = this.symbolToFileSelector(symbol); 45 60 const file = this.files.reference(selector); 46 - file.symbols.body.push(symbol.id); 47 - // update symbol->files map 48 - const symbolIdToFileIds = 49 - this.symbolIdToFileIds.get(symbol.id) ?? new Set(); 50 - symbolIdToFileIds.add(file.id); 51 - this.symbolIdToFileIds.set(symbol.id, symbolIdToFileIds); 52 - // update re-exports 53 - if (symbol.exportFrom) { 54 - for (const exportFrom of symbol.exportFrom) { 55 - const exportSelector = [exportFrom]; 56 - const exportFile = this.files.reference(exportSelector); 57 - if (exportFile.id !== file.id) { 58 - exportFile.symbols.exports.push(symbol.id); 59 - } 61 + file.symbols.push(symbol); 62 + symbol.setFile(file); 63 + for (const exportFrom of symbol.exportFrom) { 64 + const selector = [exportFrom]; 65 + this.files.reference(selector); 66 + } 67 + for (const dependency of ctx.symbols) { 68 + if (dependency.external) { 69 + const selector = this.symbolToFileSelector(dependency); 70 + const file = this.files.reference(selector); 71 + dependency.setFile(file); 60 72 } 61 73 } 74 + }); 75 + } 76 + 77 + private planExports(): void { 78 + const seenByFile = new Map< 79 + number, 80 + Map<string, { dep: Symbol; kinds: Set<SymbolKind> }> 81 + >(); 82 + const sourceFile = new Map<number, number>(); 83 + 84 + this.analyzer.analyze(this.nodes.all(), (ctx) => { 85 + const symbol = ctx.root; 86 + const file = ctx.root.file; 87 + if (!file) return; 88 + 89 + for (const exportFrom of symbol.exportFrom) { 90 + const target = this.files.reference([exportFrom]); 91 + if (target.id === file.id) continue; 92 + 93 + let map = seenByFile.get(target.id); 94 + if (!map) { 95 + map = new Map(); 96 + seenByFile.set(target.id, map); 97 + } 98 + 99 + const dep = this.symbols.register({ 100 + exported: true, 101 + external: symbol.external, 102 + importKind: symbol.importKind, 103 + kind: symbol.kind, 104 + meta: symbol.meta, 105 + name: symbol.finalName, 106 + }); 107 + dep.setFile(target); 108 + sourceFile.set(dep.id, file.id); 109 + this.resolveSymbolFinalName(dep); 110 + 111 + let entry = map.get(dep.finalName); 112 + if (!entry) { 113 + entry = { dep, kinds: new Set() }; 114 + map.set(dep.finalName, entry); 115 + } 116 + entry.kinds.add(dep.kind); 117 + } 118 + }); 119 + 120 + for (const [fileId, map] of seenByFile) { 121 + const target = this.files.get(fileId)!; 122 + for (const [, entry] of map) { 123 + const symbol = entry.dep; 124 + target.reexports.push({ 125 + exportedName: symbol.finalName, 126 + from: sourceFile.get(symbol.id)!, 127 + importedName: symbol.name, 128 + isTypeOnly: [...entry.kinds].every( 129 + (kind) => kind === 'type' || kind === 'interface', 130 + ), 131 + kind: symbol.importKind, 132 + symbolId: symbol.id, 133 + }); 134 + } 62 135 } 136 + } 137 + 138 + private planImports(): void { 139 + const seenByFile = new Map<number, Set<string>>(); 140 + 141 + this.analyzer.analyze(this.nodes.all(), (ctx) => { 142 + const file = ctx.root.file; 143 + if (!file) return; 144 + 145 + let seen = seenByFile.get(file.id); 146 + if (!seen) { 147 + seen = new Set(); 148 + seenByFile.set(file.id, seen); 149 + } 150 + 151 + for (const dependency of ctx.symbols) { 152 + if (!dependency.file || dependency.file.id === file.id) continue; 153 + 154 + this.resolveSymbolFinalName(dependency); 155 + const from = dependency.file.id; 156 + const importedName = dependency.name; 157 + const localName = dependency.finalName; 158 + const isTypeOnly = false; // keep as-is for now 159 + const kind = dependency.importKind; 160 + 161 + const key = `${from}|${importedName}|${localName}|${kind}|${isTypeOnly}`; 162 + if (seen.has(key)) continue; 163 + seen.add(key); 164 + 165 + file.imports.push({ 166 + from, 167 + importedName, 168 + isTypeOnly, 169 + kind, 170 + localName, 171 + symbolId: dependency.id, 172 + }); 173 + } 174 + }); 175 + } 176 + 177 + /** 178 + * Registers all files. 179 + */ 180 + private registerFiles(): void { 63 181 for (const file of this.files.referenced()) { 64 - if (!file.selector) continue; 65 - if (file.selector[0] === externalSourceSymbol) { 66 - const filePath = file.selector[1]; 67 - if (!filePath) { 68 - this.files.register({ 69 - external: true, 70 - selector: file.selector, 71 - }); 72 - continue; 73 - } 74 - const extension = path.extname(filePath); 75 - if (!extension) { 76 - this.files.register({ 77 - external: true, 78 - path: filePath, 79 - selector: file.selector, 80 - }); 81 - continue; 82 - } 182 + const selector = file.selector; 183 + if (!selector) continue; 184 + if (selector[0] === externalSourceSymbol) { 185 + this.files.register({ 186 + external: true, 187 + path: selector[1], 188 + selector, 189 + }); 190 + } else { 191 + const dirs = file.selector.slice(0, -1); 192 + let name = file.selector[file.selector.length - 1]!; 193 + name = this.fileName?.(name) || name; 194 + const extension = '.ts'; 83 195 this.files.register({ 84 196 extension, 85 - external: true, 86 - path: filePath, 197 + name, 198 + path: path.resolve(this.root, ...dirs, `${name}${extension}`), 87 199 selector: file.selector, 88 200 }); 89 - continue; 90 201 } 91 - const dirs = file.selector.slice(0, -1); 92 - let name = file.selector[file.selector.length - 1]!; 93 - name = this.fileName?.(name) || name; 94 - this.files.register({ 95 - extension, 96 - name, 97 - path: path.resolve(this.root, ...dirs, `${name}${extension}`), 98 - selector: file.selector, 99 - }); 100 202 } 101 - 102 - // TODO: track symbol dependencies and inject imports into files 103 - // based on symbol references so the render step can just render 104 203 } 105 204 106 - render(meta?: IProjectRenderMeta): ReadonlyArray<IOutput> { 107 - this.prepareFiles(); 205 + private renderFiles(meta?: IProjectRenderMeta): ReadonlyArray<IOutput> { 108 206 const files: Map<number, IOutput> = new Map(); 109 207 for (const file of this.files.registered()) { 110 208 if (file.external || !file.path) continue; 111 - const renderer = this.getRenderer(file); 209 + const renderer = file.extension 210 + ? this.renderers[file.extension] 211 + : undefined; 112 212 if (!renderer) continue; 113 213 files.set(file.id, { 114 - content: renderer.renderSymbols(file, this, meta), 214 + content: renderer.render(file, this, meta), 115 215 path: file.path, 116 216 }); 117 217 } 118 - for (const [fileId, value] of files.entries()) { 119 - const file = this.files.get(fileId)!; 120 - const renderer = this.getRenderer(file)!; 121 - const content = renderer.renderFile(value.content, file, this, meta); 122 - if (content) { 123 - files.set(file.id, { ...value, content }); 124 - } else { 125 - files.delete(file.id); 218 + return Array.from(files.values()); 219 + } 220 + 221 + private resolveSymbolFinalName(symbol: Symbol): void { 222 + const file = symbol.file; 223 + if (symbol._finalName || !file) return; 224 + 225 + let name = symbol.name; 226 + let index = 1; 227 + while (file.reservedNames.has(name)) { 228 + const scopes = file.reservedNames.get(name)!; 229 + let exit = true; 230 + for (const kind of scopes) { 231 + if (!canShareName(symbol.kind, kind)) { 232 + exit = false; 233 + index = index + 1; 234 + name = `${name}${index}`; 235 + break; 236 + } 126 237 } 238 + if (exit) break; 127 239 } 128 - return Array.from(files.values()); 240 + // TODO: ensure valid names 241 + symbol.setFinalName(name); 242 + const scopes = file.reservedNames.get(name) ?? new Set<SymbolKind>(); 243 + scopes.add(symbol.kind); 244 + file.reservedNames.set(name, scopes); 129 245 } 130 246 131 - symbolIdToFiles(symbolId: number): ReadonlyArray<IFileOut> { 132 - const fileIds = this.symbolIdToFileIds.get(symbolId); 133 - return Array.from(fileIds ?? []).map((fileId) => this.files.get(fileId)!); 247 + private resolveFinalSymbolNames(): void { 248 + for (const file of this.files.registered()) { 249 + for (const symbol of file.symbols) { 250 + this.resolveSymbolFinalName(symbol); 251 + } 252 + } 134 253 } 135 254 136 255 private symbolToFileSelector(symbol: Symbol): IFileSelector {
+9 -24
packages/codegen-core/src/project/types.d.ts
··· 1 1 import type { IProjectRenderMeta } from '../extensions'; 2 - import type { IFileOut, IFileRegistry } from '../files/types'; 2 + import type { IFileRegistry } from '../files/types'; 3 + import type { INodeRegistry } from '../nodes/types'; 3 4 import type { IOutput } from '../output'; 4 5 import type { IRenderer } from '../renderer/types'; 5 6 import type { ISymbolRegistry } from '../symbols/types'; 6 7 7 8 /** 8 - * Represents a code generation project consisting of multiple codegen files. 9 + * Represents a code generation project consisting of codegen files. 10 + * 9 11 * Manages imports, symbols, and output generation across the project. 10 12 */ 11 13 export interface IProject { ··· 22 24 * @returns The transformed file name. 23 25 */ 24 26 readonly fileName?: (name: string) => string; 25 - /** 26 - * Centralized file registry for the project. 27 - */ 27 + /** Centralized file registry for the project. */ 28 28 readonly files: IFileRegistry; 29 + /** Centralized node registry for the project. */ 30 + readonly nodes: INodeRegistry; 29 31 /** 30 32 * Produces output representations for all files in the project. 31 33 * ··· 45 47 * } 46 48 */ 47 49 readonly renderers: Record<string, IRenderer>; 48 - /** 49 - * The absolute path to the root folder of the project. 50 - */ 50 + /** The absolute path to the root folder of the project. */ 51 51 readonly root: string; 52 - /** 53 - * Retrieves files that include symbol ID. The first file is the one 54 - * where the symbol is declared, the rest are files that re-export it. 55 - * 56 - * @param symbolId The symbol ID to find. 57 - * @returns An array of files containing the symbol. 58 - * @example 59 - * const files = project.symbolIdToFiles(31); 60 - * for (const file of files) { 61 - * console.log(file.path); 62 - * } 63 - */ 64 - symbolIdToFiles(symbolId: number): ReadonlyArray<IFileOut>; 65 - /** 66 - * Centralized symbol registry for the project. 67 - */ 52 + /** Centralized symbol registry for the project. */ 68 53 readonly symbols: ISymbolRegistry; 69 54 }
+9
packages/codegen-core/src/renderer/types.d.ts
··· 4 4 5 5 export interface IRenderer { 6 6 /** 7 + * Renders the given file. 8 + * 9 + * @param file The file to render. 10 + * @param project The project the file belongs to. 11 + * @param meta Arbitrary metadata. 12 + * @returns Rendered content. 13 + */ 14 + render(file: IFileOut, project: IProject, meta?: IProjectRenderMeta): string; 15 + /** 7 16 * Renders content with replaced symbols. 8 17 * 9 18 * @param content Content to render.
+61
packages/codegen-core/src/symbols/analyzer.ts
··· 1 + import { debug } from '../debug'; 2 + import type { INode } from '../nodes/node'; 3 + import type { Symbol } from './symbol'; 4 + 5 + export interface IAnalysisContext { 6 + /** Register a dependency on another symbol. */ 7 + addDependency(symbol: Symbol): void; 8 + /** Local names declared by nodes within the analyzed symbol. */ 9 + localNames: Set<string>; 10 + /** Root symbol for the current top‑level analysis pass. */ 11 + root: Symbol; 12 + /** Collected symbol references discovered during analysis. */ 13 + symbols: Set<Symbol>; 14 + } 15 + 16 + export class AnalysisContext implements IAnalysisContext { 17 + localNames: Set<string> = new Set(); 18 + root: Symbol; 19 + symbols: Set<Symbol> = new Set(); 20 + 21 + constructor(symbol: Symbol) { 22 + this.root = symbol; 23 + } 24 + 25 + addDependency(symbol: Symbol): void { 26 + if (this.root !== symbol) { 27 + this.symbols.add(symbol); 28 + } 29 + } 30 + } 31 + 32 + export class Analyzer { 33 + private nodeCache = new WeakMap<INode, AnalysisContext>(); 34 + 35 + analyzeNode(node: INode): AnalysisContext { 36 + const cached = this.nodeCache.get(node); 37 + if (cached) return cached; 38 + 39 + if (!node.symbol) { 40 + const message = `Analyzer: cannot analyze node "${node}" without a defining symbol.`; 41 + debug(message, 'analyzer'); 42 + throw new Error(message); 43 + } 44 + 45 + const ctx = new AnalysisContext(node.symbol); 46 + node.analyze(ctx); 47 + 48 + this.nodeCache.set(node, ctx); 49 + return ctx; 50 + } 51 + 52 + analyze( 53 + nodes: ReadonlyArray<INode>, 54 + callback?: (ctx: AnalysisContext) => void, 55 + ): void { 56 + for (const node of nodes) { 57 + const ctx = this.analyzeNode(node); 58 + callback?.(ctx); 59 + } 60 + } 61 + }
+3
packages/codegen-core/src/symbols/registry.ts
··· 119 119 } 120 120 } 121 121 122 + /** 123 + * @deprecated 124 + */ 122 125 setValue(symbolId: SymbolId, value: unknown): Map<SymbolId, unknown> { 123 126 return this.nodes.set(symbolId, value); 124 127 }
+22 -13
packages/codegen-core/src/symbols/symbol.ts
··· 1 1 import { debug } from '../debug'; 2 2 import type { ISymbolMeta } from '../extensions'; 3 3 import type { IFileOut } from '../files/types'; 4 + import type { INode } from '../nodes/node'; 4 5 import { wrapId } from '../renderer/utils'; 5 - import type { ISyntaxNode } from '../syntax-node'; 6 6 import type { ISymbolIn, SymbolImportKind, SymbolKind } from './types'; 7 + 8 + export const symbolBrand = globalThis.Symbol('symbol'); 7 9 8 10 export class Symbol { 9 11 /** ··· 49 51 private _file?: IFileOut; 50 52 /** 51 53 * The alias-resolved, conflict-free emitted name. 52 - * 53 - * @private 54 54 */ 55 - private _finalName?: string; 55 + _finalName?: string; 56 56 /** 57 57 * Custom strategy to determine file output path. 58 58 * ··· 84 84 */ 85 85 private _name: string; 86 86 /** 87 - * Root node that defines this symbol. 87 + * Node that defines this symbol. 88 88 * 89 89 * @private 90 90 */ 91 - private _rootNode?: ISyntaxNode; 91 + private _node?: INode; 92 92 93 + /** Brand used for identifying symbols. */ 94 + readonly '~brand': symbol = symbolBrand; 93 95 /** 94 96 * Globally unique, stable symbol ID. 95 97 */ ··· 208 210 } 209 211 210 212 /** 211 - * Read‑only accessor for the defining DSL root node. 213 + * Read‑only accessor for the defining node. 212 214 */ 213 - get rootNode(): ISyntaxNode | undefined { 214 - return this.canonical._rootNode; 215 + get node(): INode | undefined { 216 + return this.canonical._node; 215 217 } 216 218 217 219 /** ··· 315 317 * 316 318 * This may only be set once. 317 319 */ 318 - setRootNode(node: ISyntaxNode): void { 320 + setNode(node: INode): void { 319 321 this.assertCanonical(); 320 - if (this._rootNode && this._rootNode !== node) { 321 - throw new Error('Symbol is already bound to a different root node.'); 322 + if (this._node && this._node !== node) { 323 + throw new Error('Symbol is already bound to a different node.'); 322 324 } 323 - this._rootNode = node; 325 + this._node = node; 326 + node.symbol = this; 324 327 } 325 328 326 329 /** ··· 349 352 } 350 353 } 351 354 } 355 + 356 + export function isSymbol(value: unknown): value is Symbol { 357 + if (!value || typeof value !== 'object' || Array.isArray(value)) return false; 358 + const obj = value as { '~brand'?: unknown }; 359 + return obj['~brand'] === symbolBrand && Object.hasOwn(obj, '~brand'); 360 + }
-20
packages/codegen-core/src/syntax-node.d.ts
··· 1 - import type { Symbol } from './symbols/symbol'; 2 - 3 - export interface ISyntaxNode { 4 - /** 5 - * Collect symbols referenced directly by this node into the provided accumulator. 6 - */ 7 - collectSymbols(out: Set<Symbol>): void; 8 - /** 9 - * Return local names introduced by this node. 10 - */ 11 - getLocalNames(): Iterable<string>; 12 - /** 13 - * Rewrite local identifiers based on a rename map. 14 - */ 15 - rewriteIdentifiers(map: Map<string, string>): void; 16 - /** 17 - * Walk this node and its children with a visitor. 18 - */ 19 - traverse(visitor: (node: ISyntaxNode) => void): void; 20 - }
+112 -47
packages/openapi-ts/src/generate/renderer.ts
··· 4 4 Binding, 5 5 File, 6 6 IProject, 7 + PlannedImport, 8 + PlannedReexport, 9 + Project, 7 10 ProjectRenderMeta, 8 11 Renderer, 9 12 Symbol, ··· 12 15 import ts from 'typescript'; 13 16 14 17 import { ensureValidIdentifier } from '~/openApi/shared/utils/identifier'; 15 - import { $, TsDsl } from '~/ts-dsl'; 18 + import { $, isTsDsl } from '~/ts-dsl'; 16 19 17 20 const printer = ts.createPrinter({ 18 21 newLine: ts.NewLineKind.LineFeed, ··· 93 96 ]); 94 97 95 98 export class TypeScriptRenderer implements Renderer { 99 + render(file: File, project: Project, meta?: ProjectRenderMeta): string { 100 + let output = ''; 101 + const header = '// This file is auto-generated by @hey-api/openapi-ts'; 102 + output += `${header}\n`; 103 + 104 + let outputImports = ''; 105 + for (const plan of file.imports) { 106 + outputImports += nodeToString( 107 + this.renderImport(plan, file, project, meta), 108 + ); 109 + outputImports += '\n'; 110 + } 111 + output = `${output}${output && outputImports ? '\n' : ''}${outputImports}`; 112 + 113 + let outputSymbols = ''; 114 + for (const symbol of file.symbols) { 115 + if (outputSymbols) outputSymbols += '\n'; 116 + const node = symbol.node; 117 + if (isTsDsl(node)) { 118 + outputSymbols += nodeToString(node.$render()); 119 + } 120 + outputSymbols += '\n'; 121 + } 122 + output = `${output}${output && outputSymbols ? '\n' : ''}${outputSymbols}`; 123 + 124 + let outputReexports = ''; 125 + for (const plan of file.reexports) { 126 + if (!outputReexports && outputSymbols) outputReexports += '\n'; 127 + outputReexports += nodeToString( 128 + this.renderExport(plan, file, project, meta), 129 + ); 130 + outputReexports += '\n'; 131 + } 132 + output = `${output}${output && outputReexports ? '\n' : ''}${outputReexports}`; 133 + 134 + return output; 135 + } 136 + 137 + private renderExport( 138 + plan: PlannedReexport, 139 + file: File, 140 + project: Project, 141 + meta?: ProjectRenderMeta, 142 + ): ts.ExportDeclaration { 143 + const modulePath = this.getBindingPath( 144 + file, 145 + project.files.get(plan.from)!, 146 + meta, 147 + ); 148 + const specifier = ts.factory.createExportSpecifier( 149 + false, 150 + plan.importedName !== plan.exportedName 151 + ? $.id(plan.importedName).$render() 152 + : undefined, 153 + $.id(plan.exportedName).$render(), 154 + ); 155 + return ts.factory.createExportDeclaration( 156 + undefined, 157 + plan.isTypeOnly, 158 + ts.factory.createNamedExports([specifier]), 159 + $.literal(modulePath).$render(), 160 + ); 161 + } 162 + 163 + private renderImport( 164 + plan: PlannedImport, 165 + file: File, 166 + project: Project, 167 + meta?: ProjectRenderMeta, 168 + ): ts.ImportDeclaration { 169 + const modulePath = this.getBindingPath( 170 + file, 171 + project.files.get(plan.from)!, 172 + meta, 173 + ); 174 + const localName = $.id(plan.localName).$render(); 175 + const specifier = ts.factory.createImportSpecifier( 176 + false, 177 + plan.importedName !== plan.localName 178 + ? $.id(plan.importedName).$render() 179 + : undefined, 180 + localName, 181 + ); 182 + const importClause = ts.factory.createImportClause( 183 + false, 184 + plan.kind === 'default' ? localName : undefined, 185 + plan.kind === 'namespace' 186 + ? ts.factory.createNamespaceImport(localName) 187 + : ts.factory.createNamedImports([specifier]), 188 + ); 189 + return ts.factory.createImportDeclaration( 190 + undefined, 191 + importClause, 192 + $.literal(modulePath).$render(), 193 + ); 194 + } 195 + 96 196 renderFile( 97 197 symbolsAndExports: string, 98 198 file: File, ··· 117 217 return `${output}${symbolsAndExports}`; 118 218 } 119 219 120 - renderSymbols( 121 - file: File, 122 - project: IProject, 123 - meta?: ProjectRenderMeta, 124 - ): string { 220 + renderSymbols(file: File, project: IProject): string { 125 221 const exports: Map<string, Binding> = new Map(); 126 222 let output = ''; 127 - const bodyLines = this.getBodyLines(file, project); 223 + const bodyLines: Array<string> = []; 128 224 output += `${bodyLines.join('\n\n')}${bodyLines.length ? '\n' : ''}`; 129 - output = renderIds(output, (symbolId) => { 130 - if (!file.symbols.body.includes(symbolId)) return; 131 - const symbol = project.symbols.get(symbolId); 132 - return this.replacerFn({ file, project, symbol }); 133 - }); 134 - for (const symbolId of file.symbols.exports) { 135 - const symbol = project.symbols.get(symbolId); 136 - if (symbol) { 137 - this.addBinding({ bindings: exports, file, meta, project, symbol }); 138 - } 139 - } 140 225 // cast everything into namespace exports for now 141 226 for (const binding of exports.values()) { 142 227 binding.namespaceBinding = true; ··· 168 253 return; 169 254 } 170 255 171 - const [symbolFile] = project.symbolIdToFiles(symbol.id); 256 + const symbolFile: File | undefined = undefined; 172 257 if (!symbolFile || file === symbolFile) return; 173 258 174 259 const modulePath = this.getBindingPath(file, symbolFile, meta); ··· 220 305 return relativePath; 221 306 } 222 307 223 - private getBodyLine(value: unknown, lines: Array<string>): void { 224 - if (value instanceof TsDsl) { 225 - const node = value.$render(); 226 - lines.push(nodeToString(node)); 227 - } else if (typeof value === 'string') { 228 - lines.push(value); 229 - } else if (value instanceof Array) { 230 - for (const node of value) { 231 - this.getBodyLine(node, lines); 232 - } 233 - } else if (value !== undefined && value !== null) { 234 - lines.push(nodeToString(value as any)); 235 - } 236 - } 237 - 238 - private getBodyLines(file: File, project: IProject): Array<string> { 239 - const lines: Array<string> = []; 240 - for (const symbolId of file.symbols.body) { 241 - this.getBodyLine(project.symbols.getValue(symbolId), lines); 242 - } 243 - return lines; 244 - } 245 - 246 308 private getExportLines( 247 309 bindings: Map<string, Binding>, 248 310 file: File, ··· 283 345 finalName = renderIds(finalName, (symbolId) => { 284 346 const symbol = project.symbols.get(symbolId); 285 347 const name = this.replacerFn({ file, project, symbol }); 286 - const [symbolFile] = project.symbolIdToFiles(symbolId); 348 + const symbolFile: File | undefined = undefined; 287 349 const sourceName = symbolFile 288 - ? symbolFile.resolvedNames.get(symbolId) 350 + ? // @ts-expect-error 351 + symbolFile.resolvedNames.get(symbolId) 289 352 : undefined; 290 353 if (sourceName && sourceName !== name) { 291 354 // handle only simple imports for now ··· 421 484 finalName = renderIds(finalName, (symbolId) => { 422 485 const symbol = project.symbols.get(symbolId); 423 486 const name = this.replacerFn({ file, project, symbol }); 424 - const [symbolFile] = project.symbolIdToFiles(symbolId); 487 + const symbolFile: File | undefined = undefined; 425 488 const sourceName = symbolFile 426 - ? symbolFile.resolvedNames.get(symbolId) 489 + ? // @ts-expect-error 490 + symbolFile.resolvedNames.get(symbolId) 427 491 : undefined; 428 492 if (sourceName && sourceName !== name) { 429 493 // handle only simple imports for now ··· 547 611 const cached = file.resolvedNames.get(symbol.id); 548 612 if (cached) return cached; 549 613 if (!symbol.name) return; 550 - const [symbolFile] = project.symbolIdToFiles(symbol.id); 614 + const symbolFile: File | undefined = undefined; 615 + // @ts-expect-error 551 616 const symbolFileResolvedName = symbolFile?.resolvedNames.get(symbol.id); 552 617 const name = this.getUniqueName({ 553 618 base: ensureValidIdentifier(symbolFileResolvedName ?? symbol.name),
+13 -13
packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts
··· 156 156 ), 157 157 ) 158 158 .do(...currentClass.nodes); 159 - plugin.setSymbolValue(symbolClass, node); 159 + plugin.addNode(node); 160 160 161 161 generatedClasses.add(currentClass.className); 162 162 }; ··· 195 195 plugin, 196 196 symbol, 197 197 }); 198 - plugin.setSymbolValue(symbol, node); 198 + plugin.addNode(node); 199 199 }, 200 200 { 201 201 order: 'declarations', ··· 217 217 const optionsClient = $('options') 218 218 .attr('client') 219 219 .optional() 220 - .$if(symbolClient, (c, s) => c.coalesce(s.placeholder)); 220 + .$if(symbolClient, (c, s) => c.coalesce(s)); 221 221 222 222 return optionsClient 223 223 .attr('requestOptions') ··· 259 259 role: 'data', 260 260 tool: 'typescript', 261 261 }); 262 - const dataType = symbolDataType?.placeholder || 'unknown'; 263 262 264 263 return $.method(methodName) 265 264 .public() 266 265 .$if(createOperationComment(operation), (c, v) => c.doc(v)) 267 266 .param('options', (p) => 268 - p 269 - .required(isRequiredOptions) 270 - .type($.type(symbolOptions).generic(dataType).generic('ThrowOnError')), 267 + p.required(isRequiredOptions).type( 268 + $.type(symbolOptions) 269 + .generic(symbolDataType ?? 'unknown') 270 + .generic('ThrowOnError'), 271 + ), 271 272 ) 272 273 .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 273 274 .returns($.type(symbolHttpRequest).generic('unknown')) ··· 310 311 role: 'data', 311 312 tool: 'typescript', 312 313 }); 313 - const dataType = symbolDataType?.placeholder || 'unknown'; 314 314 315 315 return $.const(symbol) 316 316 .export() ··· 318 318 .assign( 319 319 $.func() 320 320 .param('options', (p) => 321 - p 322 - .required(isRequiredOptions) 323 - .type( 324 - $.type(symbolOptions).generic(dataType).generic('ThrowOnError'), 325 - ), 321 + p.required(isRequiredOptions).type( 322 + $.type(symbolOptions) 323 + .generic(symbolDataType ?? 'unknown') 324 + .generic('ThrowOnError'), 325 + ), 326 326 ) 327 327 .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 328 328 .returns($.type(symbolHttpRequest).generic('unknown'))
+29 -23
packages/openapi-ts/src/plugins/@angular/common/httpResources.ts
··· 153 153 ), 154 154 ) 155 155 .do(...currentClass.nodes); 156 - plugin.setSymbolValue(symbolClass, node); 156 + plugin.addNode(node); 157 157 158 158 generatedClasses.add(currentClass.className); 159 159 }; ··· 185 185 plugin, 186 186 symbol, 187 187 }); 188 - plugin.setSymbolValue(symbol, node); 188 + plugin.addNode(node); 189 189 }, 190 190 { 191 191 order: 'declarations', ··· 213 213 resourceId: operation.id, 214 214 role: 'response', 215 215 }); 216 - const responseType = symbolResponseType?.placeholder || 'unknown'; 217 216 218 217 if (plugin.config.httpRequests.asClass) { 219 218 // For class-based request methods, use inject and class hierarchy ··· 239 238 category: 'external', 240 239 resource: '@angular/core.inject', 241 240 }); 242 - let methodAccess: ReturnType<typeof $.attr | typeof $.call> = $( 243 - symbolInject.placeholder, 244 - ).call(symbolClass.placeholder); 241 + let methodAccess: ReturnType<typeof $.attr | typeof $.call> = 242 + $(symbolInject).call(symbolClass); 245 243 246 244 // Navigate through the class hierarchy 247 245 for (let i = 1; i < firstEntry.path.length; i++) { ··· 260 258 plugin.config.httpRequests.methodNameBuilder(operation), 261 259 ); 262 260 263 - return $(symbolHttpResource.placeholder) 261 + return $(symbolHttpResource) 264 262 .call( 265 263 $.func().do( 266 264 $.const('opts').assign( ··· 275 273 ), 276 274 ), 277 275 ) 278 - .generic(responseType); 276 + .generic(symbolResponseType ?? 'unknown'); 279 277 } 280 278 } else { 281 279 const symbolHttpRequest = plugin.referenceSymbol({ ··· 286 284 tool: 'angular', 287 285 }); 288 286 289 - return $(symbolHttpResource.placeholder) 287 + return $(symbolHttpResource) 290 288 .call( 291 289 $.func().do( 292 290 $.const('opts').assign( ··· 296 294 ), 297 295 $.return( 298 296 $.ternary('opts') 299 - .do($(symbolHttpRequest.placeholder).call('opts')) 297 + .do($(symbolHttpRequest).call('opts')) 300 298 .otherwise($.id('undefined')), 301 299 ), 302 300 ), 303 301 ) 304 - .generic(responseType); 302 + .generic(symbolResponseType ?? 'unknown'); 305 303 } 306 304 307 305 // Fallback return (should not reach here) 308 - return $(symbolHttpResource.placeholder).call( 306 + return $(symbolHttpResource).call( 309 307 $.func() 310 308 .do($.return($.id('undefined'))) 311 - .generic(responseType), 309 + .generic(symbolResponseType ?? 'unknown'), 312 310 ); 313 311 }; 314 312 ··· 336 334 role: 'data', 337 335 tool: 'typescript', 338 336 }); 339 - const dataType = symbolDataType?.placeholder || 'unknown'; 340 337 341 338 return $.method(methodName) 342 339 .public() 343 340 .$if(createOperationComment(operation), (c, v) => c.doc(v)) 344 341 .param('options', (p) => 345 - p 346 - .required(isRequiredOptions) 347 - .type( 348 - `() => ${symbolOptions.placeholder}<${dataType}, ThrowOnError> | undefined`, 342 + p.required(isRequiredOptions).type( 343 + $.type.func().returns( 344 + $.type.or( 345 + $.type(symbolOptions) 346 + .generic(symbolDataType ?? 'unknown') 347 + .generic('ThrowOnError'), 348 + $.type('undefined'), 349 + ), 349 350 ), 351 + ), 350 352 ) 351 353 .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 352 354 .do( ··· 383 385 role: 'data', 384 386 tool: 'typescript', 385 387 }); 386 - const dataType = symbolDataType?.placeholder || 'unknown'; 387 388 388 389 return $.const(symbol) 389 390 .export() ··· 391 392 .assign( 392 393 $.func() 393 394 .param('options', (p) => 394 - p 395 - .required(isRequiredOptions) 396 - .type( 397 - `() => ${symbolOptions.placeholder}<${dataType}, ThrowOnError> | undefined`, 395 + p.required(isRequiredOptions).type( 396 + $.type.func().returns( 397 + $.type.or( 398 + $.type(symbolOptions) 399 + .generic(symbolDataType ?? 'unknown') 400 + .generic('ThrowOnError'), 401 + $.type('undefined'), 402 + ), 398 403 ), 404 + ), 399 405 ) 400 406 .generic('ThrowOnError', (g) => g.extends('boolean').default(false)) 401 407 .do(
+5 -5
packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts
··· 79 79 } 80 80 81 81 const createConfigParameters = [ 82 - $(symbolCreateConfig.placeholder) 82 + $(symbolCreateConfig) 83 83 .call(defaultVals.hasProps() ? defaultVals : undefined) 84 - .generic(symbolClientOptions.placeholder), 84 + .generic(symbolClientOptions), 85 85 ]; 86 86 87 87 const symbolClient = plugin.registerSymbol({ ··· 93 93 const statement = $.const(symbolClient) 94 94 .export() 95 95 .assign( 96 - $(symbolCreateClient.placeholder).$if( 96 + $(symbolCreateClient).$if( 97 97 symbolCreateClientConfig, 98 - (c, s) => c.call($(s.placeholder).call(...createConfigParameters)), 98 + (c, s) => c.call($(s).call(...createConfigParameters)), 99 99 (c) => c.call(...createConfigParameters), 100 100 ), 101 101 ); 102 - plugin.setSymbolValue(symbolClient, statement); 102 + plugin.addNode(statement); 103 103 };
+4 -8
packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts
··· 38 38 'to ensure your client always has the correct values.', 39 39 ]) 40 40 .generic('T', (g) => 41 - g 42 - .extends(symbolDefaultClientOptions.placeholder) 43 - .default(symbolClientOptions.placeholder), 41 + g.extends(symbolDefaultClientOptions).default(symbolClientOptions), 44 42 ) 45 43 .type( 46 44 $.type ··· 50 48 .optional() 51 49 .type( 52 50 $.type(symbolConfig).generic( 53 - $.type.and(symbolDefaultClientOptions.placeholder, 'T'), 51 + $.type.and(symbolDefaultClientOptions, 'T'), 54 52 ), 55 53 ), 56 54 ) 57 55 .returns( 58 56 $.type(symbolConfig).generic( 59 57 $.type.and( 60 - $.type('Required').generic( 61 - symbolDefaultClientOptions.placeholder, 62 - ), 58 + $.type('Required').generic(symbolDefaultClientOptions), 63 59 'T', 64 60 ), 65 61 ), 66 62 ), 67 63 ); 68 - plugin.setSymbolValue(symbolCreateClientConfig, typeCreateClientConfig); 64 + plugin.addNode(typeCreateClientConfig); 69 65 };
+3 -3
packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts
··· 391 391 }), 392 392 ).as('const'), 393 393 ); 394 - plugin.setSymbolValue(symbol, statement); 394 + plugin.addNode(statement); 395 395 } 396 396 }; 397 397 ··· 431 431 }), 432 432 ).as('const'), 433 433 ); 434 - plugin.setSymbolValue(symbol, statement); 434 + plugin.addNode(statement); 435 435 } 436 436 }; 437 437 ··· 471 471 }), 472 472 ).as('const'), 473 473 ); 474 - plugin.setSymbolValue(symbol, statement); 474 + plugin.addNode(statement); 475 475 } 476 476 }; 477 477
+15 -19
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/class.ts
··· 122 122 resource: 'client.Client', 123 123 }); 124 124 return $.class(symbol) 125 - .field('client', (f) => f.protected().type(symbolClient.placeholder)) 125 + .field('client', (f) => f.protected().type(symbolClient)) 126 126 .newline() 127 127 .init((i) => 128 128 i ··· 133 133 $.type 134 134 .object() 135 135 .prop('client', (p) => 136 - p.optional(optionalClient).type(symbolClient.placeholder), 136 + p.optional(optionalClient).type(symbolClient), 137 137 ), 138 138 ), 139 139 ) ··· 144 144 $('args') 145 145 .attr('client') 146 146 .optional(optionalClient) 147 - .$if(optionalClient, (a) => a.coalesce(symClient!.placeholder)), 147 + .$if(optionalClient, (a) => a.coalesce(symClient!)), 148 148 ), 149 149 ), 150 150 ); ··· 266 266 plugin.referenceSymbol({ 267 267 category: 'external', 268 268 resource: 'client.Composable', 269 - }).placeholder, 269 + }), 270 270 ) 271 271 .default($.type.literal('$fetch')), 272 272 ) 273 273 .generic(nuxtTypeDefault, (t) => 274 - t.$if(symbolResponse, (t, s) => 275 - t.extends(s.placeholder).default(s.placeholder), 276 - ), 274 + t.$if(symbolResponse, (t, s) => t.extends(s).default(s)), 277 275 ), 278 276 (m) => 279 277 m.generic('ThrowOnError', (t) => ··· 374 372 .static(!plugin.config.instance) 375 373 .assign( 376 374 plugin.config.instance 377 - ? $.new(refChildClass.placeholder).args( 375 + ? $.new(refChildClass).args( 378 376 $.object().prop('client', $('this').attr('client')), 379 377 ) 380 - : $(refChildClass.placeholder), 378 + : $(refChildClass), 381 379 ), 382 380 ) 383 381 : $.getter(memberName, (g) => ··· 386 384 .do( 387 385 $.return( 388 386 plugin.config.instance 389 - ? $.new(refChildClass.placeholder).args( 387 + ? $.new(refChildClass).args( 390 388 $.object().prop('client', $('this').attr('client')), 391 389 ) 392 - : refChildClass.placeholder, 390 + : refChildClass, 393 391 ), 394 392 ), 395 393 ); ··· 410 408 plugin, 411 409 symbol: symbolHeyApiClient, 412 410 }); 413 - plugin.setSymbolValue(symbolHeyApiClient, node); 411 + plugin.addNode(node); 414 412 } 415 413 416 414 const symbol = plugin.registerSymbol({ ··· 439 437 $.type 440 438 .object() 441 439 .prop('client', (p) => 442 - p.required(isClientRequired).type(symbolClient.placeholder), 440 + p.required(isClientRequired).type(symbolClient), 443 441 ) 444 442 .prop('key', (p) => p.optional().type('string')), 445 443 ), 446 444 ) 447 445 .do( 448 446 $('super').call('args'), 449 - $(symbol.placeholder) 447 + $(symbol) 450 448 .attr(registryName) 451 449 .attr('set') 452 450 .call('this', $('args').attr('key').required(isClientRequired)), ··· 464 462 sdkName: symbol.placeholder, 465 463 symbol: symbolHeyApiRegistry, 466 464 }); 467 - plugin.setSymbolValue(symbolHeyApiRegistry, node); 465 + plugin.addNode(node); 468 466 const registryNode = $.field(registryName, (f) => 469 467 f 470 468 .public() 471 469 .static() 472 470 .readonly() 473 - .assign( 474 - $.new(symbolHeyApiRegistry.placeholder).generic(symbol.placeholder), 475 - ), 471 + .assign($.new(symbolHeyApiRegistry).generic(symbol)), 476 472 ); 477 473 currentClass.nodes.unshift(registryNode, $.newline()); 478 474 } ··· 490 486 ), 491 487 ) 492 488 .do(...currentClass.nodes); 493 - plugin.setSymbolValue(symbol, node); 489 + plugin.addNode(node); 494 490 }; 495 491 496 492 for (const sdkClass of sdkClasses.values()) {
+3 -3
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/functions.ts
··· 97 97 plugin.referenceSymbol({ 98 98 category: 'external', 99 99 resource: 'client.Composable', 100 - }).placeholder, 100 + }), 101 101 ) 102 102 .default($.type.literal('$fetch')), 103 103 ) 104 104 .generic(nuxtTypeDefault, (g) => 105 105 g.$if( 106 106 symbolResponse, 107 - (t, s) => t.extends(s.placeholder).default(s.placeholder), 107 + (t, s) => t.extends(s).default(s), 108 108 (t) => t.default('undefined'), 109 109 ), 110 110 ), ··· 121 121 ) 122 122 .do(...statements), 123 123 ); 124 - plugin.setSymbolValue(symbol, node); 124 + plugin.addNode(node); 125 125 }, 126 126 { 127 127 order: 'declarations',
+24 -23
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/operation.ts
··· 184 184 resourceId: operation.id, 185 185 role: 'response', 186 186 }); 187 - const dataType = isDataAllowed 188 - ? symbolDataType?.placeholder || 'unknown' 189 - : 'never'; 190 - const responseType = symbolResponseType?.placeholder || 'unknown'; 191 - return `${symbolOptions.placeholder}<${nuxtTypeComposable}, ${dataType}, ${responseType}, ${nuxtTypeDefault}>`; 187 + return $.type(symbolOptions) 188 + .generic(nuxtTypeComposable) 189 + .generic(isDataAllowed ? (symbolDataType ?? 'unknown') : 'never') 190 + .generic(symbolResponseType ?? 'unknown') 191 + .generic(nuxtTypeDefault); 192 192 } 193 193 194 194 // TODO: refactor this to be more generic, works for now 195 195 if (throwOnError) { 196 - const dataType = isDataAllowed 197 - ? symbolDataType?.placeholder || 'unknown' 198 - : 'never'; 199 - return `${symbolOptions.placeholder}<${dataType}, ${throwOnError}>`; 196 + return $.type(symbolOptions) 197 + .generic(isDataAllowed ? (symbolDataType ?? 'unknown') : 'never') 198 + .generic(throwOnError); 200 199 } 201 - const dataType = isDataAllowed ? symbolDataType?.placeholder : 'never'; 202 - return dataType 203 - ? `${symbolOptions.placeholder}<${dataType}>` 204 - : symbolOptions.placeholder; 200 + return $.type(symbolOptions).$if(!isDataAllowed || symbolDataType, (t) => 201 + t.generic(isDataAllowed ? symbolDataType! : 'never'), 202 + ); 205 203 }; 206 204 207 205 type OperationParameters = { ··· 359 357 resourceId: operation.id, 360 358 role: isNuxtClient ? 'response' : 'responses', 361 359 }); 362 - const responseType = symbolResponseType?.placeholder || 'unknown'; 363 360 364 361 const symbolErrorType = plugin.querySymbol({ 365 362 category: 'type', ··· 367 364 resourceId: operation.id, 368 365 role: isNuxtClient ? 'error' : 'errors', 369 366 }); 370 - const errorType = symbolErrorType?.placeholder || 'unknown'; 371 367 372 368 // TODO: transform parameters 373 369 // const query = { ··· 394 390 category: 'external', 395 391 resource: 'client.formDataBodySerializer', 396 392 }); 397 - reqOptions.spread(symbol.placeholder); 393 + reqOptions.spread(symbol); 398 394 break; 399 395 } 400 396 case 'json': ··· 410 406 category: 'external', 411 407 resource: 'client.urlSearchParamsBodySerializer', 412 408 }); 413 - reqOptions.spread(symbol.placeholder); 409 + reqOptions.spread(symbol); 414 410 break; 415 411 } 416 412 } ··· 489 485 }; 490 486 if (plugin.isSymbolRegistered(query)) { 491 487 const ref = plugin.referenceSymbol(query); 492 - reqOptions.prop('responseTransformer', $(ref.placeholder)); 488 + reqOptions.prop('responseTransformer', $(ref)); 493 489 } 494 490 } 495 491 ··· 563 559 }); 564 560 statements.push( 565 561 $.const('params').assign( 566 - $(symbol.placeholder).call($.array(...args), $.array(...config)), 562 + $(symbol).call($.array(...args), $.array(...config)), 567 563 ), 568 564 ); 569 565 reqOptions.spread('params'); ··· 604 600 if (plugin.config.instance) { 605 601 clientExpression = optionsClient.coalesce($('this').attr('client')); 606 602 } else if (symbolClient) { 607 - clientExpression = optionsClient.coalesce(symbolClient.placeholder); 603 + clientExpression = optionsClient.coalesce(symbolClient); 608 604 } else { 609 605 clientExpression = optionsClient; 610 606 } ··· 623 619 (f) => 624 620 f 625 621 .generic(nuxtTypeComposable) 626 - .generic(`${responseType} | ${nuxtTypeDefault}`) 627 - .generic(errorType) 622 + .generic( 623 + $.type.or(symbolResponseType ?? 'unknown', nuxtTypeDefault), 624 + ) 625 + .generic(symbolErrorType ?? 'unknown') 628 626 .generic(nuxtTypeDefault), 629 627 (f) => 630 - f.generic(responseType).generic(errorType).generic('ThrowOnError'), 628 + f 629 + .generic(symbolResponseType ?? 'unknown') 630 + .generic(symbolErrorType ?? 'unknown') 631 + .generic('ThrowOnError'), 631 632 ) 632 633 .$if(plugin.config.responseStyle === 'data', (f) => 633 634 f.generic($.type.literal(plugin.config.responseStyle)),
+5 -9
packages/openapi-ts/src/plugins/@hey-api/sdk/shared/typeOptions.ts
··· 56 56 plugin.referenceSymbol({ 57 57 category: 'external', 58 58 resource: 'client.Composable', 59 - }).placeholder, 59 + }), 60 60 ) 61 61 .default($.type.literal('$fetch')), 62 62 ) 63 63 .generic('TData', (g) => 64 - g 65 - .extends(symbolTDataShape.placeholder) 66 - .default(symbolTDataShape.placeholder), 64 + g.extends(symbolTDataShape).default(symbolTDataShape), 67 65 ) 68 66 .generic(nuxtTypeResponse, (g) => g.default('unknown')) 69 67 .generic(nuxtTypeDefault, (g) => g.default('undefined')), 70 68 (t) => 71 69 t 72 70 .generic('TData', (g) => 73 - g 74 - .extends(symbolTDataShape.placeholder) 75 - .default(symbolTDataShape.placeholder), 71 + g.extends(symbolTDataShape).default(symbolTDataShape), 76 72 ) 77 73 .generic('ThrowOnError', (g) => 78 74 g.extends('boolean').default('boolean'), ··· 100 96 'custom client.', 101 97 ]) 102 98 .required(!plugin.config.client && !plugin.config.instance) 103 - .type(symbolClient.placeholder), 99 + .type(symbolClient), 104 100 ) 105 101 .prop('meta', (p) => 106 102 p ··· 113 109 ), 114 110 ), 115 111 ); 116 - plugin.setSymbolValue(symbolOptions, typeOptions); 112 + plugin.addNode(typeOptions); 117 113 };
+7 -8
packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts
··· 4 4 import { createOperationKey, operationResponsesMap } from '~/ir/operation'; 5 5 import type { IR } from '~/ir/types'; 6 6 import { buildName } from '~/openApi/shared/utils/name'; 7 - import { $ } from '~/ts-dsl'; 8 - import { TsDsl } from '~/ts-dsl'; 7 + import { $, isTsDsl } from '~/ts-dsl'; 9 8 import { refToName } from '~/utils/ref'; 10 9 11 10 import type { HeyApiTransformersPlugin } from './types'; ··· 28 27 }: { 29 28 node: ts.Expression | ts.Statement | Expr; 30 29 }) => { 31 - if (node instanceof TsDsl) { 30 + if (isTsDsl(node)) { 32 31 node = node.$render(); 33 32 } 34 33 return node.kind === ts.SyntaxKind.ReturnStatement; ··· 112 111 }); 113 112 114 113 if (nodes.length) { 115 - const node = $.const(symbol.placeholder).assign( 114 + const node = $.const(symbol).assign( 116 115 // TODO: parser - add types, generate types without transforms 117 116 $.func() 118 117 .param(dataVariableName, (p) => p.type('any')) 119 118 .do(...ensureStatements(nodes)), 120 119 ); 121 - plugin.setSymbolValue(symbol, node); 120 + plugin.addNode(node); 122 121 } 123 122 } finally { 124 123 buildingSymbols.delete(symbol.id); ··· 131 130 const currentValue = plugin.gen.symbols.getValue(symbol.id); 132 131 if (currentValue || buildingSymbols.has(symbol.id)) { 133 132 const ref = plugin.referenceSymbol(query); 134 - const callExpression = $(ref.placeholder).call(dataExpression); 133 + const callExpression = $(ref).call(dataExpression); 135 134 136 135 if (dataExpression) { 137 136 // In a map callback, the item needs to be returned, not just the transformation result ··· 358 357 $.func() 359 358 .async() 360 359 .param(dataVariableName, (p) => p.type('any')) 361 - .returns($.type('Promise').generic(symbolResponse.placeholder)) 360 + .returns($.type('Promise').generic(symbolResponse)) 362 361 .do(...ensureStatements(nodes)), 363 362 ); 364 - plugin.setSymbolValue(symbol, value); 363 + plugin.addNode(value); 365 364 }, 366 365 { 367 366 order: 'declarations',
+1 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/clientOptions.ts
··· 60 60 p.type($.type.or(...types)), 61 61 ), 62 62 ); 63 - plugin.setSymbolValue(symbolClientOptions, node); 63 + plugin.addNode(node); 64 64 };
+4 -4
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/export.ts
··· 133 133 ), 134 134 ).as('const'), 135 135 ); 136 - plugin.setSymbolValue(symbolObject, objectNode); 136 + plugin.addNode(objectNode); 137 137 138 138 const symbol = plugin.registerSymbol({ 139 139 meta: { ··· 154 154 .export() 155 155 .$if(createSchemaComment(schema), (t, v) => t.doc(v)) 156 156 .type($.type(symbol).idx($.type(symbol).typeof().keyof()).typeof()); 157 - plugin.setSymbolValue(symbol, node); 157 + plugin.addNode(node); 158 158 return; 159 159 } else if ( 160 160 plugin.config.enums.mode === 'typescript' || ··· 190 190 .value($.fromValue(item.schema.const)), 191 191 ), 192 192 ); 193 - plugin.setSymbolValue(symbol, enumNode); 193 + plugin.addNode(enumNode); 194 194 return; 195 195 } 196 196 } ··· 215 215 .export() 216 216 .$if(createSchemaComment(schema), (t, v) => t.doc(v)) 217 217 .type(type); 218 - plugin.setSymbolValue(symbol, node); 218 + plugin.addNode(node); 219 219 };
+5 -5
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/operation.ts
··· 145 145 state, 146 146 }), 147 147 ); 148 - plugin.setSymbolValue(symbol, node); 148 + plugin.addNode(node); 149 149 }; 150 150 151 151 export const operationToType = ({ ··· 186 186 state, 187 187 }), 188 188 ); 189 - plugin.setSymbolValue(symbolErrors, node); 189 + plugin.addNode(node); 190 190 191 191 if (error) { 192 192 const symbol = plugin.registerSymbol({ ··· 211 211 .alias(symbol) 212 212 .export() 213 213 .type($.type(symbolErrors).idx($.type(symbolErrors).keyof())); 214 - plugin.setSymbolValue(symbol, node); 214 + plugin.addNode(node); 215 215 } 216 216 } 217 217 ··· 241 241 state, 242 242 }), 243 243 ); 244 - plugin.setSymbolValue(symbolResponses, node); 244 + plugin.addNode(node); 245 245 246 246 if (response) { 247 247 const symbol = plugin.registerSymbol({ ··· 266 266 .alias(symbol) 267 267 .export() 268 268 .type($.type(symbolResponses).idx($.type(symbolResponses).keyof())); 269 - plugin.setSymbolValue(symbol, node); 269 + plugin.addNode(node); 270 270 } 271 271 } 272 272 };
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/shared/webhook.ts
··· 54 54 state, 55 55 }), 56 56 ); 57 - plugin.setSymbolValue(symbolWebhookPayload, node); 57 + plugin.addNode(node); 58 58 59 59 data.properties.body = { symbolRef: symbolWebhookPayload }; 60 60 dataRequired.push('body'); ··· 98 98 state, 99 99 }), 100 100 ); 101 - plugin.setSymbolValue(symbolWebhookRequest, node); 101 + plugin.addNode(node); 102 102 103 103 return symbolWebhookRequest; 104 104 };
+1 -1
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts
··· 186 186 .alias(symbolWebhooks) 187 187 .export() 188 188 .type($.type.or(...webhooks)); 189 - plugin.setSymbolValue(symbolWebhooks, node); 189 + plugin.addNode(node); 190 190 } 191 191 };
+2 -2
packages/openapi-ts/src/plugins/@hey-api/typescript/v1/toAst/string.ts
··· 60 60 .type( 61 61 $.type.template().add($.type('T')).add('_').add($.type('string')), 62 62 ); 63 - plugin.setSymbolValue(symbolTypeId, nodeTypeId); 63 + plugin.addNode(nodeTypeId); 64 64 } 65 65 66 66 const symbolTypeId = plugin.referenceSymbol(queryTypeId); ··· 75 75 .alias(symbolTypeName) 76 76 .export() 77 77 .type($.type(symbolTypeId).generic($.type.literal(type))); 78 - plugin.setSymbolValue(symbolTypeName, node); 78 + plugin.addNode(node); 79 79 } 80 80 const symbol = plugin.referenceSymbol(query); 81 81 return $.type(symbol);
+1 -1
packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts
··· 90 90 .returns(mutationType) 91 91 .do($.return(mutationOpts)), 92 92 ); 93 - plugin.setSymbolValue(symbolMutationOptions, statement); 93 + plugin.addNode(statement); 94 94 };
+11 -19
packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts
··· 70 70 .param('options', (p) => p.optional().type(TOptionsType)) 71 71 .param('tags', (p) => p.optional().type('ReadonlyArray<string>')) 72 72 .returns($.type.tuple(returnType)) 73 - .generic(TOptionsType, (g) => g.extends(symbolOptions.placeholder)) 73 + .generic(TOptionsType, (g) => g.extends(symbolOptions)) 74 74 .do( 75 75 $.const('params') 76 76 .type(returnType) ··· 86 86 $.if('tags').do( 87 87 $('params') 88 88 .attr('tags') 89 - .assign($('tags').as('unknown').as(symbolJsonValue.placeholder)), 89 + .assign($('tags').as('unknown').as(symbolJsonValue)), 90 90 ), 91 91 $.if($('options').attr('body').optional().neq($.id('undefined'))).do( 92 92 $.const('normalizedBody').assign( 93 - $(symbolSerializeQueryValue.placeholder).call( 94 - $('options').attr('body'), 95 - ), 93 + $(symbolSerializeQueryValue).call($('options').attr('body')), 96 94 ), 97 95 $.if($('normalizedBody').neq($.id('undefined'))).do( 98 96 $('params').attr('body').assign('normalizedBody'), ··· 103 101 ), 104 102 $.if($('options').attr('query').optional().neq($.id('undefined'))).do( 105 103 $.const('normalizedQuery').assign( 106 - $(symbolSerializeQueryValue.placeholder).call( 107 - $('options').attr('query'), 108 - ), 104 + $(symbolSerializeQueryValue).call($('options').attr('query')), 109 105 ), 110 106 $.if($('normalizedQuery').neq($.id('undefined'))).do( 111 107 $('params').attr('query').assign('normalizedQuery'), ··· 114 110 $.return($.array($('params'))), 115 111 ), 116 112 ); 117 - plugin.setSymbolValue(symbolCreateQueryKey, fn); 113 + plugin.addNode(fn); 118 114 }; 119 115 120 116 const createQueryKeyLiteral = ({ ··· 137 133 resource: 'createQueryKey', 138 134 tool: plugin.name, 139 135 }); 140 - const createQueryKeyCallExpression = $(symbolCreateQueryKey.placeholder).call( 136 + const createQueryKeyCallExpression = $(symbolCreateQueryKey).call( 141 137 $.literal(id), 142 138 'options', 143 139 tagsExpression, ··· 180 176 .object() 181 177 .prop('_id', (p) => p.type('string')) 182 178 .prop(getClientBaseUrlKey(plugin.context.config), (p) => 183 - p.optional().type(symbolJsonValue.placeholder), 179 + p.optional().type(symbolJsonValue), 184 180 ) 185 - .prop('body', (p) => p.optional().type(symbolJsonValue.placeholder)) 186 - .prop('query', (p) => 187 - p.optional().type(symbolJsonValue.placeholder), 188 - ) 189 - .prop('tags', (p) => 190 - p.optional().type(symbolJsonValue.placeholder), 191 - ), 181 + .prop('body', (p) => p.optional().type(symbolJsonValue)) 182 + .prop('query', (p) => p.optional().type(symbolJsonValue)) 183 + .prop('tags', (p) => p.optional().type(symbolJsonValue)), 192 184 ), 193 185 ), 194 186 ); 195 - plugin.setSymbolValue(symbolQueryKeyType, queryKeyType); 187 + plugin.addNode(queryKeyType); 196 188 }; 197 189 198 190 export const queryKeyStatement = ({
+5 -5
packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts
··· 61 61 plugin, 62 62 symbol: symbolQueryKey, 63 63 }); 64 - plugin.setSymbolValue(symbolQueryKey, node); 65 - keyExpression = $(symbolQueryKey.placeholder).call(optionsParamName); 64 + plugin.addNode(node); 65 + keyExpression = $(symbolQueryKey).call(optionsParamName); 66 66 } else { 67 67 const symbolCreateQueryKey = plugin.referenceSymbol({ 68 68 category: 'utility', ··· 78 78 ) { 79 79 tagsExpr = $.array(...operation.tags.map((t) => $.literal(t))); 80 80 } 81 - keyExpression = $(symbolCreateQueryKey.placeholder).call( 81 + keyExpression = $(symbolCreateQueryKey).call( 82 82 $.literal(operation.id), 83 83 optionsParamName, 84 84 tagsExpr, ··· 146 146 c.doc(v), 147 147 ) 148 148 .assign( 149 - $(symbolDefineQueryOptions.placeholder).call( 149 + $(symbolDefineQueryOptions).call( 150 150 $.func() 151 151 .param(optionsParamName, (p) => 152 152 p.required(isRequiredOptions).type(strippedTypeData), ··· 154 154 .do($.return(queryOpts)), 155 155 ), 156 156 ); 157 - plugin.setSymbolValue(symbolQueryOptionsFn, statement); 157 + plugin.addNode(statement); 158 158 };
+5 -5
packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts
··· 57 57 .param('options', (p) => p.optional().type(TOptionsType)) 58 58 .param('infinite', (p) => p.optional().type('boolean')) 59 59 .param('tags', (p) => p.optional().type('ReadonlyArray<string>')) 60 - .generic(TOptionsType, (g) => g.extends(symbolOptions.placeholder)) 60 + .generic(TOptionsType, (g) => g.extends(symbolOptions)) 61 61 .returns($.type.tuple(returnType)) 62 62 .do( 63 63 $.const('params') ··· 88 88 $.return($.array().element($('params'))), 89 89 ), 90 90 ); 91 - plugin.setSymbolValue(symbolCreateQueryKey, fn); 91 + plugin.addNode(fn); 92 92 }; 93 93 94 94 const createQueryKeyLiteral = ({ ··· 114 114 resource: 'createQueryKey', 115 115 tool: plugin.name, 116 116 }); 117 - const createQueryKeyCallExpression = $(symbolCreateQueryKey.placeholder).call( 117 + const createQueryKeyCallExpression = $(symbolCreateQueryKey).call( 118 118 $.literal(id), 119 119 'options', 120 120 isInfinite || tagsArray ? $.literal(Boolean(isInfinite)) : undefined, ··· 140 140 const queryKeyType = $.type 141 141 .alias(symbolQueryKeyType) 142 142 .export() 143 - .generic(TOptionsType, (g) => g.extends(symbolOptions.placeholder)) 143 + .generic(TOptionsType, (g) => g.extends(symbolOptions)) 144 144 .type( 145 145 $.type.tuple( 146 146 $.type.and( ··· 155 155 ), 156 156 ), 157 157 ); 158 - plugin.setSymbolValue(symbolQueryKeyType, queryKeyType); 158 + plugin.addNode(queryKeyType); 159 159 }; 160 160 161 161 export const queryKeyStatement = ({
+6 -9
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/infiniteQueryOptions.ts
··· 90 90 $.return($('params').as('unknown').as($('page').typeofType())), 91 91 ), 92 92 ); 93 - plugin.setSymbolValue(symbolCreateInfiniteParams, fn); 93 + plugin.addNode(fn); 94 94 }; 95 95 96 96 export const createInfiniteQueryOptions = ({ ··· 181 181 symbol: symbolInfiniteQueryKey, 182 182 typeQueryKey, 183 183 }); 184 - plugin.setSymbolValue(symbolInfiniteQueryKey, node); 184 + plugin.addNode(node); 185 185 186 186 const awaitSdkFn = $(queryFn) 187 187 .call( ··· 216 216 ), 217 217 ), 218 218 $.const('params').assign( 219 - $(symbolCreateInfiniteParams.placeholder).call('queryKey', 'page'), 219 + $(symbolCreateInfiniteParams).call('queryKey', 'page'), 220 220 ), 221 221 ]; 222 222 ··· 245 245 .param('options', (p) => p.required(isRequiredOptions).type(typeData)) 246 246 .do( 247 247 $.return( 248 - $(symbolInfiniteQueryOptions.placeholder) 248 + $(symbolInfiniteQueryOptions) 249 249 .call( 250 250 $.object() 251 251 .pretty() ··· 257 257 .param((p) => p.object('pageParam', 'queryKey', 'signal')) 258 258 .do(...statements), 259 259 ) 260 - .prop( 261 - 'queryKey', 262 - $(symbolInfiniteQueryKey.placeholder).call('options'), 263 - ) 260 + .prop('queryKey', $(symbolInfiniteQueryKey).call('options')) 264 261 .$if( 265 262 handleMeta(plugin, operation, 'infiniteQueryOptions'), 266 263 (o, v) => o.prop('meta', v), ··· 277 274 ), 278 275 ), 279 276 ); 280 - plugin.setSymbolValue(symbolInfiniteQueryOptionsFn, statement); 277 + plugin.addNode(statement); 281 278 };
+1 -1
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/mutationOptions.ts
··· 85 85 $(mutationOptionsFn).return(), 86 86 ), 87 87 ); 88 - plugin.setSymbolValue(symbolMutationOptions, statement); 88 + plugin.addNode(statement); 89 89 };
+5 -5
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/queryOptions.ts
··· 65 65 plugin, 66 66 symbol: symbolQueryKey, 67 67 }); 68 - plugin.setSymbolValue(symbolQueryKey, node); 68 + plugin.addNode(node); 69 69 70 70 const typeData = useTypeData({ operation, plugin }); 71 71 const typeError = useTypeError({ operation, plugin }); ··· 100 100 .param((p) => p.object('queryKey', 'signal')) 101 101 .do(...statements), 102 102 ) 103 - .prop('queryKey', $(symbolQueryKey.placeholder).call(optionsParamName)) 103 + .prop('queryKey', $(symbolQueryKey).call(optionsParamName)) 104 104 .$if(handleMeta(plugin, operation, 'queryOptions'), (o, v) => 105 105 o.prop('meta', v), 106 106 ); ··· 131 131 p.required(isRequiredOptions).type(typeData), 132 132 ) 133 133 .do( 134 - $(symbolQueryOptions.placeholder) 134 + $(symbolQueryOptions) 135 135 .call(queryOptionsObj) 136 136 .generics( 137 137 typeResponse, 138 138 typeError, 139 139 typeResponse, 140 - $(symbolQueryKey.placeholder).returnType(), 140 + $(symbolQueryKey).returnType(), 141 141 ) 142 142 .return(), 143 143 ), 144 144 ); 145 - plugin.setSymbolValue(symbolQueryOptionsFn, statement); 145 + plugin.addNode(statement); 146 146 };
+3 -3
packages/openapi-ts/src/plugins/@tanstack/query-core/v5/useQuery.ts
··· 63 63 p.required(isRequiredOptions).type(typeData), 64 64 ) 65 65 .do( 66 - $(symbolUseQuery.placeholder) 67 - .call($(symbolQueryOptionsFn.placeholder).call(optionsParamName)) 66 + $(symbolUseQuery) 67 + .call($(symbolQueryOptionsFn).call(optionsParamName)) 68 68 .return(), 69 69 ), 70 70 ); 71 - plugin.setSymbolValue(symbolUseQueryFn, statement); 71 + plugin.addNode(statement); 72 72 };
+4 -6
packages/openapi-ts/src/plugins/arktype/shared/export.ts
··· 34 34 // .type( 35 35 // ast.typeName 36 36 // ? (tsc.propertyAccessExpression({ 37 - // expression: z.placeholder, 37 + // expression: z, 38 38 // name: ast.typeName, 39 39 // }) as unknown as ts.TypeNode) 40 40 // : undefined, 41 41 // ) 42 - .assign( 43 - $(type.placeholder).call(ast.def ? $.literal(ast.def) : ast.expression), 44 - ); 45 - plugin.setSymbolValue(symbol, statement); 42 + .assign($(type).call(ast.def ? $.literal(ast.def) : ast.expression)); 43 + plugin.addNode(statement); 46 44 47 45 if (typeInferSymbol) { 48 46 const inferType = $.type 49 47 .alias(typeInferSymbol) 50 48 .export() 51 49 .type($.type(symbol).attr(identifiers.type.infer).typeofType()); 52 - plugin.setSymbolValue(typeInferSymbol, inferType); 50 + plugin.addNode(inferType); 53 51 } 54 52 };
+2 -14
packages/openapi-ts/src/plugins/arktype/v2/api.ts
··· 33 33 return $.func() 34 34 .async() 35 35 .param(dataParameterName) 36 - .do( 37 - $(symbol.placeholder) 38 - .attr('parseAsync') 39 - .call(dataParameterName) 40 - .await() 41 - .return(), 42 - ); 36 + .do($(symbol).attr('parseAsync').call(dataParameterName).await().return()); 43 37 }; 44 38 45 39 export const createResponseValidatorV2 = ({ ··· 59 53 return $.func() 60 54 .async() 61 55 .param(dataParameterName) 62 - .do( 63 - $(symbol.placeholder) 64 - .attr('parseAsync') 65 - .call(dataParameterName) 66 - .await() 67 - .return(), 68 - ); 56 + .do($(symbol).attr('parseAsync').call(dataParameterName).await().return()); 69 57 };
+8 -8
packages/openapi-ts/src/plugins/arktype/v2/plugin.ts
··· 43 43 }; 44 44 const refSymbol = plugin.referenceSymbol(query); 45 45 if (plugin.isSymbolRegistered(query)) { 46 - const ref = $(refSymbol.placeholder); 46 + const ref = $(refSymbol); 47 47 ast.expression = ref; 48 48 } else { 49 - // expression: z.placeholder, 49 + // expression: z, 50 50 // name: identifiers.lazy, 51 51 const lazyExpression = $('TODO') 52 52 .attr('TODO') 53 - .call($.func().returns('any').do($.return(refSymbol.placeholder))); 53 + .call($.func().returns('any').do($.return(refSymbol))); 54 54 ast.expression = lazyExpression; 55 55 ast.hasLazyExpression = true; 56 56 state.hasLazyExpression.value = true; ··· 74 74 // }), 75 75 // parameters: [ 76 76 // tsc.propertyAccessExpression({ 77 - // expression: z.placeholder, 77 + // expression: z, 78 78 // name: identifiers.globalRegistry, 79 79 // }), 80 80 // tsc.objectExpression({ ··· 113 113 // ) { 114 114 // ast.expression = tsc.callExpression({ 115 115 // functionName: tsc.propertyAccessExpression({ 116 - // expression: z.placeholder, 116 + // expression: z, 117 117 // name: identifiers.intersection, 118 118 // }), 119 119 // parameters: itemSchemas.map((schema) => schema.expression), ··· 130 130 // schema.hasCircularReference 131 131 // ? tsc.callExpression({ 132 132 // functionName: tsc.propertyAccessExpression({ 133 - // expression: z.placeholder, 133 + // expression: z, 134 134 // name: identifiers.lazy, 135 135 // }), 136 136 // parameters: [ ··· 151 151 // } else { 152 152 // ast.expression = tsc.callExpression({ 153 153 // functionName: tsc.propertyAccessExpression({ 154 - // expression: z.placeholder, 154 + // expression: z, 155 155 // name: identifiers.union, 156 156 // }), 157 157 // parameters: [ ··· 203 203 // if (optional) { 204 204 // ast.expression = tsc.callExpression({ 205 205 // functionName: tsc.propertyAccessExpression({ 206 - // expression: z.placeholder, 206 + // expression: z, 207 207 // name: identifiers.optional, 208 208 // }), 209 209 // parameters: [ast.expression],
+1 -1
packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts
··· 90 90 resource: 'arktype.type', 91 91 }); 92 92 93 - const expression = $(type.placeholder).call( 93 + const expression = $(type).call( 94 94 $.object() 95 95 .prop('name', $.literal('string')) 96 96 .prop('platform', $.literal("'android' | 'ios'"))
+1 -1
packages/openapi-ts/src/plugins/fastify/plugin.ts
··· 161 161 ); 162 162 163 163 const node = $.type.alias(symbolRouteHandlers).export().type(type); 164 - plugin.setSymbolValue(symbolRouteHandlers, node); 164 + plugin.addNode(node); 165 165 };
+8
packages/openapi-ts/src/plugins/shared/utils/instance.ts
··· 2 2 3 3 import type { 4 4 IProject, 5 + Node, 5 6 Symbol, 6 7 SymbolIdentifier, 7 8 SymbolIn, ··· 103 104 this.handler = props.handler; 104 105 this.name = props.name; 105 106 this.package = props.context.package; 107 + } 108 + 109 + addNode(node: Node): void { 110 + return this.gen.nodes.add(node); 106 111 } 107 112 108 113 /** ··· 345 350 } 346 351 } 347 352 353 + /** 354 + * @deprecated use addNode 355 + */ 348 356 setSymbolValue(symbol: Symbol, value: unknown): void { 349 357 for (const hook of this.eventHooks['symbol:setValue:before']) { 350 358 hook({ plugin: this, symbol, value });
+2 -2
packages/openapi-ts/src/plugins/swr/v2/useSwr.ts
··· 54 54 ) 55 55 .assign( 56 56 $.func().do( 57 - $(symbolUseSwr.placeholder) 57 + $(symbolUseSwr) 58 58 .call( 59 59 $.literal(operation.path), 60 60 $.func() ··· 64 64 .return(), 65 65 ), 66 66 ); 67 - plugin.setSymbolValue(symbolUseQueryFn, statement); 67 + plugin.addNode(statement); 68 68 };
+1 -1
packages/openapi-ts/src/plugins/valibot/shared/export.ts
··· 33 33 c.type($.type(v).attr(ast.typeName || identifiers.types.GenericSchema)), 34 34 ) 35 35 .assign(pipesToAst({ pipes: ast.pipes, plugin })); 36 - plugin.setSymbolValue(symbol, statement); 36 + plugin.addNode(statement); 37 37 };
+1 -1
packages/openapi-ts/src/plugins/valibot/shared/pipesToAst.ts
··· 18 18 category: 'external', 19 19 resource: 'valibot.v', 20 20 }); 21 - return $(v.placeholder) 21 + return $(v) 22 22 .attr(identifiers.methods.pipe) 23 23 .call(...pipes); 24 24 };
+1 -5
packages/openapi-ts/src/plugins/valibot/v1/api.ts
··· 8 8 schema, 9 9 v, 10 10 }: ValidatorResolverArgs): ReturnType<typeof $.return> => 11 - $(v.placeholder) 12 - .attr(identifiers.async.parseAsync) 13 - .call(schema.placeholder, 'data') 14 - .await() 15 - .return(); 11 + $(v).attr(identifiers.async.parseAsync).call(schema, 'data').await().return(); 16 12 17 13 export const createRequestValidatorV1 = ({ 18 14 operation,
+9 -11
packages/openapi-ts/src/plugins/valibot/v1/plugin.ts
··· 50 50 }; 51 51 const refSymbol = plugin.referenceSymbol(query); 52 52 if (plugin.isSymbolRegistered(query)) { 53 - const ref = $(refSymbol.placeholder); 53 + const ref = $(refSymbol); 54 54 ast.pipes.push(ref); 55 55 } else { 56 - const lazyExpression = $(v.placeholder) 56 + const lazyExpression = $(v) 57 57 .attr(identifiers.schemas.lazy) 58 - .call($.func().do($(refSymbol.placeholder).return())); 58 + .call($.func().do($(refSymbol).return())); 59 59 ast.pipes.push(lazyExpression); 60 60 state.hasLazyExpression.value = true; 61 61 } ··· 69 69 ast.pipes.push(typeAst.expression); 70 70 71 71 if (plugin.config.metadata && schema.description) { 72 - const expression = $(v.placeholder) 72 + const expression = $(v) 73 73 .attr(identifiers.actions.metadata) 74 74 .call($.object().prop('description', $.literal(schema.description))); 75 75 ast.pipes.push(expression); ··· 91 91 }); 92 92 93 93 if (schema.logicalOperator === 'and') { 94 - const intersectExpression = $(v.placeholder) 94 + const intersectExpression = $(v) 95 95 .attr(identifiers.schemas.intersect) 96 96 .call($.array(...itemsAst)); 97 97 ast.pipes.push(intersectExpression); 98 98 } else { 99 - const unionExpression = $(v.placeholder) 99 + const unionExpression = $(v) 100 100 .attr(identifiers.schemas.union) 101 101 .call($.array(...itemsAst)); 102 102 ast.pipes.push(unionExpression); ··· 120 120 121 121 if (ast.pipes.length) { 122 122 if (schema.accessScope === 'read') { 123 - const readonlyExpression = $(v.placeholder) 124 - .attr(identifiers.actions.readonly) 125 - .call(); 123 + const readonlyExpression = $(v).attr(identifiers.actions.readonly).call(); 126 124 ast.pipes.push(readonlyExpression); 127 125 } 128 126 ··· 132 130 const isBigInt = schema.type === 'integer' && schema.format === 'int64'; 133 131 callParameter = numberParameter({ isBigInt, value: schema.default }); 134 132 ast.pipes = [ 135 - $(v.placeholder) 133 + $(v) 136 134 .attr(identifiers.schemas.optional) 137 135 .call(pipesToAst({ pipes: ast.pipes, plugin }), callParameter), 138 136 ]; ··· 140 138 141 139 if (optional && !callParameter) { 142 140 ast.pipes = [ 143 - $(v.placeholder) 141 + $(v) 144 142 .attr(identifiers.schemas.optional) 145 143 .call(pipesToAst({ pipes: ast.pipes, plugin })), 146 144 ];
+4 -4
packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts
··· 24 24 category: 'external', 25 25 resource: 'valibot.v', 26 26 }); 27 - const functionName = $(v.placeholder).attr(identifiers.schemas.array); 27 + const functionName = $(v).attr(identifiers.schemas.array); 28 28 29 29 if (!schema.items) { 30 30 const expression = functionName.call( ··· 84 84 } 85 85 86 86 if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { 87 - const expression = $(v.placeholder) 87 + const expression = $(v) 88 88 .attr(identifiers.actions.length) 89 89 .call($.fromValue(schema.minItems)); 90 90 result.pipes.push(expression); 91 91 } else { 92 92 if (schema.minItems !== undefined) { 93 - const expression = $(v.placeholder) 93 + const expression = $(v) 94 94 .attr(identifiers.actions.minLength) 95 95 .call($.fromValue(schema.minItems)); 96 96 result.pipes.push(expression); 97 97 } 98 98 99 99 if (schema.maxItems !== undefined) { 100 - const expression = $(v.placeholder) 100 + const expression = $(v) 101 101 .attr(identifiers.actions.maxLength) 102 102 .call($.fromValue(schema.maxItems)); 103 103 result.pipes.push(expression);
+2 -4
packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts
··· 20 20 21 21 if (typeof schema.const === 'boolean') { 22 22 pipes.push( 23 - $(v.placeholder) 24 - .attr(identifiers.schemas.literal) 25 - .call($.literal(schema.const)), 23 + $(v).attr(identifiers.schemas.literal).call($.literal(schema.const)), 26 24 ); 27 25 return pipesToAst({ pipes, plugin }); 28 26 } 29 27 30 - pipes.push($(v.placeholder).attr(identifiers.schemas.boolean).call()); 28 + pipes.push($(v).attr(identifiers.schemas.boolean).call()); 31 29 return pipesToAst({ pipes, plugin }); 32 30 };
+2 -2
packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts
··· 40 40 resource: 'valibot.v', 41 41 }); 42 42 43 - let resultExpression = $(v.placeholder) 43 + let resultExpression = $(v) 44 44 .attr(identifiers.schemas.picklist) 45 45 .call($.array(...enumMembers)); 46 46 47 47 if (isNullable) { 48 - resultExpression = $(v.placeholder) 48 + resultExpression = $(v) 49 49 .attr(identifiers.schemas.nullable) 50 50 .call(resultExpression); 51 51 }
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts
··· 13 13 category: 'external', 14 14 resource: 'valibot.v', 15 15 }); 16 - const expression = $(v.placeholder).attr(identifiers.schemas.never).call(); 16 + const expression = $(v).attr(identifiers.schemas.never).call(); 17 17 return expression; 18 18 };
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts
··· 13 13 category: 'external', 14 14 resource: 'valibot.v', 15 15 }); 16 - const expression = $(v.placeholder).attr(identifiers.schemas.null).call(); 16 + const expression = $(v).attr(identifiers.schemas.null).call(); 17 17 return expression; 18 18 };
+14 -18
packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts
··· 68 68 literalValue = $.fromValue(constValue); 69 69 } 70 70 71 - return $(v.placeholder) 72 - .attr(identifiers.schemas.literal) 73 - .call(literalValue); 71 + return $(v).attr(identifiers.schemas.literal).call(literalValue); 74 72 } 75 73 76 74 const pipes: Array<ReturnType<typeof $.call>> = []; 77 75 78 76 // For bigint formats (int64, uint64), create union of number, string, and bigint with transform 79 77 if (isBigInt) { 80 - const unionExpression = $(v.placeholder) 78 + const unionExpression = $(v) 81 79 .attr(identifiers.schemas.union) 82 80 .call( 83 81 $.array( 84 - $(v.placeholder).attr(identifiers.schemas.number).call(), 85 - $(v.placeholder).attr(identifiers.schemas.string).call(), 86 - $(v.placeholder).attr(identifiers.schemas.bigInt).call(), 82 + $(v).attr(identifiers.schemas.number).call(), 83 + $(v).attr(identifiers.schemas.string).call(), 84 + $(v).attr(identifiers.schemas.bigInt).call(), 87 85 ), 88 86 ); 89 87 pipes.push(unionExpression); 90 88 91 89 // Add transform to convert to BigInt 92 - const transformExpression = $(v.placeholder) 90 + const transformExpression = $(v) 93 91 .attr(identifiers.actions.transform) 94 92 .call($.func().param('x').do($('BigInt').call('x').return())); 95 93 pipes.push(transformExpression); 96 94 } else { 97 95 // For regular number formats, use number schema 98 - const expression = $(v.placeholder).attr(identifiers.schemas.number).call(); 96 + const expression = $(v).attr(identifiers.schemas.number).call(); 99 97 pipes.push(expression); 100 98 } 101 99 102 100 // Add integer validation for integer types (except when using bigint union) 103 101 if (!isBigInt && isInteger) { 104 - const expression = $(v.placeholder) 105 - .attr(identifiers.actions.integer) 106 - .call(); 102 + const expression = $(v).attr(identifiers.actions.integer).call(); 107 103 pipes.push(expression); 108 104 } 109 105 ··· 115 111 const maxErrorMessage = formatInfo.maxError; 116 112 117 113 // Add minimum value validation 118 - const minExpression = $(v.placeholder) 114 + const minExpression = $(v) 119 115 .attr(identifiers.actions.minValue) 120 116 .call( 121 117 isBigInt ? $('BigInt').call($.literal(minValue)) : $.literal(minValue), ··· 124 120 pipes.push(minExpression); 125 121 126 122 // Add maximum value validation 127 - const maxExpression = $(v.placeholder) 123 + const maxExpression = $(v) 128 124 .attr(identifiers.actions.maxValue) 129 125 .call( 130 126 isBigInt ? $('BigInt').call($.literal(maxValue)) : $.literal(maxValue), ··· 134 130 } 135 131 136 132 if (schema.exclusiveMinimum !== undefined) { 137 - const expression = $(v.placeholder) 133 + const expression = $(v) 138 134 .attr(identifiers.actions.gtValue) 139 135 .call(numberParameter({ isBigInt, value: schema.exclusiveMinimum })); 140 136 pipes.push(expression); 141 137 } else if (schema.minimum !== undefined) { 142 - const expression = $(v.placeholder) 138 + const expression = $(v) 143 139 .attr(identifiers.actions.minValue) 144 140 .call(numberParameter({ isBigInt, value: schema.minimum })); 145 141 pipes.push(expression); 146 142 } 147 143 148 144 if (schema.exclusiveMaximum !== undefined) { 149 - const expression = $(v.placeholder) 145 + const expression = $(v) 150 146 .attr(identifiers.actions.ltValue) 151 147 .call(numberParameter({ isBigInt, value: schema.exclusiveMaximum })); 152 148 pipes.push(expression); 153 149 } else if (schema.maximum !== undefined) { 154 - const expression = $(v.placeholder) 150 + const expression = $(v) 155 151 .attr(identifiers.actions.maxValue) 156 152 .call(numberParameter({ isBigInt, value: schema.maximum })); 157 153 pipes.push(expression);
+5 -14
packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts
··· 21 21 22 22 // Handle `additionalProperties: { type: 'never' }` → v.strictObject() 23 23 if (additional === null) { 24 - return pipes.push( 25 - $(v.placeholder).attr(identifiers.schemas.strictObject).call(shape), 26 - ); 24 + return pipes.push($(v).attr(identifiers.schemas.strictObject).call(shape)); 27 25 } 28 26 29 27 // Handle additionalProperties as schema → v.record() or v.objectWithRest() 30 28 if (additional) { 31 29 if (shape.isEmpty) { 32 30 return pipes.push( 33 - $(v.placeholder) 31 + $(v) 34 32 .attr(identifiers.schemas.record) 35 - .call( 36 - $(v.placeholder).attr(identifiers.schemas.string).call(), 37 - additional, 38 - ), 33 + .call($(v).attr(identifiers.schemas.string).call(), additional), 39 34 ); 40 35 } 41 36 42 37 // If there are named properties, use v.objectWithRest() to validate both 43 38 return pipes.push( 44 - $(v.placeholder) 45 - .attr(identifiers.schemas.objectWithRest) 46 - .call(shape, additional), 39 + $(v).attr(identifiers.schemas.objectWithRest).call(shape, additional), 47 40 ); 48 41 } 49 42 50 43 // Default case → v.object() 51 - return pipes.push( 52 - $(v.placeholder).attr(identifiers.schemas.object).call(shape), 53 - ); 44 + return pipes.push($(v).attr(identifiers.schemas.object).call(shape)); 54 45 } 55 46 56 47 export const objectToAst = ({
+13 -27
packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts
··· 18 18 19 19 switch (schema.format) { 20 20 case 'date': 21 - return pipes.push( 22 - $(v.placeholder).attr(identifiers.actions.isoDate).call(), 23 - ); 21 + return pipes.push($(v).attr(identifiers.actions.isoDate).call()); 24 22 case 'date-time': 25 - return pipes.push( 26 - $(v.placeholder).attr(identifiers.actions.isoTimestamp).call(), 27 - ); 23 + return pipes.push($(v).attr(identifiers.actions.isoTimestamp).call()); 28 24 case 'email': 29 - return pipes.push( 30 - $(v.placeholder).attr(identifiers.actions.email).call(), 31 - ); 25 + return pipes.push($(v).attr(identifiers.actions.email).call()); 32 26 case 'ipv4': 33 27 case 'ipv6': 34 - return pipes.push($(v.placeholder).attr(identifiers.actions.ip).call()); 28 + return pipes.push($(v).attr(identifiers.actions.ip).call()); 35 29 case 'time': 36 - return pipes.push( 37 - $(v.placeholder).attr(identifiers.actions.isoTimeSecond).call(), 38 - ); 30 + return pipes.push($(v).attr(identifiers.actions.isoTimeSecond).call()); 39 31 case 'uri': 40 - return pipes.push($(v.placeholder).attr(identifiers.actions.url).call()); 32 + return pipes.push($(v).attr(identifiers.actions.url).call()); 41 33 case 'uuid': 42 - return pipes.push($(v.placeholder).attr(identifiers.actions.uuid).call()); 34 + return pipes.push($(v).attr(identifiers.actions.uuid).call()); 43 35 } 44 36 45 37 return true; ··· 60 52 61 53 if (typeof schema.const === 'string') { 62 54 pipes.push( 63 - $(v.placeholder) 64 - .attr(identifiers.schemas.literal) 65 - .call($.literal(schema.const)), 55 + $(v).attr(identifiers.schemas.literal).call($.literal(schema.const)), 66 56 ); 67 57 return pipesToAst({ pipes, plugin }); 68 58 } 69 59 70 - pipes.push($(v.placeholder).attr(identifiers.schemas.string).call()); 60 + pipes.push($(v).attr(identifiers.schemas.string).call()); 71 61 72 62 if (schema.format) { 73 63 const args: FormatResolverArgs = { $, pipes, plugin, schema }; ··· 78 68 79 69 if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { 80 70 pipes.push( 81 - $(v.placeholder) 82 - .attr(identifiers.actions.length) 83 - .call($.literal(schema.minLength)), 71 + $(v).attr(identifiers.actions.length).call($.literal(schema.minLength)), 84 72 ); 85 73 } else { 86 74 if (schema.minLength !== undefined) { 87 75 pipes.push( 88 - $(v.placeholder) 76 + $(v) 89 77 .attr(identifiers.actions.minLength) 90 78 .call($.literal(schema.minLength)), 91 79 ); ··· 93 81 94 82 if (schema.maxLength !== undefined) { 95 83 pipes.push( 96 - $(v.placeholder) 84 + $(v) 97 85 .attr(identifiers.actions.maxLength) 98 86 .call($.literal(schema.maxLength)), 99 87 ); ··· 102 90 103 91 if (schema.pattern) { 104 92 pipes.push( 105 - $(v.placeholder) 106 - .attr(identifiers.actions.regex) 107 - .call($.regexp(schema.pattern)), 93 + $(v).attr(identifiers.actions.regex).call($.regexp(schema.pattern)), 108 94 ); 109 95 } 110 96
+3 -5
packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts
··· 24 24 25 25 if (schema.const && Array.isArray(schema.const)) { 26 26 const tupleElements = schema.const.map((value) => 27 - $(v.placeholder) 28 - .attr(identifiers.schemas.literal) 29 - .call($.fromValue(value)), 27 + $(v).attr(identifiers.schemas.literal).call($.fromValue(value)), 30 28 ); 31 29 result.pipes = [ 32 - $(v.placeholder) 30 + $(v) 33 31 .attr(identifiers.schemas.tuple) 34 32 .call($.array(...tupleElements)), 35 33 ]; ··· 52 50 return pipesToAst({ pipes: schemaPipes.pipes, plugin }); 53 51 }); 54 52 result.pipes = [ 55 - $(v.placeholder) 53 + $(v) 56 54 .attr(identifiers.schemas.tuple) 57 55 .call($.array(...tupleElements)), 58 56 ];
+1 -3
packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts
··· 14 14 resource: 'valibot.v', 15 15 }); 16 16 17 - const expression = $(v.placeholder) 18 - .attr(identifiers.schemas.undefined) 19 - .call(); 17 + const expression = $(v).attr(identifiers.schemas.undefined).call(); 20 18 return expression; 21 19 };
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts
··· 14 14 resource: 'valibot.v', 15 15 }); 16 16 17 - const expression = $(v.placeholder).attr(identifiers.schemas.unknown).call(); 17 + const expression = $(v).attr(identifiers.schemas.unknown).call(); 18 18 return expression; 19 19 };
+1 -1
packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts
··· 14 14 resource: 'valibot.v', 15 15 }); 16 16 17 - const expression = $(v.placeholder).attr(identifiers.schemas.void).call(); 17 + const expression = $(v).attr(identifiers.schemas.void).call(); 18 18 return expression; 19 19 };
+1 -5
packages/openapi-ts/src/plugins/zod/mini/api.ts
··· 7 7 const defaultValidatorResolver = ({ 8 8 schema, 9 9 }: ValidatorResolverArgs): ReturnType<typeof $.return> => 10 - $(schema.placeholder) 11 - .attr(identifiers.parseAsync) 12 - .call('data') 13 - .await() 14 - .return(); 10 + $(schema).attr(identifiers.parseAsync).call('data').await().return(); 15 11 16 12 export const createRequestValidatorMini = ({ 17 13 operation,
+11 -15
packages/openapi-ts/src/plugins/zod/mini/plugin.ts
··· 48 48 }; 49 49 const refSymbol = plugin.referenceSymbol(query); 50 50 if (plugin.isSymbolRegistered(query)) { 51 - ast.expression = $(refSymbol.placeholder); 51 + ast.expression = $(refSymbol); 52 52 } else { 53 - ast.expression = $(z.placeholder) 53 + ast.expression = $(z) 54 54 .attr(identifiers.lazy) 55 - .call($.func().returns('any').do($(refSymbol.placeholder).return())); 55 + .call($.func().returns('any').do($(refSymbol).return())); 56 56 ast.hasLazyExpression = true; 57 57 state.hasLazyExpression.value = true; 58 58 } ··· 69 69 ast.expression = ast.expression 70 70 .attr(identifiers.register) 71 71 .call( 72 - $(z.placeholder).attr(identifiers.globalRegistry), 72 + $(z).attr(identifiers.globalRegistry), 73 73 $.object() 74 74 .pretty() 75 75 .prop('description', $.literal(schema.description)), ··· 99 99 firstSchema.logicalOperator === 'or' || 100 100 (firstSchema.type && firstSchema.type !== 'object') 101 101 ) { 102 - ast.expression = $(z.placeholder) 102 + ast.expression = $(z) 103 103 .attr(identifiers.intersection) 104 104 .call(...itemSchemas.map((schema) => schema.expression)); 105 105 } else { 106 106 ast.expression = itemSchemas[0]!.expression; 107 107 itemSchemas.slice(1).forEach((schema) => { 108 - ast.expression = $(z.placeholder) 108 + ast.expression = $(z) 109 109 .attr(identifiers.intersection) 110 110 .call( 111 111 ast.expression, 112 112 schema.hasLazyExpression 113 - ? $(z.placeholder) 113 + ? $(z) 114 114 .attr(identifiers.lazy) 115 115 .call($.func().do(schema.expression.return())) 116 116 : schema.expression, ··· 118 118 }); 119 119 } 120 120 } else { 121 - ast.expression = $(z.placeholder) 121 + ast.expression = $(z) 122 122 .attr(identifiers.union) 123 123 .call( 124 124 $.array() ··· 143 143 144 144 if (ast.expression) { 145 145 if (schema.accessScope === 'read') { 146 - ast.expression = $(z.placeholder) 147 - .attr(identifiers.readonly) 148 - .call(ast.expression); 146 + ast.expression = $(z).attr(identifiers.readonly).call(ast.expression); 149 147 } 150 148 151 149 if (optional) { 152 - ast.expression = $(z.placeholder) 153 - .attr(identifiers.optional) 154 - .call(ast.expression); 150 + ast.expression = $(z).attr(identifiers.optional).call(ast.expression); 155 151 ast.typeName = identifiers.ZodMiniOptional; 156 152 } 157 153 158 154 if (schema.default !== undefined) { 159 155 const isBigInt = schema.type === 'integer' && schema.format === 'int64'; 160 - ast.expression = $(z.placeholder) 156 + ast.expression = $(z) 161 157 .attr(identifiers._default) 162 158 .call( 163 159 ast.expression,
+8 -14
packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts
··· 22 22 23 23 const result: Partial<Omit<Ast, 'typeName'>> = {}; 24 24 25 - const functionName = $(z.placeholder).attr(identifiers.array); 25 + const functionName = $(z).attr(identifiers.array); 26 26 27 27 if (!schema.items) { 28 28 result.expression = functionName.call( ··· 66 66 firstSchema.logicalOperator === 'or' || 67 67 (firstSchema.type && firstSchema.type !== 'object') 68 68 ) { 69 - intersectionExpression = $(z.placeholder) 69 + intersectionExpression = $(z) 70 70 .attr(identifiers.intersection) 71 71 .call(...itemExpressions); 72 72 } else { 73 73 intersectionExpression = itemExpressions[0]!; 74 74 for (let i = 1; i < itemExpressions.length; i++) { 75 - intersectionExpression = $(z.placeholder) 75 + intersectionExpression = $(z) 76 76 .attr(identifiers.intersection) 77 77 .call(intersectionExpression, itemExpressions[i]); 78 78 } ··· 80 80 81 81 result.expression = functionName.call(intersectionExpression); 82 82 } else { 83 - result.expression = $(z.placeholder) 83 + result.expression = $(z) 84 84 .attr(identifiers.array) 85 85 .call( 86 - $(z.placeholder) 86 + $(z) 87 87 .attr(identifiers.union) 88 88 .call($.array(...itemExpressions)), 89 89 ); ··· 95 95 96 96 if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { 97 97 checks.push( 98 - $(z.placeholder) 99 - .attr(identifiers.length) 100 - .call($.fromValue(schema.minItems)), 98 + $(z).attr(identifiers.length).call($.fromValue(schema.minItems)), 101 99 ); 102 100 } else { 103 101 if (schema.minItems !== undefined) { 104 102 checks.push( 105 - $(z.placeholder) 106 - .attr(identifiers.minLength) 107 - .call($.fromValue(schema.minItems)), 103 + $(z).attr(identifiers.minLength).call($.fromValue(schema.minItems)), 108 104 ); 109 105 } 110 106 111 107 if (schema.maxItems !== undefined) { 112 108 checks.push( 113 - $(z.placeholder) 114 - .attr(identifiers.maxLength) 115 - .call($.fromValue(schema.maxItems)), 109 + $(z).attr(identifiers.maxLength).call($.fromValue(schema.maxItems)), 116 110 ); 117 111 } 118 112 }
+2 -4
packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts
··· 19 19 }); 20 20 21 21 if (typeof schema.const === 'boolean') { 22 - chain = $(z.placeholder) 23 - .attr(identifiers.literal) 24 - .call($.literal(schema.const)); 22 + chain = $(z).attr(identifiers.literal).call($.literal(schema.const)); 25 23 result.expression = chain; 26 24 return result as Omit<Ast, 'typeName'>; 27 25 } 28 26 29 - chain = $(z.placeholder).attr(identifiers.boolean).call(); 27 + chain = $(z).attr(identifiers.boolean).call(); 30 28 result.expression = chain; 31 29 return result as Omit<Ast, 'typeName'>; 32 30 };
+6 -14
packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts
··· 30 30 if (item.type === 'string' && typeof item.const === 'string') { 31 31 const literal = $.literal(item.const); 32 32 enumMembers.push(literal); 33 - literalMembers.push( 34 - $(z.placeholder).attr(identifiers.literal).call(literal), 35 - ); 33 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 36 34 } else if ( 37 35 (item.type === 'number' || item.type === 'integer') && 38 36 typeof item.const === 'number' 39 37 ) { 40 38 allStrings = false; 41 39 const literal = $.literal(item.const); 42 - literalMembers.push( 43 - $(z.placeholder).attr(identifiers.literal).call(literal), 44 - ); 40 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 45 41 } else if (item.type === 'boolean' && typeof item.const === 'boolean') { 46 42 allStrings = false; 47 43 const literal = $.literal(item.const); 48 - literalMembers.push( 49 - $(z.placeholder).attr(identifiers.literal).call(literal), 50 - ); 44 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 51 45 } else if (item.type === 'null' || item.const === null) { 52 46 isNullable = true; 53 47 } ··· 65 59 66 60 // Use z.enum() for pure string enums, z.union() for mixed or non-string types 67 61 if (allStrings && enumMembers.length > 0) { 68 - result.expression = $(z.placeholder) 62 + result.expression = $(z) 69 63 .attr(identifiers.enum) 70 64 .call($.array(...enumMembers)); 71 65 } else if (literalMembers.length === 1) { 72 66 // For single-member unions, use the member directly instead of wrapping in z.union() 73 67 result.expression = literalMembers[0]!; 74 68 } else { 75 - result.expression = $(z.placeholder) 69 + result.expression = $(z) 76 70 .attr(identifiers.union) 77 71 .call($.array(...literalMembers)); 78 72 } 79 73 80 74 if (isNullable) { 81 - result.expression = $(z.placeholder) 82 - .attr(identifiers.nullable) 83 - .call(result.expression); 75 + result.expression = $(z).attr(identifiers.nullable).call(result.expression); 84 76 } 85 77 86 78 return result as Omit<Ast, 'typeName'>;
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts
··· 14 14 resource: 'zod.z', 15 15 }); 16 16 const result: Partial<Omit<Ast, 'typeName'>> = {}; 17 - result.expression = $(z.placeholder).attr(identifiers.never).call(); 17 + result.expression = $(z).attr(identifiers.never).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts
··· 14 14 resource: 'zod.z', 15 15 }); 16 16 const result: Partial<Omit<Ast, 'typeName'>> = {}; 17 - result.expression = $(z.placeholder).attr(identifiers.null).call(); 17 + result.expression = $(z).attr(identifiers.null).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+8 -8
packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts
··· 22 22 23 23 if (typeof schema.const === 'number') { 24 24 // TODO: parser - handle bigint constants 25 - result.expression = $(z.placeholder) 25 + result.expression = $(z) 26 26 .attr(identifiers.literal) 27 27 .call($.literal(schema.const)); 28 28 return result as Omit<Ast, 'typeName'>; 29 29 } 30 30 31 31 result.expression = isBigInt 32 - ? $(z.placeholder).attr(identifiers.coerce).attr(identifiers.bigint).call() 33 - : $(z.placeholder).attr(identifiers.number).call(); 32 + ? $(z).attr(identifiers.coerce).attr(identifiers.bigint).call() 33 + : $(z).attr(identifiers.number).call(); 34 34 35 35 if (!isBigInt && schema.type === 'integer') { 36 - result.expression = $(z.placeholder).attr(identifiers.int).call(); 36 + result.expression = $(z).attr(identifiers.int).call(); 37 37 } 38 38 39 39 const checks: Array<ReturnType<typeof $.call>> = []; 40 40 41 41 if (schema.exclusiveMinimum !== undefined) { 42 42 checks.push( 43 - $(z.placeholder) 43 + $(z) 44 44 .attr(identifiers.gt) 45 45 .call(numberParameter({ isBigInt, value: schema.exclusiveMinimum })), 46 46 ); 47 47 } else if (schema.minimum !== undefined) { 48 48 checks.push( 49 - $(z.placeholder) 49 + $(z) 50 50 .attr(identifiers.gte) 51 51 .call(numberParameter({ isBigInt, value: schema.minimum })), 52 52 ); ··· 54 54 55 55 if (schema.exclusiveMaximum !== undefined) { 56 56 checks.push( 57 - $(z.placeholder) 57 + $(z) 58 58 .attr(identifiers.lt) 59 59 .call(numberParameter({ isBigInt, value: schema.exclusiveMaximum })), 60 60 ); 61 61 } else if (schema.maximum !== undefined) { 62 62 checks.push( 63 - $(z.placeholder) 63 + $(z) 64 64 .attr(identifiers.lte) 65 65 .call(numberParameter({ isBigInt, value: schema.maximum })), 66 66 );
+3 -3
packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts
··· 18 18 }); 19 19 20 20 if (additional) { 21 - return $(z.placeholder) 21 + return $(z) 22 22 .attr(identifiers.record) 23 - .call($(z.placeholder).attr(identifiers.string).call(), additional); 23 + .call($(z).attr(identifiers.string).call(), additional); 24 24 } 25 25 26 - return $(z.placeholder).attr(identifiers.object).call(shape); 26 + return $(z).attr(identifiers.object).call(shape); 27 27 } 28 28 29 29 export const objectToAst = ({
+14 -30
packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts
··· 17 17 18 18 switch (schema.format) { 19 19 case 'date': 20 - return $(z.placeholder) 21 - .attr(identifiers.iso) 22 - .attr(identifiers.date) 23 - .call(); 20 + return $(z).attr(identifiers.iso).attr(identifiers.date).call(); 24 21 case 'date-time': { 25 22 const obj = $.object() 26 23 .$if(plugin.config.dates.offset, (o) => ··· 29 26 .$if(plugin.config.dates.local, (o) => 30 27 o.prop('local', $.literal(true)), 31 28 ); 32 - return $(z.placeholder) 29 + return $(z) 33 30 .attr(identifiers.iso) 34 31 .attr(identifiers.datetime) 35 32 .call(obj.hasProps() ? obj : undefined); 36 33 } 37 34 case 'email': 38 - return $(z.placeholder).attr(identifiers.email).call(); 35 + return $(z).attr(identifiers.email).call(); 39 36 case 'ipv4': 40 - return $(z.placeholder).attr(identifiers.ipv4).call(); 37 + return $(z).attr(identifiers.ipv4).call(); 41 38 case 'ipv6': 42 - return $(z.placeholder).attr(identifiers.ipv6).call(); 39 + return $(z).attr(identifiers.ipv6).call(); 43 40 case 'time': 44 - return $(z.placeholder) 45 - .attr(identifiers.iso) 46 - .attr(identifiers.time) 47 - .call(); 41 + return $(z).attr(identifiers.iso).attr(identifiers.time).call(); 48 42 case 'uri': 49 - return $(z.placeholder).attr(identifiers.url).call(); 43 + return $(z).attr(identifiers.url).call(); 50 44 case 'uuid': 51 - return $(z.placeholder).attr(identifiers.uuid).call(); 45 + return $(z).attr(identifiers.uuid).call(); 52 46 default: 53 47 return chain; 54 48 } ··· 69 63 }); 70 64 71 65 if (typeof schema.const === 'string') { 72 - chain = $(z.placeholder) 73 - .attr(identifiers.literal) 74 - .call($.literal(schema.const)); 66 + chain = $(z).attr(identifiers.literal).call($.literal(schema.const)); 75 67 result.expression = chain; 76 68 return result as Omit<Ast, 'typeName'>; 77 69 } 78 70 79 - chain = $(z.placeholder).attr(identifiers.string).call(); 71 + chain = $(z).attr(identifiers.string).call(); 80 72 81 73 if (schema.format) { 82 74 const args: FormatResolverArgs = { $, chain, plugin, schema }; ··· 89 81 90 82 if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { 91 83 checks.push( 92 - $(z.placeholder) 93 - .attr(identifiers.length) 94 - .call($.literal(schema.minLength)), 84 + $(z).attr(identifiers.length).call($.literal(schema.minLength)), 95 85 ); 96 86 } else { 97 87 if (schema.minLength !== undefined) { 98 88 checks.push( 99 - $(z.placeholder) 100 - .attr(identifiers.minLength) 101 - .call($.literal(schema.minLength)), 89 + $(z).attr(identifiers.minLength).call($.literal(schema.minLength)), 102 90 ); 103 91 } 104 92 105 93 if (schema.maxLength !== undefined) { 106 94 checks.push( 107 - $(z.placeholder) 108 - .attr(identifiers.maxLength) 109 - .call($.literal(schema.maxLength)), 95 + $(z).attr(identifiers.maxLength).call($.literal(schema.maxLength)), 110 96 ); 111 97 } 112 98 } 113 99 114 100 if (schema.pattern) { 115 - checks.push( 116 - $(z.placeholder).attr(identifiers.regex).call($.regexp(schema.pattern)), 117 - ); 101 + checks.push($(z).attr(identifiers.regex).call($.regexp(schema.pattern))); 118 102 } 119 103 120 104 if (checks.length) {
+3 -3
packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts
··· 22 22 23 23 if (schema.const && Array.isArray(schema.const)) { 24 24 const tupleElements = schema.const.map((value) => 25 - $(z.placeholder).attr(identifiers.literal).call($.fromValue(value)), 25 + $(z).attr(identifiers.literal).call($.fromValue(value)), 26 26 ); 27 - result.expression = $(z.placeholder) 27 + result.expression = $(z) 28 28 .attr(identifiers.tuple) 29 29 .call($.array(...tupleElements)); 30 30 return result as Omit<Ast, 'typeName'>; ··· 49 49 }); 50 50 } 51 51 52 - result.expression = $(z.placeholder) 52 + result.expression = $(z) 53 53 .attr(identifiers.tuple) 54 54 .call($.array(...tupleElements)); 55 55
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts
··· 14 14 resource: 'zod.z', 15 15 }); 16 16 const result: Partial<Omit<Ast, 'typeName'>> = {}; 17 - result.expression = $(z.placeholder).attr(identifiers.undefined).call(); 17 + result.expression = $(z).attr(identifiers.undefined).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts
··· 14 14 resource: 'zod.z', 15 15 }); 16 16 const result: Partial<Omit<Ast, 'typeName'>> = {}; 17 - result.expression = $(z.placeholder).attr(identifiers.unknown).call(); 17 + result.expression = $(z).attr(identifiers.unknown).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+1 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts
··· 14 14 resource: 'zod.z', 15 15 }); 16 16 const result: Partial<Omit<Ast, 'typeName'>> = {}; 17 - result.expression = $(z.placeholder).attr(identifiers.void).call(); 17 + result.expression = $(z).attr(identifiers.void).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+3 -7
packages/openapi-ts/src/plugins/zod/shared/export.ts
··· 33 33 ) 34 34 .$if(ast.typeName, (c, v) => c.type($.type(z).attr(v))) 35 35 .assign(ast.expression); 36 - plugin.setSymbolValue(symbol, statement); 36 + plugin.addNode(statement); 37 37 38 38 if (typeInferSymbol) { 39 39 const inferType = $.type 40 40 .alias(typeInferSymbol) 41 41 .export() 42 - .type( 43 - $.type(z) 44 - .attr(identifiers.infer) 45 - .generic($(symbol.placeholder).typeofType()), 46 - ); 47 - plugin.setSymbolValue(typeInferSymbol, inferType); 42 + .type($.type(z).attr(identifiers.infer).generic($(symbol).typeofType())); 43 + plugin.addNode(inferType); 48 44 } 49 45 };
+1 -5
packages/openapi-ts/src/plugins/zod/v3/api.ts
··· 7 7 const defaultValidatorResolver = ({ 8 8 schema, 9 9 }: ValidatorResolverArgs): ReturnType<typeof $.return> => 10 - $(schema.placeholder) 11 - .attr(identifiers.parseAsync) 12 - .call('data') 13 - .await() 14 - .return(); 10 + $(schema).attr(identifiers.parseAsync).call('data').await().return(); 15 11 16 12 export const createRequestValidatorV3 = ({ 17 13 operation,
+5 -5
packages/openapi-ts/src/plugins/zod/v3/plugin.ts
··· 48 48 }; 49 49 const refSymbol = plugin.referenceSymbol(query); 50 50 if (plugin.isSymbolRegistered(query)) { 51 - ast.expression = $(refSymbol.placeholder); 51 + ast.expression = $(refSymbol); 52 52 } else { 53 - ast.expression = $(z.placeholder) 53 + ast.expression = $(z) 54 54 .attr(identifiers.lazy) 55 - .call($.func().do($(refSymbol.placeholder).return())); 55 + .call($.func().do($(refSymbol).return())); 56 56 ast.hasLazyExpression = true; 57 57 state.hasLazyExpression.value = true; 58 58 } ··· 95 95 firstSchema.logicalOperator === 'or' || 96 96 (firstSchema.type && firstSchema.type !== 'object') 97 97 ) { 98 - ast.expression = $(z.placeholder) 98 + ast.expression = $(z) 99 99 .attr(identifiers.intersection) 100 100 .call(...itemTypes); 101 101 } else { ··· 105 105 }); 106 106 } 107 107 } else { 108 - ast.expression = $(z.placeholder) 108 + ast.expression = $(z) 109 109 .attr(identifiers.union) 110 110 .call( 111 111 $.array()
+4 -4
packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts
··· 22 22 resource: 'zod.z', 23 23 }); 24 24 25 - const functionName = $(z.placeholder).attr(identifiers.array); 25 + const functionName = $(z).attr(identifiers.array); 26 26 27 27 let arrayExpression: ReturnType<typeof $.call> | undefined; 28 28 let hasLazyExpression = false; ··· 69 69 firstSchema.logicalOperator === 'or' || 70 70 (firstSchema.type && firstSchema.type !== 'object') 71 71 ) { 72 - intersectionExpression = $(z.placeholder) 72 + intersectionExpression = $(z) 73 73 .attr(identifiers.intersection) 74 74 .call(...itemExpressions); 75 75 } else { ··· 83 83 84 84 arrayExpression = functionName.call(intersectionExpression); 85 85 } else { 86 - arrayExpression = $(z.placeholder) 86 + arrayExpression = $(z) 87 87 .attr(identifiers.array) 88 88 .call( 89 - $(z.placeholder) 89 + $(z) 90 90 .attr(identifiers.union) 91 91 .call($.array(...itemExpressions)), 92 92 );
+2 -4
packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts
··· 18 18 }); 19 19 20 20 if (typeof schema.const === 'boolean') { 21 - chain = $(z.placeholder) 22 - .attr(identifiers.literal) 23 - .call($.literal(schema.const)); 21 + chain = $(z).attr(identifiers.literal).call($.literal(schema.const)); 24 22 return chain; 25 23 } 26 24 27 - chain = $(z.placeholder).attr(identifiers.boolean).call(); 25 + chain = $(z).attr(identifiers.boolean).call(); 28 26 return chain; 29 27 };
+5 -11
packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts
··· 28 28 if (item.type === 'string' && typeof item.const === 'string') { 29 29 const literal = $.literal(item.const); 30 30 enumMembers.push(literal); 31 - literalMembers.push( 32 - $(z.placeholder).attr(identifiers.literal).call(literal), 33 - ); 31 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 34 32 } else if ( 35 33 (item.type === 'number' || item.type === 'integer') && 36 34 typeof item.const === 'number' 37 35 ) { 38 36 allStrings = false; 39 37 const literal = $.literal(item.const); 40 - literalMembers.push( 41 - $(z.placeholder).attr(identifiers.literal).call(literal), 42 - ); 38 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 43 39 } else if (item.type === 'boolean' && typeof item.const === 'boolean') { 44 40 allStrings = false; 45 41 const literal = $.literal(item.const); 46 - literalMembers.push( 47 - $(z.placeholder).attr(identifiers.literal).call(literal), 48 - ); 42 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 49 43 } else if (item.type === 'null' || item.const === null) { 50 44 isNullable = true; 51 45 } ··· 64 58 // Use z.enum() for pure string enums, z.union() for mixed or non-string types 65 59 let enumExpression: ReturnType<typeof $.call>; 66 60 if (allStrings && enumMembers.length > 0) { 67 - enumExpression = $(z.placeholder) 61 + enumExpression = $(z) 68 62 .attr(identifiers.enum) 69 63 .call($.array(...enumMembers)); 70 64 } else if (literalMembers.length === 1) { 71 65 // For single-member unions, use the member directly instead of wrapping in z.union() 72 66 enumExpression = literalMembers[0]!; 73 67 } else { 74 - enumExpression = $(z.placeholder) 68 + enumExpression = $(z) 75 69 .attr(identifiers.union) 76 70 .call($.array(...literalMembers)); 77 71 }
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts
··· 13 13 category: 'external', 14 14 resource: 'zod.z', 15 15 }); 16 - const expression = $(z.placeholder).attr(identifiers.never).call(); 16 + const expression = $(z).attr(identifiers.never).call(); 17 17 return expression; 18 18 };
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts
··· 13 13 category: 'external', 14 14 resource: 'zod.z', 15 15 }); 16 - const expression = $(z.placeholder).attr(identifiers.null).call(); 16 + const expression = $(z).attr(identifiers.null).call(); 17 17 return expression; 18 18 };
+3 -3
packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts
··· 20 20 21 21 if (typeof schema.const === 'number') { 22 22 // TODO: parser - handle bigint constants 23 - const expression = $(z.placeholder) 23 + const expression = $(z) 24 24 .attr(identifiers.literal) 25 25 .call($.literal(schema.const)); 26 26 return expression; 27 27 } 28 28 29 29 let numberExpression = isBigInt 30 - ? $(z.placeholder).attr(identifiers.coerce).attr(identifiers.bigint).call() 31 - : $(z.placeholder).attr(identifiers.number).call(); 30 + ? $(z).attr(identifiers.coerce).attr(identifiers.bigint).call() 31 + : $(z).attr(identifiers.number).call(); 32 32 33 33 if (!isBigInt && schema.type === 'integer') { 34 34 numberExpression = numberExpression.attr(identifiers.int).call();
+2 -2
packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts
··· 18 18 }); 19 19 20 20 if (additional) { 21 - return $(z.placeholder).attr(identifiers.record).call(additional); 21 + return $(z).attr(identifiers.record).call(additional); 22 22 } 23 23 24 - return $(z.placeholder).attr(identifiers.object).call(shape); 24 + return $(z).attr(identifiers.object).call(shape); 25 25 } 26 26 27 27 export const objectToAst = ({
+2 -4
packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts
··· 55 55 }); 56 56 57 57 if (typeof schema.const === 'string') { 58 - chain = $(z.placeholder) 59 - .attr(identifiers.literal) 60 - .call($.literal(schema.const)); 58 + chain = $(z).attr(identifiers.literal).call($.literal(schema.const)); 61 59 return chain; 62 60 } 63 61 64 - chain = $(z.placeholder).attr(identifiers.string).call(); 62 + chain = $(z).attr(identifiers.string).call(); 65 63 66 64 if (schema.format) { 67 65 const args: FormatResolverArgs = { $, chain, plugin, schema };
+3 -3
packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts
··· 24 24 25 25 if (schema.const && Array.isArray(schema.const)) { 26 26 const tupleElements = schema.const.map((value) => 27 - $(z.placeholder).attr(identifiers.literal).call($.fromValue(value)), 27 + $(z).attr(identifiers.literal).call($.fromValue(value)), 28 28 ); 29 - const expression = $(z.placeholder) 29 + const expression = $(z) 30 30 .attr(identifiers.tuple) 31 31 .call($.array(...tupleElements)); 32 32 return { ··· 54 54 }); 55 55 } 56 56 57 - const expression = $(z.placeholder) 57 + const expression = $(z) 58 58 .attr(identifiers.tuple) 59 59 .call($.array(...tupleElements)); 60 60
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts
··· 13 13 category: 'external', 14 14 resource: 'zod.z', 15 15 }); 16 - const expression = $(z.placeholder).attr(identifiers.undefined).call(); 16 + const expression = $(z).attr(identifiers.undefined).call(); 17 17 return expression; 18 18 };
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts
··· 13 13 category: 'external', 14 14 resource: 'zod.z', 15 15 }); 16 - const expression = $(z.placeholder).attr(identifiers.unknown).call(); 16 + const expression = $(z).attr(identifiers.unknown).call(); 17 17 return expression; 18 18 };
+1 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts
··· 13 13 category: 'external', 14 14 resource: 'zod.z', 15 15 }); 16 - const expression = $(z.placeholder).attr(identifiers.void).call(); 16 + const expression = $(z).attr(identifiers.void).call(); 17 17 return expression; 18 18 };
+1 -5
packages/openapi-ts/src/plugins/zod/v4/api.ts
··· 7 7 const defaultValidatorResolver = ({ 8 8 schema, 9 9 }: ValidatorResolverArgs): ReturnType<typeof $.return> => 10 - $(schema.placeholder) 11 - .attr(identifiers.parseAsync) 12 - .call('data') 13 - .await() 14 - .return(); 10 + $(schema).attr(identifiers.parseAsync).call('data').await().return(); 15 11 16 12 export const createRequestValidatorV4 = ({ 17 13 operation,
+8 -10
packages/openapi-ts/src/plugins/zod/v4/plugin.ts
··· 48 48 }; 49 49 const refSymbol = plugin.referenceSymbol(query); 50 50 if (plugin.isSymbolRegistered(query)) { 51 - ast.expression = $(refSymbol.placeholder); 51 + ast.expression = $(refSymbol); 52 52 } else { 53 - ast.expression = $(z.placeholder) 53 + ast.expression = $(z) 54 54 .attr(identifiers.lazy) 55 - .call($.func().returns('any').do($(refSymbol.placeholder).return())); 55 + .call($.func().returns('any').do($(refSymbol).return())); 56 56 ast.hasLazyExpression = true; 57 57 state.hasLazyExpression.value = true; 58 58 } ··· 69 69 ast.expression = ast.expression 70 70 .attr(identifiers.register) 71 71 .call( 72 - $(z.placeholder).attr(identifiers.globalRegistry), 72 + $(z).attr(identifiers.globalRegistry), 73 73 $.object() 74 74 .pretty() 75 75 .prop('description', $.literal(schema.description)), ··· 99 99 firstSchema.logicalOperator === 'or' || 100 100 (firstSchema.type && firstSchema.type !== 'object') 101 101 ) { 102 - ast.expression = $(z.placeholder) 102 + ast.expression = $(z) 103 103 .attr(identifiers.intersection) 104 104 .call(...itemSchemas.map((schema) => schema.expression)); 105 105 } else { ··· 109 109 .expression!.attr(identifiers.and) 110 110 .call( 111 111 schema.hasLazyExpression 112 - ? $(z.placeholder) 112 + ? $(z) 113 113 .attr(identifiers.lazy) 114 114 .call($.func().do(schema.expression.return())) 115 115 : schema.expression, ··· 117 117 }); 118 118 } 119 119 } else { 120 - ast.expression = $(z.placeholder) 120 + ast.expression = $(z) 121 121 .attr(identifiers.union) 122 122 .call( 123 123 $.array() ··· 150 150 } 151 151 152 152 if (optional) { 153 - ast.expression = $(z.placeholder) 154 - .attr(identifiers.optional) 155 - .call(ast.expression); 153 + ast.expression = $(z).attr(identifiers.optional).call(ast.expression); 156 154 ast.typeName = identifiers.ZodOptional; 157 155 } 158 156
+4 -4
packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts
··· 22 22 resource: 'zod.z', 23 23 }); 24 24 25 - const functionName = $(z.placeholder).attr(identifiers.array); 25 + const functionName = $(z).attr(identifiers.array); 26 26 27 27 if (!schema.items) { 28 28 result.expression = functionName.call( ··· 66 66 firstSchema.logicalOperator === 'or' || 67 67 (firstSchema.type && firstSchema.type !== 'object') 68 68 ) { 69 - intersectionExpression = $(z.placeholder) 69 + intersectionExpression = $(z) 70 70 .attr(identifiers.intersection) 71 71 .call(...itemExpressions); 72 72 } else { ··· 80 80 81 81 result.expression = functionName.call(intersectionExpression); 82 82 } else { 83 - result.expression = $(z.placeholder) 83 + result.expression = $(z) 84 84 .attr(identifiers.array) 85 85 .call( 86 - $(z.placeholder) 86 + $(z) 87 87 .attr(identifiers.union) 88 88 .call($.array(...itemExpressions)), 89 89 );
+2 -4
packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts
··· 19 19 }); 20 20 21 21 if (typeof schema.const === 'boolean') { 22 - chain = $(z.placeholder) 23 - .attr(identifiers.literal) 24 - .call($.literal(schema.const)); 22 + chain = $(z).attr(identifiers.literal).call($.literal(schema.const)); 25 23 result.expression = chain; 26 24 return result as Omit<Ast, 'typeName'>; 27 25 } 28 26 29 - chain = $(z.placeholder).attr(identifiers.boolean).call(); 27 + chain = $(z).attr(identifiers.boolean).call(); 30 28 result.expression = chain; 31 29 return result as Omit<Ast, 'typeName'>; 32 30 };
+6 -14
packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts
··· 30 30 if (item.type === 'string' && typeof item.const === 'string') { 31 31 const literal = $.literal(item.const); 32 32 enumMembers.push(literal); 33 - literalMembers.push( 34 - $(z.placeholder).attr(identifiers.literal).call(literal), 35 - ); 33 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 36 34 } else if ( 37 35 (item.type === 'number' || item.type === 'integer') && 38 36 typeof item.const === 'number' 39 37 ) { 40 38 allStrings = false; 41 39 const literal = $.literal(item.const); 42 - literalMembers.push( 43 - $(z.placeholder).attr(identifiers.literal).call(literal), 44 - ); 40 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 45 41 } else if (item.type === 'boolean' && typeof item.const === 'boolean') { 46 42 allStrings = false; 47 43 const literal = $.literal(item.const); 48 - literalMembers.push( 49 - $(z.placeholder).attr(identifiers.literal).call(literal), 50 - ); 44 + literalMembers.push($(z).attr(identifiers.literal).call(literal)); 51 45 } else if (item.type === 'null' || item.const === null) { 52 46 isNullable = true; 53 47 } ··· 65 59 66 60 // Use z.enum() for pure string enums, z.union() for mixed or non-string types 67 61 if (allStrings && enumMembers.length > 0) { 68 - result.expression = $(z.placeholder) 62 + result.expression = $(z) 69 63 .attr(identifiers.enum) 70 64 .call($.array(...enumMembers)); 71 65 } else if (literalMembers.length === 1) { 72 66 // For single-member unions, use the member directly instead of wrapping in z.union() 73 67 result.expression = literalMembers[0]!; 74 68 } else { 75 - result.expression = $(z.placeholder) 69 + result.expression = $(z) 76 70 .attr(identifiers.union) 77 71 .call($.array(...literalMembers)); 78 72 } 79 73 80 74 if (isNullable) { 81 - result.expression = $(z.placeholder) 82 - .attr(identifiers.nullable) 83 - .call(result.expression); 75 + result.expression = $(z).attr(identifiers.nullable).call(result.expression); 84 76 } 85 77 86 78 return result as Omit<Ast, 'typeName'>;
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts
··· 14 14 category: 'external', 15 15 resource: 'zod.z', 16 16 }); 17 - result.expression = $(z.placeholder).attr(identifiers.never).call(); 17 + result.expression = $(z).attr(identifiers.never).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts
··· 14 14 category: 'external', 15 15 resource: 'zod.z', 16 16 }); 17 - result.expression = $(z.placeholder).attr(identifiers.null).call(); 17 + result.expression = $(z).attr(identifiers.null).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+4 -4
packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts
··· 22 22 23 23 if (typeof schema.const === 'number') { 24 24 // TODO: parser - handle bigint constants 25 - result.expression = $(z.placeholder) 25 + result.expression = $(z) 26 26 .attr(identifiers.literal) 27 27 .call($.literal(schema.const)); 28 28 return result as Omit<Ast, 'typeName'>; 29 29 } 30 30 31 31 result.expression = isBigInt 32 - ? $(z.placeholder).attr(identifiers.coerce).attr(identifiers.bigint).call() 33 - : $(z.placeholder).attr(identifiers.number).call(); 32 + ? $(z).attr(identifiers.coerce).attr(identifiers.bigint).call() 33 + : $(z).attr(identifiers.number).call(); 34 34 35 35 if (!isBigInt && schema.type === 'integer') { 36 - result.expression = $(z.placeholder).attr(identifiers.int).call(); 36 + result.expression = $(z).attr(identifiers.int).call(); 37 37 } 38 38 39 39 if (schema.exclusiveMinimum !== undefined) {
+3 -3
packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts
··· 18 18 }); 19 19 20 20 if (additional) { 21 - return $(z.placeholder) 21 + return $(z) 22 22 .attr(identifiers.record) 23 - .call($(z.placeholder).attr(identifiers.string).call(), additional); 23 + .call($(z).attr(identifiers.string).call(), additional); 24 24 } 25 25 26 - return $(z.placeholder).attr(identifiers.object).call(shape); 26 + return $(z).attr(identifiers.object).call(shape); 27 27 } 28 28 29 29 export const objectToAst = ({
+10 -18
packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts
··· 17 17 18 18 switch (schema.format) { 19 19 case 'date': 20 - return $(z.placeholder) 21 - .attr(identifiers.iso) 22 - .attr(identifiers.date) 23 - .call(); 20 + return $(z).attr(identifiers.iso).attr(identifiers.date).call(); 24 21 case 'date-time': { 25 22 const obj = $.object() 26 23 .$if(plugin.config.dates.offset, (o) => ··· 29 26 .$if(plugin.config.dates.local, (o) => 30 27 o.prop('local', $.literal(true)), 31 28 ); 32 - return $(z.placeholder) 29 + return $(z) 33 30 .attr(identifiers.iso) 34 31 .attr(identifiers.datetime) 35 32 .call(obj.hasProps() ? obj : undefined); 36 33 } 37 34 case 'email': 38 - return $(z.placeholder).attr(identifiers.email).call(); 35 + return $(z).attr(identifiers.email).call(); 39 36 case 'ipv4': 40 - return $(z.placeholder).attr(identifiers.ipv4).call(); 37 + return $(z).attr(identifiers.ipv4).call(); 41 38 case 'ipv6': 42 - return $(z.placeholder).attr(identifiers.ipv6).call(); 39 + return $(z).attr(identifiers.ipv6).call(); 43 40 case 'time': 44 - return $(z.placeholder) 45 - .attr(identifiers.iso) 46 - .attr(identifiers.time) 47 - .call(); 41 + return $(z).attr(identifiers.iso).attr(identifiers.time).call(); 48 42 case 'uri': 49 - return $(z.placeholder).attr(identifiers.url).call(); 43 + return $(z).attr(identifiers.url).call(); 50 44 case 'uuid': 51 - return $(z.placeholder).attr(identifiers.uuid).call(); 45 + return $(z).attr(identifiers.uuid).call(); 52 46 default: 53 47 return chain; 54 48 } ··· 69 63 }); 70 64 71 65 if (typeof schema.const === 'string') { 72 - chain = $(z.placeholder) 73 - .attr(identifiers.literal) 74 - .call($.literal(schema.const)); 66 + chain = $(z).attr(identifiers.literal).call($.literal(schema.const)); 75 67 result.expression = chain; 76 68 return result as Omit<Ast, 'typeName'>; 77 69 } 78 70 79 - chain = $(z.placeholder).attr(identifiers.string).call(); 71 + chain = $(z).attr(identifiers.string).call(); 80 72 81 73 if (schema.format) { 82 74 const args: FormatResolverArgs = { $, chain, plugin, schema };
+3 -3
packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts
··· 22 22 23 23 if (schema.const && Array.isArray(schema.const)) { 24 24 const tupleElements = schema.const.map((value) => 25 - $(z.placeholder).attr(identifiers.literal).call($.fromValue(value)), 25 + $(z).attr(identifiers.literal).call($.fromValue(value)), 26 26 ); 27 - result.expression = $(z.placeholder) 27 + result.expression = $(z) 28 28 .attr(identifiers.tuple) 29 29 .call($.array(...tupleElements)); 30 30 return result as Omit<Ast, 'typeName'>; ··· 49 49 }); 50 50 } 51 51 52 - result.expression = $(z.placeholder) 52 + result.expression = $(z) 53 53 .attr(identifiers.tuple) 54 54 .call($.array(...tupleElements)); 55 55
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts
··· 14 14 category: 'external', 15 15 resource: 'zod.z', 16 16 }); 17 - result.expression = $(z.placeholder).attr(identifiers.undefined).call(); 17 + result.expression = $(z).attr(identifiers.undefined).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts
··· 14 14 category: 'external', 15 15 resource: 'zod.z', 16 16 }); 17 - result.expression = $(z.placeholder).attr(identifiers.unknown).call(); 17 + result.expression = $(z).attr(identifiers.unknown).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+1 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts
··· 14 14 category: 'external', 15 15 resource: 'zod.z', 16 16 }); 17 - result.expression = $(z.placeholder).attr(identifiers.void).call(); 17 + result.expression = $(z).attr(identifiers.void).call(); 18 18 return result as Omit<Ast, 'typeName'>; 19 19 };
+30 -90
packages/openapi-ts/src/ts-dsl/base.ts
··· 1 1 // TODO: symbol should be protected, but needs to be public to satisfy types 2 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 2 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 3 + import { Symbol } from '@hey-api/codegen-core'; 3 4 import { debug } from '@hey-api/codegen-core'; 4 5 import ts from 'typescript'; 5 6 6 7 export type MaybeArray<T> = T | ReadonlyArray<T>; 7 8 8 - export interface ITsDsl<T extends ts.Node = ts.Node> extends SyntaxNode { 9 + export interface ITsDsl<T extends ts.Node = ts.Node> extends Node { 9 10 /** Render this node into a concrete TypeScript AST. */ 10 11 $render(): T; 11 12 } 12 13 13 - // @deprecated 14 - export type Constructor<T = ITsDsl> = new (...args: ReadonlyArray<any>) => T; 14 + export const tsDslBrand = globalThis.Symbol('ts-dsl'); 15 15 16 16 export abstract class TsDsl<T extends ts.Node = ts.Node> implements ITsDsl<T> { 17 17 /** Render this node into a concrete TypeScript AST. */ 18 18 protected abstract _render(): T; 19 19 20 - /** Parent node in the constructed syntax tree. */ 21 - protected parent?: TsDsl<any>; 20 + readonly '~brand': symbol = tsDslBrand; 22 21 23 - /** The codegen symbol associated with this node. */ 22 + parent?: Node; 23 + 24 24 symbol?: Symbol; 25 25 26 26 /** Conditionally applies a callback to this builder. */ ··· 106 106 return this._render(); 107 107 } 108 108 109 - /** Returns all symbols referenced by this node (directly or through children). */ 110 109 // eslint-disable-next-line @typescript-eslint/no-unused-vars 111 - collectSymbols(_out: Set<Symbol>): void { 112 - // noop 113 - } 114 - 115 - /** Returns all locally declared names within this node. */ 116 - getLocalNames(): Iterable<string> { 117 - return []; 118 - } 119 - 120 - /** Rewrites internal identifier nodes after final name resolution. */ 121 - // eslint-disable-next-line @typescript-eslint/no-unused-vars 122 - rewriteIdentifiers(_map: Map<string, string>): void { 110 + analyze(_: AnalysisContext): void { 123 111 // noop 124 112 } 125 113 126 - /** Assigns the parent node, enforcing a single-parent invariant. */ 127 - setParent(parent: TsDsl<any>): this { 128 - if (this.parent && this.parent !== parent) { 129 - const message = `${this.constructor.name} already had a parent (${this.parent.constructor.name}), new parent attempted: ${parent.constructor.name}`; 130 - debug(message, 'dsl'); 131 - throw new Error(message); 132 - } 133 - 134 - if (this.symbol) { 135 - const message = `${this.constructor.name} has BOTH a symbol and a parent. This violates DSL invariants.`; 136 - debug(message, 'dsl'); 137 - throw new Error(message); 138 - } 139 - 140 - this.parent = parent; 141 - return this; 142 - } 143 - 144 - /** Walk this node and its children with a visitor. */ 145 - traverse(visitor: (node: SyntaxNode) => void): void { 146 - visitor(this); 147 - } 148 - 149 114 protected $maybeId<T extends string | ts.Expression>( 150 115 expr: T, 151 116 ): T extends string ? ts.Identifier : T { ··· 158 123 if (value === undefined) { 159 124 return undefined as NodeOfMaybe<I>; 160 125 } 126 + if (value instanceof Symbol) { 127 + return this.$maybeId(value.finalName) as NodeOfMaybe<I>; 128 + } 161 129 if (typeof value === 'string') { 162 - return ts.factory.createIdentifier(value) as NodeOfMaybe<I>; 130 + return this.$maybeId(value) as NodeOfMaybe<I>; 163 131 } 164 132 if (value instanceof Array) { 165 133 return value.map((item) => this.unwrap(item)) as NodeOfMaybe<I>; ··· 174 142 if (value === undefined) { 175 143 return undefined as TypeOfMaybe<I>; 176 144 } 145 + if (value instanceof Symbol) { 146 + return ts.factory.createTypeReferenceNode( 147 + value.finalName, 148 + args, 149 + ) as TypeOfMaybe<I>; 150 + } 177 151 if (typeof value === 'string') { 178 152 return ts.factory.createTypeReferenceNode(value, args) as TypeOfMaybe<I>; 179 153 } ··· 194 168 return this.unwrap(value as any) as TypeOfMaybe<I>; 195 169 } 196 170 197 - /** Returns the root symbol associated with this DSL subtree. */ 198 - protected getRootSymbol(): Symbol { 199 - if (!this.parent && !this.symbol) { 200 - const message = `${this.constructor.name} has neither a parent nor a symbol — root symbol resolution failed.`; 201 - debug(message, 'dsl'); 202 - throw new Error(message); 203 - } 204 - return this.parent ? this.parent.getRootSymbol() : this.symbol!.canonical; 205 - } 206 - 207 171 /** Unwraps nested nodes into raw TypeScript AST. */ 208 172 protected unwrap<I>(value: I): I extends TsDsl<infer N> ? N : I { 209 - return ( 210 - value instanceof TsDsl ? value._render() : value 211 - ) as I extends TsDsl<infer N> ? N : I; 173 + return (isTsDsl(value) ? value._render() : value) as I extends TsDsl< 174 + infer N 175 + > 176 + ? N 177 + : I; 212 178 } 213 179 214 - /** Validate DSL invariants. */ 180 + /** Validate invariants. */ 215 181 protected _validate(): void { 216 - if (!this.parent && !this.symbol) { 217 - const message = 218 - `${this.constructor.name}: node has neither a parent nor a symbol — ` + 219 - `this likely means the node was never attached to a parent or is incorrectly ` + 220 - `being treated as a root-level construct.`; 221 - debug(message, 'dsl'); 222 - // TODO: enable later 223 - // throw new Error(message); 224 - } 225 - 226 - if (this.parent && this.symbol) { 227 - const message = `${this.constructor.name}: non-top-level node must not have a symbol`; 228 - debug(message, 'dsl'); 229 - throw new Error(message); 230 - } 231 - 232 - if (this.parent === undefined && this.symbol === undefined) { 233 - const message = `${this.constructor.name}: non-root node is missing a parent`; 234 - debug(message, 'dsl'); 235 - // TODO: enable later 236 - // throw new Error(message); 237 - } 238 - 239 182 if (this.symbol && this.symbol.canonical !== this.symbol) { 240 183 const message = `${this.constructor.name}: node is holding a non-canonical (stub) symbol`; 241 184 debug(message, 'dsl'); 242 185 throw new Error(message); 243 186 } 244 - 245 - this.traverse((node) => { 246 - const dsl = node as TsDsl<any>; 247 - if (dsl !== this && !dsl.parent) { 248 - const message = `${dsl.constructor.name}: child node has missing parent`; 249 - debug(message, 'dsl'); 250 - throw new Error(message); 251 - } 252 - }); 253 187 } 254 188 } 255 189 ··· 300 234 : I extends ts.TypeNode 301 235 ? I 302 236 : never; 237 + 238 + export function isTsDsl(value: unknown): value is TsDsl { 239 + if (!value || typeof value !== 'object' || Array.isArray(value)) return false; 240 + const obj = value as { '~brand'?: unknown }; 241 + return obj['~brand'] === tsDslBrand && Object.hasOwn(obj, '~brand'); 242 + }
+35 -44
packages/openapi-ts/src/ts-dsl/decl/class.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 + import { isTsDsl, TsDsl } from '../base'; 6 7 import { NewlineTsDsl } from '../layout/newline'; 7 8 import { DecoratorMixin } from '../mixins/decorator'; 8 9 import { DocMixin } from '../mixins/doc'; ··· 12 13 import { InitTsDsl } from './init'; 13 14 import { MethodTsDsl } from './method'; 14 15 16 + type Base = Symbol | string; 17 + type Name = Symbol | string; 18 + type Body = Array<MaybeTsDsl<ts.ClassElement | ts.Node>>; 19 + 15 20 const Mixed = AbstractMixin( 16 21 DecoratorMixin( 17 22 DefaultMixin( ··· 21 26 ); 22 27 23 28 export class ClassTsDsl extends Mixed { 24 - protected baseClass?: Symbol | string; 25 - protected body: Array<MaybeTsDsl<ts.ClassElement | NewlineTsDsl>> = []; 26 - protected name: Symbol | string; 29 + protected baseClass?: Base; 30 + protected body: Body = []; 31 + protected name: Name; 27 32 28 - constructor(name: Symbol | string) { 33 + constructor(name: Name) { 29 34 super(); 30 - if (typeof name === 'string') { 31 - this.name = name; 32 - return; 33 - } 34 35 this.name = name; 35 - this.symbol = name; 36 - this.symbol.setKind('class'); 37 - this.symbol.setRootNode(this); 36 + if (isSymbol(name)) { 37 + name.setKind('class'); 38 + name.setNode(this); 39 + } 38 40 } 39 41 40 - override collectSymbols(out: Set<Symbol>): void { 41 - console.log(out); 42 + override analyze(ctx: AnalysisContext): void { 43 + super.analyze(ctx); 44 + if (isSymbol(this.baseClass)) ctx.addDependency(this.baseClass); 45 + if (isSymbol(this.name)) ctx.addDependency(this.name); 46 + for (const item of this.body) { 47 + if (isTsDsl(item)) item.analyze(ctx); 48 + } 42 49 } 43 50 44 51 /** Adds one or more class members (fields, methods, etc.). */ 45 - do(...items: ReadonlyArray<MaybeTsDsl<ts.ClassElement | ts.Node>>): this { 46 - for (const item of items) { 47 - if (item instanceof TsDsl) { 48 - item.setParent(this); 49 - } 50 - // @ts-expect-error --- IGNORE --- 51 - this.body.push(item); 52 - } 52 + do(...items: Body): this { 53 + this.body.push(...items); 53 54 return this; 54 55 } 55 56 56 - /** Records a base class to extend from without rendering early. */ 57 - extends(base?: Symbol | string): this { 58 - if (!base) return this; 57 + /** Records a base class to extend from. */ 58 + extends(base?: Base): this { 59 59 this.baseClass = base; 60 - if (typeof base !== 'string') { 61 - this.getRootSymbol().addDependency(base); 62 - } 63 60 return this; 64 61 } 65 62 66 63 /** Adds a class field. */ 67 64 field(name: string, fn?: (f: FieldTsDsl) => void): this { 68 - const f = new FieldTsDsl(name, fn).setParent(this); 65 + const f = new FieldTsDsl(name, fn); 69 66 this.body.push(f); 70 67 return this; 71 68 } 72 69 73 70 /** Adds a class constructor. */ 74 71 init(fn?: (i: InitTsDsl) => void): this { 75 - const i = new InitTsDsl(fn).setParent(this); 72 + const i = new InitTsDsl(fn); 76 73 this.body.push(i); 77 74 return this; 78 75 } 79 76 80 77 /** Adds a class method. */ 81 78 method(name: string, fn?: (m: MethodTsDsl) => void): this { 82 - const m = new MethodTsDsl(name, fn).setParent(this); 79 + const m = new MethodTsDsl(name, fn); 83 80 this.body.push(m); 84 81 return this; 85 82 } ··· 88 85 newline(): this { 89 86 this.body.push(new NewlineTsDsl()); 90 87 return this; 91 - } 92 - 93 - override traverse(visitor: (node: SyntaxNode) => void): void { 94 - super.traverse(visitor); 95 88 } 96 89 97 90 protected override _render() { 98 91 const body = this.$node(this.body) as ReadonlyArray<ts.ClassElement>; 99 - const name = 100 - typeof this.name === 'string' ? this.name : this.name.finalName; 101 92 return ts.factory.createClassDeclaration( 102 93 [...this.$decorators(), ...this.modifiers], 103 - name, 94 + // @ts-expect-error need to improve types 95 + this.$node(this.name), 104 96 this.$generics(), 105 97 this._renderHeritage(), 106 98 body, ··· 110 102 /** Builds heritage clauses (extends). */ 111 103 private _renderHeritage(): ReadonlyArray<ts.HeritageClause> { 112 104 if (!this.baseClass) return []; 113 - const id = 114 - typeof this.baseClass === 'string' 115 - ? this.$maybeId(this.baseClass) 116 - : this.$maybeId(this.baseClass.finalName); 117 105 return [ 118 106 ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ 119 - ts.factory.createExpressionWithTypeArguments(id, undefined), 107 + ts.factory.createExpressionWithTypeArguments( 108 + this.$node(this.baseClass), 109 + undefined, 110 + ), 120 111 ]), 121 112 ]; 122 113 }
+15 -19
packages/openapi-ts/src/ts-dsl/decl/decorator.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 2 - import { Symbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 3 import ts from 'typescript'; 4 4 5 5 import type { MaybeTsDsl } from '../base'; 6 - import { TsDsl } from '../base'; 6 + import { isTsDsl, TsDsl } from '../base'; 7 7 import { ArgsMixin } from '../mixins/args'; 8 + 9 + export type DecoratorName = Symbol | string | MaybeTsDsl<ts.Expression>; 8 10 9 11 const Mixed = ArgsMixin(TsDsl<ts.Decorator>); 10 12 11 13 export class DecoratorTsDsl extends Mixed { 12 - protected name: Symbol | string | MaybeTsDsl<ts.Expression>; 14 + protected name: DecoratorName; 13 15 14 16 constructor( 15 - name: Symbol | string | MaybeTsDsl<ts.Expression>, 17 + name: DecoratorName, 16 18 ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 17 19 ) { 18 20 super(); 19 21 this.name = name; 20 - if (name instanceof Symbol) { 21 - this.getRootSymbol().addDependency(name); 22 - } 23 22 this.args(...args); 24 23 } 25 24 26 - override collectSymbols(out: Set<Symbol>): void { 27 - console.log(out); 28 - } 29 - 30 - override traverse(visitor: (node: SyntaxNode) => void): void { 31 - super.traverse(visitor); 25 + override analyze(ctx: AnalysisContext): void { 26 + super.analyze(ctx); 27 + if (isSymbol(this.name)) { 28 + ctx.addDependency(this.name); 29 + } else if (isTsDsl(this.name)) { 30 + this.name.analyze(ctx); 31 + } 32 32 } 33 33 34 34 protected override _render() { 35 - const target = 36 - this.name instanceof Symbol 37 - ? this.$maybeId(this.name.finalName) 38 - : this.$node(this.name); 39 - 35 + const target = this.$node(this.name); 40 36 const args = this.$args(); 41 37 return ts.factory.createDecorator( 42 38 args.length
+19 -15
packages/openapi-ts/src/ts-dsl/decl/enum.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; ··· 7 8 import { ConstMixin, ExportMixin } from '../mixins/modifiers'; 8 9 import { EnumMemberTsDsl } from './member'; 9 10 11 + export type EnumName = Symbol | string; 10 12 type Value = string | number | MaybeTsDsl<ts.Expression>; 11 13 type ValueFn = Value | ((m: EnumMemberTsDsl) => void); 12 14 ··· 14 16 15 17 export class EnumTsDsl extends Mixed { 16 18 private _members: Array<EnumMemberTsDsl> = []; 17 - private _name: string | ts.Identifier; 19 + private _name: EnumName; 18 20 19 - constructor(name: Symbol | string, fn?: (e: EnumTsDsl) => void) { 21 + constructor(name: EnumName, fn?: (e: EnumTsDsl) => void) { 20 22 super(); 21 - if (typeof name === 'string') { 22 - this._name = name; 23 - } else { 24 - this._name = name.finalName; 25 - this.symbol = name; 26 - this.symbol.setKind('enum'); 27 - this.symbol.setRootNode(this); 23 + this._name = name; 24 + if (isSymbol(name)) { 25 + name.setKind('enum'); 26 + name.setNode(this); 28 27 } 29 28 fn?.(this); 30 29 } 31 30 31 + override analyze(ctx: AnalysisContext): void { 32 + super.analyze(ctx); 33 + if (isSymbol(this._name)) ctx.addDependency(this._name); 34 + for (const member of this._members) { 35 + member.analyze(ctx); 36 + } 37 + } 38 + 32 39 /** Adds an enum member. */ 33 40 member(name: string, value?: ValueFn): this { 34 41 const m = new EnumMemberTsDsl(name, value); ··· 42 49 return this; 43 50 } 44 51 45 - override traverse(visitor: (node: SyntaxNode) => void): void { 46 - super.traverse(visitor); 47 - } 48 - 49 52 protected override _render() { 50 53 return ts.factory.createEnumDeclaration( 51 54 this.modifiers, 52 - this._name, 55 + // @ts-expect-error need to improve types 56 + this.$node(this._name), 53 57 this.$node(this._members), 54 58 ); 55 59 }
+16 -7
packages/openapi-ts/src/ts-dsl/decl/field.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 - import { TsDsl, TypeTsDsl } from '../base'; 5 + import { isTsDsl, TsDsl, TypeTsDsl } from '../base'; 5 6 import { DecoratorMixin } from '../mixins/decorator'; 6 7 import { DocMixin } from '../mixins/doc'; 7 8 import { ··· 12 13 StaticMixin, 13 14 } from '../mixins/modifiers'; 14 15 import { ValueMixin } from '../mixins/value'; 16 + import type { TypeExprName } from '../type/expr'; 15 17 import { TypeExprTsDsl } from '../type/expr'; 18 + 19 + export type FieldType = TypeExprName | TypeTsDsl; 16 20 17 21 const Mixed = DecoratorMixin( 18 22 DocMixin( ··· 36 40 fn?.(this); 37 41 } 38 42 43 + override analyze(ctx: AnalysisContext): void { 44 + super.analyze(ctx); 45 + if (isSymbol(this._type)) { 46 + ctx.addDependency(this._type); 47 + } else if (isTsDsl(this._type)) { 48 + this._type.analyze(ctx); 49 + } 50 + } 51 + 39 52 /** Sets the field type. */ 40 - type(type: string | TypeTsDsl): this { 53 + type(type: FieldType): this { 41 54 this._type = type instanceof TypeTsDsl ? type : new TypeExprTsDsl(type); 42 55 return this; 43 - } 44 - 45 - override traverse(visitor: (node: SyntaxNode) => void): void { 46 - super.traverse(visitor); 47 56 } 48 57 49 58 protected override _render() {
+25 -22
packages/openapi-ts/src/ts-dsl/decl/func.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import { TsDsl, TypeTsDsl } from '../base'; ··· 18 19 import { TypeParamsMixin } from '../mixins/type-params'; 19 20 import { TypeExprTsDsl } from '../type/expr'; 20 21 21 - type FuncMode = 'arrow' | 'decl' | 'expr'; 22 + export type FuncMode = 'arrow' | 'decl' | 'expr'; 23 + export type FuncName = Symbol | string; 22 24 23 25 const Mixed = AbstractMixin( 24 26 AsMixin( ··· 44 46 45 47 class ImplFuncTsDsl<M extends FuncMode = 'arrow'> extends Mixed { 46 48 protected mode?: FuncMode; 47 - protected name?: string; 49 + protected name?: FuncName; 48 50 protected _returns?: TypeTsDsl; 49 51 50 52 constructor(); 51 53 constructor(fn: (f: ImplFuncTsDsl<'arrow'>) => void); 52 - constructor(name: Symbol | string); 53 - constructor(name: Symbol | string, fn: (f: ImplFuncTsDsl<'decl'>) => void); 54 + constructor(name: FuncName); 55 + constructor(name: FuncName, fn: (f: ImplFuncTsDsl<'decl'>) => void); 54 56 constructor( 55 - nameOrFn?: Symbol | string | ((f: ImplFuncTsDsl<'arrow'>) => void), 57 + name?: FuncName | ((f: ImplFuncTsDsl<'arrow'>) => void), 56 58 fn?: (f: ImplFuncTsDsl<'decl'>) => void, 57 59 ) { 58 60 super(); 59 - if (typeof nameOrFn === 'function') { 61 + if (typeof name === 'function') { 60 62 this.mode = 'arrow'; 61 - nameOrFn(this as unknown as FuncTsDsl<'arrow'>); 62 - } else if (nameOrFn) { 63 + name(this as unknown as FuncTsDsl<'arrow'>); 64 + } else if (name) { 63 65 this.mode = 'decl'; 64 - if (typeof nameOrFn === 'string') { 65 - this.name = nameOrFn; 66 - } else { 67 - this.name = nameOrFn.finalName; 68 - this.symbol = nameOrFn; 69 - this.symbol.setKind('function'); 70 - this.symbol.setRootNode(this); 66 + this.name = name; 67 + if (isSymbol(name)) { 68 + name.setKind('function'); 69 + name.setNode(this); 71 70 } 72 71 fn?.(this as unknown as FuncTsDsl<'decl'>); 73 72 } 74 73 } 75 74 75 + override analyze(ctx: AnalysisContext): void { 76 + super.analyze(ctx); 77 + if (isSymbol(this.name)) ctx.addDependency(this.name); 78 + this._returns?.analyze(ctx); 79 + } 80 + 76 81 /** Switches the function to an arrow function form. */ 77 82 arrow(): FuncTsDsl<'arrow'> { 78 83 this.mode = 'arrow'; ··· 97 102 return this; 98 103 } 99 104 100 - override traverse(visitor: (node: SyntaxNode) => void): void { 101 - super.traverse(visitor); 102 - } 103 - 104 105 // @ts-expect-error --- need to fix types --- 105 106 protected override _render(): M extends 'decl' 106 107 ? ts.FunctionDeclaration ··· 112 113 return ts.factory.createFunctionDeclaration( 113 114 [...this.$decorators(), ...this.modifiers], 114 115 undefined, 115 - this.name, 116 + // @ts-expect-error need to improve types 117 + this.$node(this.name), 116 118 this.$generics(), 117 119 this.$params(), 118 120 this.$type(this._returns), ··· 124 126 return ts.factory.createFunctionExpression( 125 127 this.modifiers, 126 128 undefined, 127 - this.name, 129 + // @ts-expect-error need to improve types 130 + this.$node(this.name), 128 131 this.$generics(), 129 132 this.$params(), 130 133 this.$type(this._returns),
+3 -3
packages/openapi-ts/src/ts-dsl/decl/getter.ts
··· 1 - import type { SyntaxNode } 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'; ··· 42 42 fn?.(this); 43 43 } 44 44 45 - override traverse(visitor: (node: SyntaxNode) => void): void { 46 - super.traverse(visitor); 45 + override analyze(ctx: AnalysisContext): void { 46 + super.analyze(ctx); 47 47 } 48 48 49 49 protected override _render() {
+3 -3
packages/openapi-ts/src/ts-dsl/decl/init.ts
··· 1 - import type { SyntaxNode } 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'; ··· 26 26 fn?.(this); 27 27 } 28 28 29 - override traverse(visitor: (node: SyntaxNode) => void): void { 30 - super.traverse(visitor); 29 + override analyze(ctx: AnalysisContext): void { 30 + super.analyze(ctx); 31 31 } 32 32 33 33 protected override _render() {
+5 -4
packages/openapi-ts/src/ts-dsl/decl/member.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 import { DocMixin } from '../mixins/doc'; 7 7 import { safeMemberName } from '../utils/prop'; 8 8 ··· 25 25 } 26 26 } 27 27 28 - override traverse(visitor: (node: SyntaxNode) => void): void { 29 - super.traverse(visitor); 28 + override analyze(ctx: AnalysisContext): void { 29 + super.analyze(ctx); 30 + if (isTsDsl(this._value)) this._value.analyze(ctx); 30 31 } 31 32 32 33 /** Sets the enum member value. */
+6 -5
packages/openapi-ts/src/ts-dsl/decl/method.ts
··· 1 - import type { SyntaxNode } 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, TypeTsDsl } from '../base'; ··· 51 51 fn?.(this); 52 52 } 53 53 54 + override analyze(ctx: AnalysisContext): void { 55 + super.analyze(ctx); 56 + this._returns?.analyze(ctx); 57 + } 58 + 54 59 /** Sets the return type. */ 55 60 returns(type: string | TypeTsDsl): this { 56 61 this._returns = type instanceof TypeTsDsl ? type : new TypeExprTsDsl(type); 57 62 return this; 58 - } 59 - 60 - override traverse(visitor: (node: SyntaxNode) => void): void { 61 - super.traverse(visitor); 62 63 } 63 64 64 65 protected override _render() {
+4 -3
packages/openapi-ts/src/ts-dsl/decl/param.ts
··· 1 - import type { SyntaxNode } 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, TypeTsDsl } from '../base'; ··· 30 30 } 31 31 } 32 32 33 - override traverse(visitor: (node: SyntaxNode) => void): void { 34 - super.traverse(visitor); 33 + override analyze(ctx: AnalysisContext): void { 34 + super.analyze(ctx); 35 + this._type?.analyze(ctx); 35 36 } 36 37 37 38 /** Sets the parameter type. */
+5 -5
packages/openapi-ts/src/ts-dsl/decl/pattern.ts
··· 1 - import type { SyntaxNode } 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'; ··· 16 16 | { kind: 'array'; values: ReadonlyArray<string> } 17 17 | { kind: 'object'; values: Record<string, string> }; 18 18 protected _spread?: string; 19 + 20 + override analyze(ctx: AnalysisContext): void { 21 + super.analyze(ctx); 22 + } 19 23 20 24 /** Defines an array pattern (e.g. `[a, b, c]`). */ 21 25 array(...props: ReadonlyArray<string> | [ReadonlyArray<string>]): this { ··· 45 49 spread(name: string): this { 46 50 this._spread = name; 47 51 return this; 48 - } 49 - 50 - override traverse(visitor: (node: SyntaxNode) => void): void { 51 - super.traverse(visitor); 52 52 } 53 53 54 54 protected override _render() {
+3 -3
packages/openapi-ts/src/ts-dsl/decl/setter.ts
··· 1 - import type { SyntaxNode } 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'; ··· 42 42 fn?.(this); 43 43 } 44 44 45 - override traverse(visitor: (node: SyntaxNode) => void): void { 46 - super.traverse(visitor); 45 + override analyze(ctx: AnalysisContext): void { 46 + super.analyze(ctx); 47 47 } 48 48 49 49 protected override _render() {
+9 -6
packages/openapi-ts/src/ts-dsl/expr/array.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 import { AsMixin } from '../mixins/as'; 7 7 import { LayoutMixin } from '../mixins/layout'; 8 8 import { LiteralTsDsl } from './literal'; ··· 22 22 this.elements(...exprs); 23 23 } 24 24 25 + override analyze(ctx: AnalysisContext): void { 26 + super.analyze(ctx); 27 + for (const item of this._elements) { 28 + if (isTsDsl(item.expr)) item.expr.analyze(ctx); 29 + } 30 + } 31 + 25 32 /** Adds a single array element. */ 26 33 element(expr: string | number | boolean | MaybeTsDsl<ts.Expression>): this { 27 34 const node = ··· 48 55 spread(expr: MaybeTsDsl<ts.Expression>): this { 49 56 this._elements.push({ expr, kind: 'spread' }); 50 57 return this; 51 - } 52 - 53 - override traverse(visitor: (node: SyntaxNode) => void): void { 54 - super.traverse(visitor); 55 58 } 56 59 57 60 protected override _render() {
+24 -12
packages/openapi-ts/src/ts-dsl/expr/as.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl, TypeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 - import { AsMixin, registerLazyAccessAsFactory } from '../mixins/as'; 6 + import { isTsDsl, TsDsl } from '../base'; 7 + import { AsMixin, setAsFactory } from '../mixins/as'; 7 8 import { ExprMixin } from '../mixins/expr'; 8 9 10 + export type AsExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 11 + export type AsType = Symbol | string | TypeTsDsl; 12 + export type AsCtor = (expr: AsExpr, type: AsType) => AsTsDsl; 13 + 9 14 const Mixed = AsMixin(ExprMixin(TsDsl<ts.AsExpression>)); 10 15 11 16 export class AsTsDsl extends Mixed { 12 - protected expr: string | MaybeTsDsl<ts.Expression>; 13 - protected type: string | TypeTsDsl; 17 + protected expr: AsExpr; 18 + protected type: AsType; 14 19 15 - constructor( 16 - expr: string | MaybeTsDsl<ts.Expression>, 17 - type: string | TypeTsDsl, 18 - ) { 20 + constructor(expr: AsExpr, type: AsType) { 19 21 super(); 20 22 this.expr = expr; 21 23 this.type = type; 22 24 } 23 25 24 - override traverse(visitor: (node: SyntaxNode) => void): void { 25 - super.traverse(visitor); 26 + override analyze(ctx: AnalysisContext): void { 27 + super.analyze(ctx); 28 + if (isSymbol(this.expr)) { 29 + ctx.addDependency(this.expr); 30 + } else if (isTsDsl(this.expr)) { 31 + this.expr.analyze(ctx); 32 + } 33 + if (isSymbol(this.type)) { 34 + ctx.addDependency(this.type); 35 + } else if (isTsDsl(this.type)) { 36 + this.type.analyze(ctx); 37 + } 26 38 } 27 39 28 40 protected override _render() { ··· 33 45 } 34 46 } 35 47 36 - registerLazyAccessAsFactory((...args) => new AsTsDsl(...args)); 48 + setAsFactory((...args) => new AsTsDsl(...args));
+24 -14
packages/openapi-ts/src/ts-dsl/expr/attr.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import { validTypescriptIdentifierRegExp } from '~/utils/regexp'; 5 6 6 7 import type { MaybeTsDsl } from '../base'; 7 - import { TsDsl } from '../base'; 8 + import { isTsDsl, TsDsl } from '../base'; 8 9 import { AsMixin } from '../mixins/as'; 9 - import { ExprMixin, registerLazyAccessAttrFactory } from '../mixins/expr'; 10 + import { ExprMixin, setAttrFactory } from '../mixins/expr'; 10 11 import { OperatorMixin } from '../mixins/operator'; 11 12 import { OptionalMixin } from '../mixins/optional'; 12 13 import { TokenTsDsl } from '../token'; 13 14 import { LiteralTsDsl } from './literal'; 14 15 16 + export type AttrLeft = Symbol | string | MaybeTsDsl<ts.Expression>; 17 + export type AttrRight = Symbol | string | ts.MemberName | number; 18 + export type AttrCtor = (left: AttrLeft, right: AttrRight) => AttrTsDsl; 19 + 15 20 const Mixed = AsMixin( 16 21 ExprMixin( 17 22 OperatorMixin( ··· 23 28 ); 24 29 25 30 export class AttrTsDsl extends Mixed { 26 - protected left: string | MaybeTsDsl<ts.Expression>; 27 - protected right: string | ts.MemberName | number; 31 + protected left: AttrLeft; 32 + protected right: AttrRight; 28 33 29 - constructor( 30 - left: string | MaybeTsDsl<ts.Expression>, 31 - right: string | ts.MemberName | number, 32 - ) { 34 + constructor(left: AttrLeft, right: AttrRight) { 33 35 super(); 34 36 this.left = left; 35 37 this.right = right; 36 38 } 37 39 38 - override traverse(visitor: (node: SyntaxNode) => void): void { 39 - super.traverse(visitor); 40 + override analyze(ctx: AnalysisContext): void { 41 + super.analyze(ctx); 42 + if (isSymbol(this.left)) { 43 + ctx.addDependency(this.left); 44 + } else if (isTsDsl(this.left)) { 45 + this.left.analyze(ctx); 46 + } 47 + if (isSymbol(this.right)) ctx.addDependency(this.right); 40 48 } 41 49 42 50 protected override _render() { ··· 63 71 return ts.factory.createPropertyAccessChain( 64 72 leftNode, 65 73 this.$node(new TokenTsDsl().questionDot()), 66 - this.$maybeId(this.right), 74 + // @ts-expect-error ts.MemberName is not properly recognized here 75 + this.$node(this.right), 67 76 ); 68 77 } 69 78 return ts.factory.createPropertyAccessExpression( 70 79 leftNode, 71 - this.$maybeId(this.right), 80 + // @ts-expect-error ts.MemberName is not properly recognized here 81 + this.$node(this.right), 72 82 ); 73 83 } 74 84 } 75 85 76 - registerLazyAccessAttrFactory((...args) => new AttrTsDsl(...args)); 86 + setAttrFactory((...args) => new AttrTsDsl(...args));
+17 -8
packages/openapi-ts/src/ts-dsl/expr/await.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 - import { ExprMixin, registerLazyAccessAwaitFactory } from '../mixins/expr'; 6 + import { isTsDsl, TsDsl } from '../base'; 7 + import { ExprMixin, setAwaitFactory } from '../mixins/expr'; 8 + 9 + export type AwaitExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 10 + export type AwaitCtor = (expr: AwaitExpr) => AwaitTsDsl; 7 11 8 12 const Mixed = ExprMixin(TsDsl<ts.AwaitExpression>); 9 13 10 14 export class AwaitTsDsl extends Mixed { 11 - protected _awaitExpr: string | MaybeTsDsl<ts.Expression>; 15 + protected _awaitExpr: AwaitExpr; 12 16 13 - constructor(expr: string | MaybeTsDsl<ts.Expression>) { 17 + constructor(expr: AwaitExpr) { 14 18 super(); 15 19 this._awaitExpr = expr; 16 20 } 17 21 18 - override traverse(visitor: (node: SyntaxNode) => void): void { 19 - super.traverse(visitor); 22 + override analyze(ctx: AnalysisContext): void { 23 + super.analyze(ctx); 24 + if (isSymbol(this._awaitExpr)) { 25 + ctx.addDependency(this._awaitExpr); 26 + } else if (isTsDsl(this._awaitExpr)) { 27 + this._awaitExpr.analyze(ctx); 28 + } 20 29 } 21 30 22 31 protected override _render() { ··· 24 33 } 25 34 } 26 35 27 - registerLazyAccessAwaitFactory((...args) => new AwaitTsDsl(...args)); 36 + setAwaitFactory((...args) => new AwaitTsDsl(...args));
+18 -7
packages/openapi-ts/src/ts-dsl/expr/binary.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 + import { isTsDsl, TsDsl } from '../base'; 6 7 import { AsMixin } from '../mixins/as'; 7 8 import { ExprMixin } from '../mixins/expr'; 8 9 9 - type Expr = string | MaybeTsDsl<ts.Expression>; 10 + type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 10 11 type Op = Operator | ts.BinaryOperator; 11 12 type Operator = 12 13 | '!=' ··· 38 39 this._base = base; 39 40 this._op = op; 40 41 this._expr = expr; 42 + } 43 + 44 + override analyze(ctx: AnalysisContext): void { 45 + super.analyze(ctx); 46 + if (isSymbol(this._base)) { 47 + ctx.addDependency(this._base); 48 + } else if (isTsDsl(this._base)) { 49 + this._base.analyze(ctx); 50 + } 51 + if (isSymbol(this._expr)) { 52 + ctx.addDependency(this._expr); 53 + } else if (isTsDsl(this._expr)) { 54 + this._expr.analyze(ctx); 55 + } 41 56 } 42 57 43 58 /** Logical AND — `this && expr` */ ··· 118 133 /** Multiplication — `this * expr` */ 119 134 times(expr: Expr): this { 120 135 return this.opAndExpr('*', expr); 121 - } 122 - 123 - override traverse(visitor: (node: SyntaxNode) => void): void { 124 - super.traverse(visitor); 125 136 } 126 137 127 138 protected override _render() {
+15 -14
packages/openapi-ts/src/ts-dsl/expr/call.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 import { ArgsMixin } from '../mixins/args'; 7 7 import { AsMixin } from '../mixins/as'; 8 - import { ExprMixin, registerLazyAccessCallFactory } from '../mixins/expr'; 8 + import { ExprMixin, setCallFactory } from '../mixins/expr'; 9 9 import { TypeArgsMixin } from '../mixins/type-args'; 10 10 11 + export type CallCallee = string | MaybeTsDsl<ts.Expression>; 12 + export type CallArg = Symbol | string | MaybeTsDsl<ts.Expression>; 13 + export type CallArgs = ReadonlyArray<CallArg | undefined>; 14 + export type CallCtor = (callee: CallCallee, ...args: CallArgs) => CallTsDsl; 15 + 11 16 const Mixed = ArgsMixin( 12 17 AsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.CallExpression>))), 13 18 ); 14 19 15 20 export class CallTsDsl extends Mixed { 16 - protected _callee: string | MaybeTsDsl<ts.Expression>; 21 + protected _callee: CallCallee; 17 22 18 - constructor( 19 - callee: string | MaybeTsDsl<ts.Expression>, 20 - ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression> | undefined> 21 - ) { 23 + constructor(callee: CallCallee, ...args: CallArgs) { 22 24 super(); 23 25 this._callee = callee; 24 - this.args( 25 - ...args.filter((a): a is NonNullable<typeof a> => a !== undefined), 26 - ); 26 + this.args(...args); 27 27 } 28 28 29 - override traverse(visitor: (node: SyntaxNode) => void): void { 30 - super.traverse(visitor); 29 + override analyze(ctx: AnalysisContext): void { 30 + super.analyze(ctx); 31 + if (isTsDsl(this._callee)) this._callee.analyze(ctx); 31 32 } 32 33 33 34 protected override _render() { ··· 39 40 } 40 41 } 41 42 42 - registerLazyAccessCallFactory((expr, args) => new CallTsDsl(expr, ...args)); 43 + setCallFactory((...args) => new CallTsDsl(...args));
+14 -6
packages/openapi-ts/src/ts-dsl/expr/expr.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import type ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 + import { isTsDsl, TsDsl } from '../base'; 6 7 import { AsMixin } from '../mixins/as'; 7 8 import { ExprMixin } from '../mixins/expr'; 8 9 import { OperatorMixin } from '../mixins/operator'; 9 10 import { TypeExprMixin } from '../mixins/type-expr'; 10 11 12 + type Id = Symbol | string | MaybeTsDsl<ts.Expression>; 13 + 11 14 const Mixed = AsMixin( 12 15 ExprMixin(OperatorMixin(TypeExprMixin(TsDsl<ts.Expression>))), 13 16 ); 14 17 15 18 export class ExprTsDsl extends Mixed { 16 - protected _exprInput: string | MaybeTsDsl<ts.Expression>; 19 + protected _exprInput: Id; 17 20 18 - constructor(id: string | MaybeTsDsl<ts.Expression>) { 21 + constructor(id: Id) { 19 22 super(); 20 23 this._exprInput = id; 21 24 } 22 25 23 - override traverse(visitor: (node: SyntaxNode) => void): void { 24 - super.traverse(visitor); 26 + override analyze(ctx: AnalysisContext): void { 27 + super.analyze(ctx); 28 + if (isSymbol(this._exprInput)) { 29 + ctx.addDependency(this._exprInput); 30 + } else if (isTsDsl(this._exprInput)) { 31 + this._exprInput.analyze(ctx); 32 + } 25 33 } 26 34 27 35 protected override _render() {
+4 -3
packages/openapi-ts/src/ts-dsl/expr/fromValue.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import { TsDsl } from '../base'; 3 + import type { TsDsl } from '../base'; 4 + import { isTsDsl } from '../base'; 4 5 import { ArrayTsDsl } from './array'; 5 6 import { LiteralTsDsl } from './literal'; 6 7 import { ObjectTsDsl } from './object'; ··· 11 12 layout?: 'pretty'; 12 13 }, 13 14 ): TsDsl<ts.Expression> => { 14 - if (input instanceof TsDsl) { 15 - return input; 15 + if (isTsDsl(input)) { 16 + return input as TsDsl<ts.Expression>; 16 17 } 17 18 18 19 if (input === null) {
+3 -3
packages/openapi-ts/src/ts-dsl/expr/id.ts
··· 1 - import type { SyntaxNode } 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'; ··· 13 13 this.name = name; 14 14 } 15 15 16 - override traverse(visitor: (node: SyntaxNode) => void): void { 17 - super.traverse(visitor); 16 + override analyze(ctx: AnalysisContext): void { 17 + super.analyze(ctx); 18 18 } 19 19 20 20 protected override _render() {
+3 -3
packages/openapi-ts/src/ts-dsl/expr/literal.ts
··· 1 - import type { SyntaxNode } 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'; ··· 15 15 this.value = value; 16 16 } 17 17 18 - override traverse(visitor: (node: SyntaxNode) => void): void { 19 - super.traverse(visitor); 18 + override analyze(ctx: AnalysisContext): void { 19 + super.analyze(ctx); 20 20 } 21 21 22 22 protected override _render() {
+14 -9
packages/openapi-ts/src/ts-dsl/expr/new.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 + import { isTsDsl, TsDsl } from '../base'; 6 7 import { ArgsMixin } from '../mixins/args'; 7 8 import { ExprMixin } from '../mixins/expr'; 8 9 import { TypeArgsMixin } from '../mixins/type-args'; 9 10 11 + export type NewExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 12 + 10 13 const Mixed = ArgsMixin(ExprMixin(TypeArgsMixin(TsDsl<ts.NewExpression>))); 11 14 12 15 export class NewTsDsl extends Mixed { 13 - protected classExpr: string | MaybeTsDsl<ts.Expression>; 16 + protected classExpr: NewExpr; 14 17 15 - constructor( 16 - classExpr: string | MaybeTsDsl<ts.Expression>, 17 - ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 18 - ) { 18 + constructor(classExpr: NewExpr, ...args: ReadonlyArray<NewExpr>) { 19 19 super(); 20 20 this.classExpr = classExpr; 21 21 this.args(...args); 22 22 } 23 23 24 - override traverse(visitor: (node: SyntaxNode) => void): void { 25 - super.traverse(visitor); 24 + override analyze(ctx: AnalysisContext): void { 25 + super.analyze(ctx); 26 + if (isSymbol(this.classExpr)) { 27 + ctx.addDependency(this.classExpr); 28 + } else if (isTsDsl(this.classExpr)) { 29 + this.classExpr.analyze(ctx); 30 + } 26 31 } 27 32 28 33 protected override _render() {
+10 -7
packages/openapi-ts/src/ts-dsl/expr/object.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 4 import type { MaybeTsDsl } from '../base'; ··· 9 9 import { LayoutMixin } from '../mixins/layout'; 10 10 import { ObjectPropTsDsl } from './prop'; 11 11 12 - type Expr = string | MaybeTsDsl<ts.Expression>; 13 - type Stmt = string | MaybeTsDsl<ts.Statement>; 12 + type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 13 + type Stmt = Symbol | string | MaybeTsDsl<ts.Statement>; 14 14 type ExprFn = Expr | ((p: ObjectPropTsDsl) => void); 15 15 type StmtFn = Stmt | ((p: ObjectPropTsDsl) => void); 16 16 ··· 24 24 constructor(...props: Array<ObjectPropTsDsl>) { 25 25 super(); 26 26 this.props(...props); 27 + } 28 + 29 + override analyze(ctx: AnalysisContext): void { 30 + super.analyze(ctx); 31 + for (const prop of this._props) { 32 + prop.analyze(ctx); 33 + } 27 34 } 28 35 29 36 /** Adds a computed property (e.g. `{ [expr]: value }`). */ ··· 72 79 spread(expr: ExprFn): this { 73 80 this._props.push(new ObjectPropTsDsl({ kind: 'spread' }).value(expr)); 74 81 return this; 75 - } 76 - 77 - override traverse(visitor: (node: SyntaxNode) => void): void { 78 - super.traverse(visitor); 79 82 } 80 83 81 84 protected override _render() {
+7 -6
packages/openapi-ts/src/ts-dsl/expr/prefix.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 7 7 const Mixed = TsDsl<ts.PrefixUnaryExpression>; 8 8 ··· 17 17 super(); 18 18 this._expr = expr; 19 19 this._op = op; 20 + } 21 + 22 + override analyze(ctx: AnalysisContext): void { 23 + super.analyze(ctx); 24 + if (isTsDsl(this._expr)) this._expr.analyze(ctx); 20 25 } 21 26 22 27 /** Sets the operand (the expression being prefixed). */ ··· 41 46 op(op: ts.PrefixUnaryOperator): this { 42 47 this._op = op; 43 48 return this; 44 - } 45 - 46 - override traverse(visitor: (node: SyntaxNode) => void): void { 47 - super.traverse(visitor); 48 49 } 49 50 50 51 protected override _render() {
+14 -8
packages/openapi-ts/src/ts-dsl/expr/prop.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 + import { isTsDsl, TsDsl } from '../base'; 6 7 import { GetterTsDsl } from '../decl/getter'; 7 8 import { SetterTsDsl } from '../decl/setter'; 8 9 import { DocMixin } from '../mixins/doc'; 9 10 import { safePropName } from '../utils/prop'; 10 11 import { IdTsDsl } from './id'; 11 12 12 - type Expr = string | MaybeTsDsl<ts.Expression>; 13 - type Stmt = string | MaybeTsDsl<ts.Statement>; 13 + type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 14 + type Stmt = Symbol | string | MaybeTsDsl<ts.Statement>; 14 15 type Kind = 'computed' | 'getter' | 'prop' | 'setter' | 'spread'; 15 16 16 17 type Meta = ··· 31 32 this.meta = meta; 32 33 } 33 34 35 + override analyze(ctx: AnalysisContext): void { 36 + super.analyze(ctx); 37 + if (isSymbol(this._value)) { 38 + ctx.addDependency(this._value); 39 + } else if (isTsDsl(this._value)) { 40 + this._value.analyze(ctx); 41 + } 42 + } 43 + 34 44 /** Returns true when all required builder calls are present. */ 35 45 get isValid(): boolean { 36 46 return this.missingRequiredCalls().length === 0; 37 - } 38 - 39 - override traverse(visitor: (node: SyntaxNode) => void): void { 40 - super.traverse(visitor); 41 47 } 42 48 43 49 value(value: Expr | Stmt | ((p: ObjectPropTsDsl) => void)) {
+3 -3
packages/openapi-ts/src/ts-dsl/expr/regexp.ts
··· 1 - import type { SyntaxNode } 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'; ··· 23 23 this.flags = flags; 24 24 } 25 25 26 - override traverse(visitor: (node: SyntaxNode) => void): void { 27 - super.traverse(visitor); 26 + override analyze(ctx: AnalysisContext): void { 27 + super.analyze(ctx); 28 28 } 29 29 30 30 protected override _render() {
+9 -6
packages/openapi-ts/src/ts-dsl/expr/template.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 7 7 const Mixed = TsDsl<ts.TemplateExpression | ts.NoSubstitutionTemplateLiteral>; 8 8 ··· 14 14 if (value !== undefined) this.add(value); 15 15 } 16 16 17 + override analyze(ctx: AnalysisContext): void { 18 + super.analyze(ctx); 19 + for (const part of this.parts) { 20 + if (isTsDsl(part)) part.analyze(ctx); 21 + } 22 + } 23 + 17 24 add(value: string | MaybeTsDsl<ts.Expression>): this { 18 25 this.parts.push(value); 19 26 return this; 20 - } 21 - 22 - override traverse(visitor: (node: SyntaxNode) => void): void { 23 - super.traverse(visitor); 24 27 } 25 28 26 29 protected override _render() {
+9 -6
packages/openapi-ts/src/ts-dsl/expr/ternary.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 7 7 const Mixed = TsDsl<ts.ConditionalExpression>; 8 8 ··· 16 16 if (condition) this.condition(condition); 17 17 } 18 18 19 + override analyze(ctx: AnalysisContext): void { 20 + super.analyze(ctx); 21 + if (isTsDsl(this._condition)) this._condition.analyze(ctx); 22 + if (isTsDsl(this._then)) this._then.analyze(ctx); 23 + if (isTsDsl(this._else)) this._else.analyze(ctx); 24 + } 25 + 19 26 condition(condition: string | MaybeTsDsl<ts.Expression>) { 20 27 this._condition = condition; 21 28 return this; ··· 29 36 otherwise(expr: string | MaybeTsDsl<ts.Expression>) { 30 37 this._else = expr; 31 38 return this; 32 - } 33 - 34 - override traverse(visitor: (node: SyntaxNode) => void): void { 35 - super.traverse(visitor); 36 39 } 37 40 38 41 protected override _render() {
+5 -4
packages/openapi-ts/src/ts-dsl/expr/typeof.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 import { OperatorMixin } from '../mixins/operator'; 7 7 import { registerLazyAccessTypeOfExprFactory } from '../mixins/type-expr'; 8 8 ··· 16 16 this._expr = expr; 17 17 } 18 18 19 - override traverse(visitor: (node: SyntaxNode) => void): void { 20 - super.traverse(visitor); 19 + override analyze(ctx: AnalysisContext): void { 20 + super.analyze(ctx); 21 + if (isTsDsl(this._expr)) this._expr.analyze(ctx); 21 22 } 22 23 23 24 protected override _render() {
+1 -1
packages/openapi-ts/src/ts-dsl/index.ts
··· 338 338 }; 339 339 340 340 export type { MaybeTsDsl, TypeTsDsl } from './base'; 341 - export { TsDsl } from './base'; 341 + export { isTsDsl, TsDsl, tsDslBrand } from './base';
+5
packages/openapi-ts/src/ts-dsl/layout/doc.ts
··· 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 1 2 import ts from 'typescript'; 2 3 3 4 import type { MaybeArray } from '../base'; ··· 17 18 } 18 19 } 19 20 fn?.(this); 21 + } 22 + 23 + override analyze(ctx: AnalysisContext): void { 24 + super.analyze(ctx); 20 25 } 21 26 22 27 add(...lines: ReadonlyArray<string>): this {
+5
packages/openapi-ts/src/ts-dsl/layout/hint.ts
··· 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 1 2 import ts from 'typescript'; 2 3 3 4 import type { MaybeArray } from '../base'; ··· 17 18 } 18 19 } 19 20 fn?.(this); 21 + } 22 + 23 + override analyze(ctx: AnalysisContext): void { 24 + super.analyze(ctx); 20 25 } 21 26 22 27 add(...lines: ReadonlyArray<string>): this {
+5
packages/openapi-ts/src/ts-dsl/layout/newline.ts
··· 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 1 2 import type ts from 'typescript'; 2 3 3 4 import { TsDsl } from '../base'; 4 5 import { IdTsDsl } from '../expr/id'; 5 6 6 7 export class NewlineTsDsl extends TsDsl<ts.Identifier> { 8 + override analyze(ctx: AnalysisContext): void { 9 + super.analyze(ctx); 10 + } 11 + 7 12 protected override _render(): ts.Identifier { 8 13 return this.$node(new IdTsDsl('\n')); 9 14 }
+5
packages/openapi-ts/src/ts-dsl/layout/note.ts
··· 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 1 2 import ts from 'typescript'; 2 3 3 4 import type { MaybeArray } from '../base'; ··· 17 18 } 18 19 } 19 20 fn?.(this); 21 + } 22 + 23 + override analyze(ctx: AnalysisContext): void { 24 + super.analyze(ctx); 20 25 } 21 26 22 27 add(...lines: ReadonlyArray<string>): this {
+27 -13
packages/openapi-ts/src/ts-dsl/mixins/args.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import type ts from 'typescript'; 3 4 4 - import type { MaybeTsDsl } from '../base'; 5 + import { isTsDsl, type MaybeTsDsl } from '../base'; 5 6 import type { BaseCtor, MixinCtor } from './types'; 6 7 7 - export interface ArgsMethods extends SyntaxNode { 8 + type Arg = Symbol | string | MaybeTsDsl<ts.Expression>; 9 + 10 + export interface ArgsMethods extends Node { 8 11 /** Renders the arguments into an array of `Expression`s. */ 9 12 $args(): ReadonlyArray<ts.Expression>; 10 13 /** Adds a single expression argument. */ 11 - arg(arg: string | MaybeTsDsl<ts.Expression>): this; 14 + arg(arg: Arg | undefined): this; 12 15 /** Adds one or more expression arguments. */ 13 - args(...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>>): this; 16 + args(...args: ReadonlyArray<Arg | undefined>): this; 14 17 } 15 18 16 19 /** ··· 20 23 Base: TBase, 21 24 ) { 22 25 abstract class Args extends Base { 23 - protected _args: Array<string | MaybeTsDsl<ts.Expression>> = []; 26 + protected _args: Array<Arg> = []; 24 27 25 - protected arg(arg: string | MaybeTsDsl<ts.Expression>): this { 26 - this._args.push(arg); 28 + override analyze(ctx: AnalysisContext): void { 29 + super.analyze(ctx); 30 + for (const arg of this._args) { 31 + if (isSymbol(arg)) { 32 + ctx.addDependency(arg); 33 + } else if (isTsDsl(arg)) { 34 + arg.analyze(ctx); 35 + } 36 + } 37 + } 38 + 39 + protected arg(arg: Arg | undefined): this { 40 + if (arg !== undefined) this._args.push(arg); 27 41 return this; 28 42 } 29 43 30 - protected args( 31 - ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 32 - ): this { 33 - this._args.push(...args); 44 + protected args(...args: ReadonlyArray<Arg | undefined>): this { 45 + this._args.push( 46 + ...args.filter((a): a is NonNullable<typeof a> => a !== undefined), 47 + ); 34 48 return this; 35 49 } 36 50 37 51 protected $args(): ReadonlyArray<ts.Expression> { 38 - return this.$node(this._args).map((arg) => this.$maybeId(arg)); 52 + return this.$node(this._args).map((arg) => this.$node(arg)); 39 53 } 40 54 } 41 55
+11 -16
packages/openapi-ts/src/ts-dsl/mixins/as.ts
··· 1 - import type { SyntaxNode } 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 - import type { MaybeTsDsl, TypeTsDsl } from '../base'; 5 - import type { AsTsDsl } from '../expr/as'; 4 + import type { AsCtor, AsTsDsl, AsType } from '../expr/as'; 6 5 import type { BaseCtor, MixinCtor } from './types'; 7 6 8 - type AsFactory = ( 9 - expr: string | MaybeTsDsl<ts.Expression>, 10 - type: string | TypeTsDsl, 11 - ) => AsTsDsl; 12 - let asFactory: AsFactory | undefined; 13 - /** Registers the As DSL factory after its module has finished evaluating. */ 14 - export function registerLazyAccessAsFactory(factory: AsFactory): void { 7 + let asFactory: AsCtor | undefined; 8 + /** Lazy register the factory to avoid circular imports. */ 9 + export function setAsFactory(factory: AsCtor): void { 15 10 asFactory = factory; 16 11 } 17 12 18 - export interface AsMethods extends SyntaxNode { 13 + export interface AsMethods extends Node { 19 14 /** Creates an `as` type assertion expression (e.g. `value as Type`). */ 20 - as(type: string | TypeTsDsl): AsTsDsl; 15 + as(type: AsType): AsTsDsl; 21 16 } 22 17 23 18 export function AsMixin<T extends ts.Expression, TBase extends BaseCtor<T>>( 24 19 Base: TBase, 25 20 ) { 26 21 abstract class As extends Base { 27 - protected as(type: string | TypeTsDsl): AsTsDsl { 28 - return asFactory!(this, type); 22 + override analyze(ctx: AnalysisContext): void { 23 + super.analyze(ctx); 29 24 } 30 25 31 - override traverse(visitor: (node: SyntaxNode) => void): void { 32 - super.traverse(visitor); 26 + protected as(type: AsType): AsTsDsl { 27 + return asFactory!(this, type); 33 28 } 34 29 } 35 30
+11 -4
packages/openapi-ts/src/ts-dsl/mixins/decorator.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, Symbol } from '@hey-api/codegen-core'; 2 2 import type ts from 'typescript'; 3 3 4 - import type { MaybeTsDsl } from '../base'; 4 + import { isTsDsl, type MaybeTsDsl } from '../base'; 5 5 import { DecoratorTsDsl } from '../decl/decorator'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export interface DecoratorMethods extends SyntaxNode { 8 + export interface DecoratorMethods extends Node { 9 9 /** Renders the decorators into an array of `ts.Decorator`s. */ 10 10 $decorators(): ReadonlyArray<ts.Decorator>; 11 11 /** Adds a decorator (e.g. `@sealed({ in: 'root' })`). */ ··· 21 21 abstract class Decorator extends Base { 22 22 protected decorators: Array<DecoratorTsDsl> = []; 23 23 24 + override analyze(ctx: AnalysisContext): void { 25 + super.analyze(ctx); 26 + for (const decorator of this.decorators) { 27 + if (isTsDsl(decorator)) decorator.analyze(ctx); 28 + } 29 + } 30 + 24 31 protected decorator( 25 32 name: Symbol | string | MaybeTsDsl<ts.Expression>, 26 33 ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression>> 27 34 ): this { 28 - this.decorators.push(new DecoratorTsDsl(name, ...args).setParent(this)); 35 + this.decorators.push(new DecoratorTsDsl(name, ...args)); 29 36 return this; 30 37 } 31 38
+10 -3
packages/openapi-ts/src/ts-dsl/mixins/do.ts
··· 1 - import type { SyntaxNode } 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 - import type { MaybeTsDsl } from '../base'; 4 + import { isTsDsl, type MaybeTsDsl } from '../base'; 5 5 import { StmtTsDsl } from '../stmt/stmt'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export interface DoMethods extends SyntaxNode { 8 + export interface DoMethods extends Node { 9 9 /** Renders the collected `.do()` calls into an array of `Statement` nodes. */ 10 10 $do(): ReadonlyArray<ts.Statement>; 11 11 /** Adds one or more expressions/statements to the body. */ ··· 20 20 ) { 21 21 abstract class Do extends Base { 22 22 protected _do: Array<MaybeTsDsl<ts.Expression | ts.Statement>> = []; 23 + 24 + override analyze(ctx: AnalysisContext): void { 25 + super.analyze(ctx); 26 + for (const item of this._do) { 27 + if (isTsDsl(item)) item.analyze(ctx); 28 + } 29 + } 23 30 24 31 protected do( 25 32 ...items: ReadonlyArray<MaybeTsDsl<ts.Expression | ts.Statement>>
+6 -2
packages/openapi-ts/src/ts-dsl/mixins/doc.ts
··· 1 - import type { SyntaxNode } 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'; 5 5 import { DocTsDsl } from '../layout/doc'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export interface DocMethods extends SyntaxNode { 8 + export interface DocMethods extends Node { 9 9 doc(lines?: MaybeArray<string>, fn?: (d: DocTsDsl) => void): this; 10 10 } 11 11 ··· 14 14 ) { 15 15 abstract class Doc extends Base { 16 16 protected _doc?: DocTsDsl; 17 + 18 + override analyze(ctx: AnalysisContext): void { 19 + super.analyze(ctx); 20 + } 17 21 18 22 protected doc( 19 23 lines?: MaybeArray<string>,
+31 -42
packages/openapi-ts/src/ts-dsl/mixins/expr.ts
··· 1 - import type { SyntaxNode } 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 - import type { MaybeTsDsl } from '../base'; 5 - import type { AttrTsDsl } from '../expr/attr'; 6 - import type { AwaitTsDsl } from '../expr/await'; 7 - import type { CallTsDsl } from '../expr/call'; 8 - import type { ReturnTsDsl } from '../stmt/return'; 4 + import type { AttrCtor, AttrRight, AttrTsDsl } from '../expr/attr'; 5 + import type { AwaitCtor, AwaitTsDsl } from '../expr/await'; 6 + import type { CallArgs, CallCtor, CallTsDsl } from '../expr/call'; 7 + import type { ReturnCtor, ReturnTsDsl } from '../stmt/return'; 9 8 import type { BaseCtor, MixinCtor } from './types'; 10 9 11 - type AttrFactory = ( 12 - expr: MaybeTsDsl<ts.Expression>, 13 - name: string | ts.MemberName | number, 14 - ) => AttrTsDsl; 15 - let attrFactory: AttrFactory | undefined; 16 - /** Registers the Attr DSL factory after its module has finished evaluating. */ 17 - export function registerLazyAccessAttrFactory(factory: AttrFactory): void { 18 - attrFactory = factory; 10 + let attrFactory: AttrCtor | undefined; 11 + /** Lazy register the factory to avoid circular imports. */ 12 + export function setAttrFactory(fn: AttrCtor): void { 13 + attrFactory = fn; 19 14 } 20 15 21 - type AwaitFactory = (expr: MaybeTsDsl<ts.Expression>) => AwaitTsDsl; 22 - let awaitFactory: AwaitFactory | undefined; 23 - /** Registers the Await DSL factory after its module has finished evaluating. */ 24 - export function registerLazyAccessAwaitFactory(factory: AwaitFactory): void { 25 - awaitFactory = factory; 16 + let awaitFactory: AwaitCtor | undefined; 17 + /** Lazy register the factory to avoid circular imports. */ 18 + export function setAwaitFactory(fn: AwaitCtor): void { 19 + awaitFactory = fn; 26 20 } 27 21 28 - type CallFactory = ( 29 - expr: MaybeTsDsl<ts.Expression>, 30 - args: ReadonlyArray<string | MaybeTsDsl<ts.Expression> | undefined>, 31 - ) => CallTsDsl; 32 - let callFactory: CallFactory | undefined; 33 - /** Registers the Call DSL factory after its module has finished evaluating. */ 34 - export function registerLazyAccessCallFactory(factory: CallFactory): void { 35 - callFactory = factory; 22 + let callFactory: CallCtor | undefined; 23 + /** Lazy register the factory to avoid circular imports. */ 24 + export function setCallFactory(fn: CallCtor): void { 25 + callFactory = fn; 36 26 } 37 27 38 - type ReturnFactory = (expr: MaybeTsDsl<ts.Expression>) => ReturnTsDsl; 39 - let returnFactory: ReturnFactory | undefined; 40 - /** Registers the Return DSL factory after its module has finished evaluating. */ 41 - export function registerLazyAccessReturnFactory(factory: ReturnFactory): void { 42 - returnFactory = factory; 28 + let returnFactory: ReturnCtor | undefined; 29 + /** Lazy register the factory to avoid circular imports. */ 30 + export function setReturnFactory(fn: ReturnCtor): void { 31 + returnFactory = fn; 43 32 } 44 33 45 - export interface ExprMethods extends SyntaxNode { 34 + export interface ExprMethods extends Node { 46 35 /** Accesses a property on the current expression (e.g. `this.foo`). */ 47 - attr(name: string | ts.MemberName | number): AttrTsDsl; 36 + attr(name: AttrRight): AttrTsDsl; 48 37 /** Awaits the current expression (e.g. `await expr`). */ 49 38 await(): AwaitTsDsl; 50 39 /** Calls the current expression (e.g. `fn(arg1, arg2)`). */ 51 - call( 52 - ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression> | undefined> 53 - ): CallTsDsl; 40 + call(...args: CallArgs): CallTsDsl; 54 41 /** Produces a `return` statement returning the current expression. */ 55 42 return(): ReturnTsDsl; 56 43 } ··· 59 46 Base: TBase, 60 47 ) { 61 48 abstract class Expr extends Base { 62 - protected attr(name: string | ts.MemberName | number): AttrTsDsl { 49 + override analyze(ctx: AnalysisContext): void { 50 + super.analyze(ctx); 51 + } 52 + 53 + protected attr(name: AttrRight): AttrTsDsl { 63 54 return attrFactory!(this, name); 64 55 } 65 56 ··· 67 58 return awaitFactory!(this); 68 59 } 69 60 70 - protected call( 71 - ...args: ReadonlyArray<string | MaybeTsDsl<ts.Expression> | undefined> 72 - ): CallTsDsl { 73 - return callFactory!(this, args); 61 + protected call(...args: CallArgs): CallTsDsl { 62 + return callFactory!(this, ...args); 74 63 } 75 64 76 65 protected return(): ReturnTsDsl {
+6 -2
packages/openapi-ts/src/ts-dsl/mixins/hint.ts
··· 1 - import type { SyntaxNode } 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'; 5 5 import { HintTsDsl } from '../layout/hint'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export interface HintMethods extends SyntaxNode { 8 + export interface HintMethods extends Node { 9 9 hint(lines?: MaybeArray<string>, fn?: (h: HintTsDsl) => void): this; 10 10 } 11 11 ··· 14 14 ) { 15 15 abstract class Hint extends Base { 16 16 protected _hint?: HintTsDsl; 17 + 18 + override analyze(ctx: AnalysisContext): void { 19 + super.analyze(ctx); 20 + } 17 21 18 22 protected hint( 19 23 lines?: MaybeArray<string>,
+6 -2
packages/openapi-ts/src/ts-dsl/mixins/layout.ts
··· 1 - import type { SyntaxNode } 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 { BaseCtor, MixinCtor } from './types'; 5 5 6 - export interface LayoutMethods extends SyntaxNode { 6 + export interface LayoutMethods extends Node { 7 7 /** Computes whether output should be multiline based on layout setting and element count. */ 8 8 $multiline(count: number): boolean; 9 9 /** Sets automatic line output with optional threshold (default: 3). */ ··· 20 20 abstract class Layout extends Base { 21 21 protected static readonly DEFAULT_THRESHOLD = 3; 22 22 protected layout: boolean | number | undefined; 23 + 24 + override analyze(ctx: AnalysisContext): void { 25 + super.analyze(ctx); 26 + } 23 27 24 28 protected auto(threshold: number = Layout.DEFAULT_THRESHOLD): this { 25 29 this.layout = threshold;
+5
packages/openapi-ts/src/ts-dsl/mixins/modifiers.ts
··· 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 1 2 import ts from 'typescript'; 2 3 3 4 import type { BaseCtor, MixinCtor } from './types'; ··· 22 23 ) { 23 24 abstract class Modifiers extends Base { 24 25 protected modifiers: Array<ts.Modifier> = []; 26 + 27 + override analyze(ctx: AnalysisContext): void { 28 + super.analyze(ctx); 29 + } 25 30 26 31 protected _m(kind: ts.ModifierSyntaxKind, condition: boolean): this { 27 32 if (condition) this.modifiers.push(ts.factory.createModifier(kind));
+6 -2
packages/openapi-ts/src/ts-dsl/mixins/note.ts
··· 1 - import type { SyntaxNode } 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'; 5 5 import { NoteTsDsl } from '../layout/note'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export interface NoteMethods extends SyntaxNode { 8 + export interface NoteMethods extends Node { 9 9 note(lines?: MaybeArray<string>, fn?: (h: NoteTsDsl) => void): this; 10 10 } 11 11 ··· 14 14 ) { 15 15 abstract class Note extends Base { 16 16 protected _note?: NoteTsDsl; 17 + 18 + override analyze(ctx: AnalysisContext): void { 19 + super.analyze(ctx); 20 + } 17 21 18 22 protected note( 19 23 lines?: MaybeArray<string>,
+7 -3
packages/openapi-ts/src/ts-dsl/mixins/operator.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, Symbol } 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 = string | MaybeTsDsl<ts.Expression>; 8 + type Expr = Symbol | string | MaybeTsDsl<ts.Expression>; 9 9 10 - export interface OperatorMethods extends SyntaxNode { 10 + export interface OperatorMethods extends Node { 11 11 /** Logical AND — `this && expr` */ 12 12 and(expr: Expr): BinaryTsDsl; 13 13 /** Creates an assignment expression (e.g. `this = expr`). */ ··· 47 47 TBase extends BaseCtor<T>, 48 48 >(Base: TBase) { 49 49 abstract class Operator extends Base { 50 + override analyze(ctx: AnalysisContext): void { 51 + super.analyze(ctx); 52 + } 53 + 50 54 protected and(expr: Expr): BinaryTsDsl { 51 55 return new BinaryTsDsl(this).and(expr); 52 56 }
+6 -2
packages/openapi-ts/src/ts-dsl/mixins/optional.ts
··· 1 - import type { SyntaxNode } 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 { BaseCtor, MixinCtor } from './types'; 5 5 6 - export interface OptionalMethods extends SyntaxNode { 6 + export interface OptionalMethods extends Node { 7 7 _optional?: boolean; 8 8 /** Marks the node as optional when the condition is true. */ 9 9 optional(condition?: boolean): this; ··· 16 16 ) { 17 17 abstract class Optional extends Base { 18 18 protected _optional?: boolean; 19 + 20 + override analyze(ctx: AnalysisContext): void { 21 + super.analyze(ctx); 22 + } 19 23 20 24 protected optional(condition?: boolean): this { 21 25 this._optional = arguments.length === 0 ? true : Boolean(condition);
+10 -3
packages/openapi-ts/src/ts-dsl/mixins/param.ts
··· 1 - import type { SyntaxNode } 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 - import type { MaybeTsDsl } from '../base'; 4 + import { isTsDsl, type MaybeTsDsl } from '../base'; 5 5 import { ParamTsDsl } from '../decl/param'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export interface ParamMethods extends SyntaxNode { 8 + export interface ParamMethods extends Node { 9 9 /** Renders the parameters into an array of `ParameterDeclaration`s. */ 10 10 $params(): ReadonlyArray<ts.ParameterDeclaration>; 11 11 /** Adds a parameter. */ ··· 22 22 ) { 23 23 abstract class Param extends Base { 24 24 protected _params: Array<MaybeTsDsl<ts.ParameterDeclaration>> = []; 25 + 26 + override analyze(ctx: AnalysisContext): void { 27 + super.analyze(ctx); 28 + for (const p of this._params) { 29 + if (isTsDsl(p)) p.analyze(ctx); 30 + } 31 + } 25 32 26 33 protected param( 27 34 name: string | ((p: ParamTsDsl) => void),
+8 -3
packages/openapi-ts/src/ts-dsl/mixins/pattern.ts
··· 1 - import type { SyntaxNode } 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 - import type { MaybeArray } from '../base'; 4 + import { isTsDsl, type MaybeArray } from '../base'; 5 5 import { PatternTsDsl } from '../decl/pattern'; 6 6 import type { BaseCtor, MixinCtor } from './types'; 7 7 8 - export interface PatternMethods extends SyntaxNode { 8 + export interface PatternMethods extends Node { 9 9 /** Renders the pattern into a `BindingName`. */ 10 10 $pattern(): ts.BindingName | undefined; 11 11 /** Defines an array binding pattern. */ ··· 26 26 ) { 27 27 abstract class Pattern extends Base { 28 28 protected pattern?: PatternTsDsl; 29 + 30 + override analyze(ctx: AnalysisContext): void { 31 + super.analyze(ctx); 32 + if (isTsDsl(this.pattern)) this.pattern.analyze(ctx); 33 + } 29 34 30 35 protected array( 31 36 ...props: ReadonlyArray<string> | [ReadonlyArray<string>]
+23 -29
packages/openapi-ts/src/ts-dsl/mixins/type-args.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import type ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl, TypeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 + import { isTsDsl } from '../base'; 6 7 import type { BaseCtor, MixinCtor } from './types'; 7 8 8 - export interface TypeArgsMethods extends SyntaxNode { 9 + type Arg = Symbol | string | MaybeTsDsl<TypeTsDsl>; 10 + 11 + export interface TypeArgsMethods extends Node { 9 12 /** Returns the type arguments as an array of ts.TypeNode nodes. */ 10 13 $generics(): ReadonlyArray<ts.TypeNode> | undefined; 11 14 /** Adds a single type argument (e.g. `string` in `Foo<string>`). */ 12 - generic(arg: string | MaybeTsDsl<TypeTsDsl>): this; 15 + generic(arg: Arg): this; 13 16 /** Adds type arguments (e.g. `Map<string, number>`). */ 14 - generics(...args: ReadonlyArray<string | MaybeTsDsl<TypeTsDsl>>): this; 17 + generics(...args: ReadonlyArray<Arg>): this; 15 18 } 16 19 17 20 export function TypeArgsMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 18 21 Base: TBase, 19 22 ) { 20 23 abstract class TypeArgs extends Base { 21 - protected _generics: Array<string | MaybeTsDsl<TypeTsDsl>> = []; 24 + protected _generics: Array<Arg> = []; 22 25 23 - protected generic(arg: string | MaybeTsDsl<TypeTsDsl>): this { 24 - if (arg instanceof TsDsl) arg.setParent(this); 26 + override analyze(ctx: AnalysisContext): void { 27 + super.analyze(ctx); 28 + for (const g of this._generics) { 29 + if (isSymbol(g)) { 30 + ctx.addDependency(g); 31 + } else if (isTsDsl(g)) { 32 + g.analyze(ctx); 33 + } 34 + } 35 + } 36 + 37 + protected generic(arg: Arg): this { 25 38 this._generics.push(arg); 26 39 return this; 27 40 } 28 41 29 - protected generics( 30 - ...args: ReadonlyArray<string | MaybeTsDsl<TypeTsDsl>> 31 - ): this { 32 - for (const arg of args) { 33 - if (arg instanceof TsDsl) arg.setParent(this); 34 - this._generics.push(arg); 35 - } 42 + protected generics(...args: ReadonlyArray<Arg>): this { 43 + this._generics.push(...args); 36 44 return this; 37 45 } 38 46 39 47 protected $generics(): ReadonlyArray<ts.TypeNode> | undefined { 40 48 return this.$type(this._generics); 41 - } 42 - 43 - override collectSymbols(out: Set<Symbol>): void { 44 - super.collectSymbols(out); 45 - for (const g of this._generics) { 46 - if (g instanceof TsDsl) g.collectSymbols(out); 47 - } 48 - } 49 - 50 - override traverse(visitor: (node: SyntaxNode) => void): void { 51 - super.traverse(visitor); 52 - for (const g of this._generics) { 53 - if (g instanceof TsDsl) g.traverse(visitor); 54 - } 55 49 } 56 50 } 57 51
+12 -17
packages/openapi-ts/src/ts-dsl/mixins/type-expr.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 - import type { MaybeTsDsl, TypeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 4 + import type { MaybeTsDsl, TsDsl, TypeTsDsl } from '../base'; 5 + import { isTsDsl } from '../base'; 6 6 import type { TypeOfExprTsDsl } from '../expr/typeof'; 7 7 import type { TypeExprTsDsl } from '../type/expr'; 8 8 import type { TypeIdxTsDsl } from '../type/idx'; ··· 65 65 typeQueryFactory = factory; 66 66 } 67 67 68 - export interface TypeExprMethods extends SyntaxNode { 68 + export interface TypeExprMethods extends Node { 69 69 /** Creates an indexed-access type (e.g. `Foo<T>[K]`). */ 70 70 idx( 71 71 this: MaybeTsDsl<TypeTsDsl>, ··· 104 104 Base: TBase, 105 105 ) { 106 106 abstract class TypeExpr extends Base { 107 + override analyze(ctx: AnalysisContext): void { 108 + super.analyze(ctx); 109 + } 110 + 107 111 protected idx( 108 112 this: TypeTsDsl, 109 113 index: string | number | MaybeTsDsl<ts.TypeNode>, ··· 120 124 } 121 125 122 126 protected returnType(this: TsDsl<ts.Expression>): TypeExprTsDsl { 123 - const node = typeExprFactory!('ReturnType'); 124 - node.setParent(this); 125 - return node.generic(typeQueryFactory!(this)); 127 + return typeExprFactory!('ReturnType').generic(typeQueryFactory!(this)); 126 128 } 127 129 128 130 protected typeofExpr(this: TsDsl<ts.Expression>): TypeOfExprTsDsl { ··· 142 144 : T extends TypeTsDsl 143 145 ? TypeQueryTsDsl 144 146 : TypeQueryTsDsl | TypeOfExprTsDsl { 145 - if (this instanceof TsDsl) { 147 + if (isTsDsl(this)) { 146 148 // @ts-expect-error 149 + const node = this._render(); 147 150 return ( 148 - ts.isExpression(this._render()) 151 + ts.isExpression(node) 149 152 ? typeOfExprFactory!(this as any) 150 153 : typeQueryFactory!(this) 151 154 ) as any; ··· 160 163 161 164 protected unique(this: TypeTsDsl): TypeOperatorTsDsl { 162 165 return typeOperatorFactory!().unique(this); 163 - } 164 - 165 - override collectSymbols(out: Set<Symbol>): void { 166 - super.collectSymbols(out); 167 - } 168 - 169 - override traverse(visitor: (node: SyntaxNode) => void): void { 170 - super.traverse(visitor); 171 166 } 172 167 } 173 168
+13 -10
packages/openapi-ts/src/ts-dsl/mixins/type-params.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 2 - import { Symbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Node, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 3 import type ts from 'typescript'; 4 4 5 - import type { MaybeTsDsl } from '../base'; 6 - import { TsDsl } from '../base'; 5 + import { isTsDsl, type MaybeTsDsl } from '../base'; 7 6 import { TypeParamTsDsl } from '../type/param'; 8 7 import type { BaseCtor, MixinCtor } from './types'; 9 8 10 - export interface TypeParamsMethods extends SyntaxNode { 9 + export interface TypeParamsMethods extends Node { 11 10 /** Returns the type parameters as an array of ts.TypeParameterDeclaration nodes. */ 12 11 $generics(): ReadonlyArray<ts.TypeParameterDeclaration> | undefined; 13 12 /** Adds a single type parameter (e.g. `T` in `Array<T>`). */ ··· 24 23 abstract class TypeParams extends Base { 25 24 protected _generics: Array<MaybeTsDsl<TypeParamTsDsl>> = []; 26 25 26 + override analyze(ctx: AnalysisContext): void { 27 + super.analyze(ctx); 28 + for (const g of this._generics) { 29 + if (isTsDsl(g)) g.analyze(ctx); 30 + } 31 + } 32 + 27 33 protected generic( 28 34 ...args: ConstructorParameters<typeof TypeParamTsDsl> 29 35 ): this { 30 - const g = new TypeParamTsDsl(...args).setParent(this); 36 + const g = new TypeParamTsDsl(...args); 31 37 this._generics.push(g); 32 38 return this; 33 39 } ··· 36 42 ...args: ReadonlyArray<Symbol | string | MaybeTsDsl<TypeParamTsDsl>> 37 43 ): this { 38 44 for (let arg of args) { 39 - if (typeof arg === 'string' || arg instanceof Symbol) { 45 + if (typeof arg === 'string' || isSymbol(arg)) { 40 46 arg = new TypeParamTsDsl(arg); 41 - } 42 - if (arg instanceof TsDsl) { 43 - arg.setParent(this); 44 47 } 45 48 this._generics.push(arg); 46 49 }
+13 -6
packages/openapi-ts/src/ts-dsl/mixins/value.ts
··· 1 - import type { SyntaxNode } 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 - import type { MaybeTsDsl } from '../base'; 4 + import { isTsDsl, type MaybeTsDsl } from '../base'; 5 5 import type { BaseCtor, MixinCtor } from './types'; 6 6 7 - export interface ValueMethods extends SyntaxNode { 7 + export type ValueExpr = string | MaybeTsDsl<ts.Expression>; 8 + 9 + export interface ValueMethods extends Node { 8 10 $value(): ts.Expression | undefined; 9 11 /** Sets the initializer expression (e.g. `= expr`). */ 10 - assign(expr: string | MaybeTsDsl<ts.Expression>): this; 12 + assign(expr: ValueExpr): this; 11 13 } 12 14 13 15 export function ValueMixin<T extends ts.Node, TBase extends BaseCtor<T>>( 14 16 Base: TBase, 15 17 ) { 16 18 abstract class Value extends Base { 17 - protected value?: string | MaybeTsDsl<ts.Expression>; 19 + protected value?: ValueExpr; 20 + 21 + override analyze(ctx: AnalysisContext): void { 22 + super.analyze(ctx); 23 + if (isTsDsl(this.value)) this.value.analyze(ctx); 24 + } 18 25 19 - protected assign(expr: string | MaybeTsDsl<ts.Expression>): this { 26 + protected assign(expr: ValueExpr): this { 20 27 this.value = expr; 21 28 return this; 22 29 }
+12 -6
packages/openapi-ts/src/ts-dsl/stmt/if.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 import { DoMixin } from '../mixins/do'; 7 7 8 8 const Mixed = DoMixin(TsDsl<ts.IfStatement>); ··· 16 16 if (condition) this.condition(condition); 17 17 } 18 18 19 + override analyze(ctx: AnalysisContext): void { 20 + super.analyze(ctx); 21 + if (isTsDsl(this._condition)) this._condition.analyze(ctx); 22 + if (this._else) { 23 + for (const stmt of this._else) { 24 + if (isTsDsl(stmt)) stmt.analyze(ctx); 25 + } 26 + } 27 + } 28 + 19 29 condition(condition: string | MaybeTsDsl<ts.Expression>): this { 20 30 this._condition = condition; 21 31 return this; ··· 24 34 otherwise(...statements: ReadonlyArray<MaybeTsDsl<ts.Statement>>): this { 25 35 this._else = statements; 26 36 return this; 27 - } 28 - 29 - override traverse(visitor: (node: SyntaxNode) => void): void { 30 - super.traverse(visitor); 31 37 } 32 38 33 39 protected override _render() {
+17 -8
packages/openapi-ts/src/ts-dsl/stmt/return.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TsDsl } from '../base'; 6 - import { registerLazyAccessReturnFactory } from '../mixins/expr'; 6 + import { isTsDsl, TsDsl } from '../base'; 7 + import { setReturnFactory } from '../mixins/expr'; 8 + 9 + export type ReturnExpr = Symbol | string | MaybeTsDsl<ts.Expression>; 10 + export type ReturnCtor = (expr?: ReturnExpr) => ReturnTsDsl; 7 11 8 12 const Mixed = TsDsl<ts.ReturnStatement>; 9 13 10 14 export class ReturnTsDsl extends Mixed { 11 - protected _returnExpr?: string | MaybeTsDsl<ts.Expression>; 15 + protected _returnExpr?: ReturnExpr; 12 16 13 - constructor(expr?: string | MaybeTsDsl<ts.Expression>) { 17 + constructor(expr?: ReturnExpr) { 14 18 super(); 15 19 this._returnExpr = expr; 16 20 } 17 21 18 - override traverse(visitor: (node: SyntaxNode) => void): void { 19 - super.traverse(visitor); 22 + override analyze(ctx: AnalysisContext): void { 23 + super.analyze(ctx); 24 + if (isSymbol(this._returnExpr)) { 25 + ctx.addDependency(this._returnExpr); 26 + } else if (isTsDsl(this._returnExpr)) { 27 + this._returnExpr.analyze(ctx); 28 + } 20 29 } 21 30 22 31 protected override _render() { ··· 24 33 } 25 34 } 26 35 27 - registerLazyAccessReturnFactory((...args) => new ReturnTsDsl(...args)); 36 + setReturnFactory((...args) => new ReturnTsDsl(...args));
+5 -4
packages/openapi-ts/src/ts-dsl/stmt/stmt.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 - import { TsDsl } from '../base'; 4 + import { isTsDsl, TsDsl } from '../base'; 5 5 6 6 const Mixed = TsDsl<ts.Statement>; 7 7 ··· 13 13 this._inner = inner; 14 14 } 15 15 16 - override traverse(visitor: (node: SyntaxNode) => void): void { 17 - super.traverse(visitor); 16 + override analyze(ctx: AnalysisContext): void { 17 + super.analyze(ctx); 18 + if (isTsDsl(this._inner)) this._inner.analyze(ctx); 18 19 } 19 20 20 21 protected override _render() {
+8 -6
packages/openapi-ts/src/ts-dsl/stmt/throw.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl } from '../base'; 5 + import { isTsDsl, TsDsl } from '../base'; 6 6 import { LiteralTsDsl } from '../expr/literal'; 7 7 8 8 const Mixed = TsDsl<ts.ThrowStatement>; ··· 18 18 this.useNew = useNew; 19 19 } 20 20 21 + override analyze(ctx: AnalysisContext): void { 22 + super.analyze(ctx); 23 + if (isTsDsl(this.error)) this.error.analyze(ctx); 24 + if (isTsDsl(this.msg)) this.msg.analyze(ctx); 25 + } 26 + 21 27 message(value: string | MaybeTsDsl<ts.Expression>): this { 22 28 this.msg = value; 23 29 return this; 24 - } 25 - 26 - override traverse(visitor: (node: SyntaxNode) => void): void { 27 - super.traverse(visitor); 28 30 } 29 31 30 32 protected override _render() {
+18 -17
packages/openapi-ts/src/ts-dsl/stmt/var.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import { TsDsl, TypeTsDsl } from '../base'; ··· 8 9 import { PatternMixin } from '../mixins/pattern'; 9 10 import { ValueMixin } from '../mixins/value'; 10 11 import { TypeExprTsDsl } from '../type/expr'; 12 + 13 + export type VarName = Symbol | string; 11 14 12 15 const Mixed = DefaultMixin( 13 16 DocMixin( ··· 19 22 20 23 export class VarTsDsl extends Mixed { 21 24 protected kind: ts.NodeFlags = ts.NodeFlags.None; 22 - protected name?: string; 25 + protected name?: VarName; 23 26 protected _type?: TypeTsDsl; 24 27 25 - constructor(name?: Symbol | string) { 28 + constructor(name?: VarName) { 26 29 super(); 27 - if (name) { 28 - if (typeof name === 'string') { 29 - this.name = name; 30 - } else { 31 - this.name = name.finalName; 32 - this.symbol = name; 33 - this.symbol.setKind('var'); 34 - this.symbol.setRootNode(this); 35 - } 30 + this.name = name; 31 + if (isSymbol(name)) { 32 + name.setKind('var'); 33 + name.setNode(this); 36 34 } 37 35 } 38 36 37 + override analyze(ctx: AnalysisContext): void { 38 + super.analyze(ctx); 39 + if (isSymbol(this.name)) ctx.addDependency(this.name); 40 + this._type?.analyze(ctx); 41 + } 42 + 39 43 const(): this { 40 44 this.kind = ts.NodeFlags.Const; 41 45 return this; ··· 46 50 return this; 47 51 } 48 52 49 - override traverse(visitor: (node: SyntaxNode) => void): void { 50 - super.traverse(visitor); 51 - } 52 - 53 53 /** Sets the variable type. */ 54 54 type(type: string | TypeTsDsl): this { 55 55 this._type = type instanceof TypeTsDsl ? type : new TypeExprTsDsl(type); ··· 62 62 } 63 63 64 64 protected override _render() { 65 - const name = this.$pattern() ?? this.name; 65 + const name = this.$pattern() ?? this.$node(this.name); 66 66 if (!name) 67 67 throw new Error('Var must have either a name or a destructuring pattern'); 68 68 return ts.factory.createVariableStatement( ··· 70 70 ts.factory.createVariableDeclarationList( 71 71 [ 72 72 ts.factory.createVariableDeclaration( 73 + // @ts-expect-error need to improve types 73 74 name, 74 75 undefined, 75 76 this.$type(this._type),
+13 -28
packages/openapi-ts/src/ts-dsl/type/alias.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 2 - import { Symbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 3 import ts from 'typescript'; 4 4 5 5 import type { MaybeTsDsl } from '../base'; 6 - import { TsDsl } from '../base'; 6 + import { isTsDsl, TsDsl } from '../base'; 7 7 import { DocMixin } from '../mixins/doc'; 8 8 import { ExportMixin } from '../mixins/modifiers'; 9 9 import { TypeParamsMixin } from '../mixins/type-params'; ··· 21 21 22 22 constructor(name: Name, fn?: (t: TypeAliasTsDsl) => void) { 23 23 super(); 24 - if (typeof name === 'string') { 25 - this.name = name; 26 - } else { 27 - this.name = name; 28 - this.symbol = name; 29 - this.symbol.setKind('type'); 30 - this.symbol.setRootNode(this); 24 + this.name = name; 25 + if (isSymbol(name)) { 26 + name.setKind('type'); 27 + name.setNode(this); 31 28 } 32 29 fn?.(this); 33 30 } 34 31 35 - override collectSymbols(out: Set<Symbol>): void { 36 - super.collectSymbols(out); 37 - if (this.name instanceof Symbol) { 38 - out.add(this.name); 39 - } 40 - if (this.value instanceof TsDsl) { 41 - this.value.collectSymbols(out); 42 - } 43 - } 44 - 45 - override traverse(visitor: (node: SyntaxNode) => void): void { 46 - super.traverse(visitor); 47 - if (this.value instanceof TsDsl) { 48 - this.value.traverse(visitor); 49 - } 32 + override analyze(ctx: AnalysisContext): void { 33 + super.analyze(ctx); 34 + if (isSymbol(this.name)) ctx.addDependency(this.name); 35 + if (isTsDsl(this.value)) this.value.analyze(ctx); 50 36 } 51 37 52 38 /** Sets the type expression on the right-hand side of `= ...`. */ 53 39 type(node: Value): this { 54 40 this.value = node; 55 - if (this.value instanceof TsDsl) this.value.setParent(this); 56 41 return this; 57 42 } 58 43 59 44 protected override _render() { 60 45 if (!this.value) 61 46 throw new Error(`Type alias '${this.name}' is missing a type definition`); 62 - const name = this.name instanceof Symbol ? this.name.finalName : this.name; 63 47 return ts.factory.createTypeAliasDeclaration( 64 48 this.modifiers, 65 - name, 49 + // @ts-expect-error need to improve types 50 + this.$node(this.name), 66 51 this.$generics(), 67 52 this.$type(this.value), 68 53 );
+17 -7
packages/openapi-ts/src/ts-dsl/type/and.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 - import { TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 + 7 + type Type = Symbol | string | ts.TypeNode | TypeTsDsl; 5 8 6 9 const Mixed = TypeTsDsl<ts.IntersectionTypeNode>; 7 10 8 11 export class TypeAndTsDsl extends Mixed { 9 - protected _types: Array<string | ts.TypeNode | TypeTsDsl> = []; 12 + protected _types: Array<Type> = []; 10 13 11 - constructor(...nodes: Array<string | ts.TypeNode | TypeTsDsl>) { 14 + constructor(...nodes: Array<Type>) { 12 15 super(); 13 16 this.types(...nodes); 14 17 } 15 18 16 - override traverse(visitor: (node: SyntaxNode) => void): void { 17 - super.traverse(visitor); 19 + override analyze(ctx: AnalysisContext): void { 20 + super.analyze(ctx); 21 + for (const t of this._types) { 22 + if (isSymbol(t)) { 23 + ctx.addDependency(t); 24 + } else if (isTsDsl(t)) { 25 + t.analyze(ctx); 26 + } 27 + } 18 28 } 19 29 20 - types(...nodes: Array<string | ts.TypeNode | TypeTsDsl>): this { 30 + types(...nodes: Array<Type>): this { 21 31 this._types.push(...nodes); 22 32 return this; 23 33 }
+19 -34
packages/openapi-ts/src/ts-dsl/type/attr.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 2 - import { Symbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 3 import ts from 'typescript'; 4 4 5 5 import type { MaybeTsDsl } from '../base'; 6 - import { TsDsl, TypeTsDsl } from '../base'; 6 + import { isTsDsl, TypeTsDsl } from '../base'; 7 7 import { TypeExprMixin } from '../mixins/type-expr'; 8 8 9 9 type Base = Symbol | string | MaybeTsDsl<ts.EntityName>; ··· 28 28 } 29 29 } 30 30 31 + override analyze(ctx: AnalysisContext): void { 32 + super.analyze(ctx); 33 + if (isSymbol(this._base)) { 34 + ctx.addDependency(this._base); 35 + } else if (isTsDsl(this._base)) { 36 + this._base.analyze(ctx); 37 + } 38 + if (isSymbol(this._right)) ctx.addDependency(this._right); 39 + } 40 + 31 41 base(base?: Base): this { 32 42 this._base = base; 33 - if (this._base instanceof TsDsl) this._base.setParent(this); 34 43 return this; 35 44 } 36 45 ··· 39 48 return this; 40 49 } 41 50 42 - override collectSymbols(out: Set<Symbol>): void { 43 - super.collectSymbols(out); 44 - if (this._base) { 45 - if (this._base instanceof Symbol) { 46 - out.add(this._base); 47 - } else if (this._base instanceof TsDsl) { 48 - this._base.collectSymbols(out); 49 - } 50 - } 51 - if (this._right instanceof Symbol) { 52 - out.add(this._right); 53 - } 54 - } 55 - 56 - override traverse(visitor: (node: SyntaxNode) => void): void { 57 - super.traverse(visitor); 58 - if (this._base instanceof TsDsl) { 59 - this._base.traverse(visitor); 60 - } 61 - } 62 - 63 51 protected override _render() { 64 52 if (!this._base) { 65 53 throw new Error('TypeAttrTsDsl: missing base for qualified name'); 66 54 } 67 - const left = 68 - this._base instanceof Symbol 69 - ? this.$node(this._base.finalName) 70 - : this.$node(this._base); 55 + const left = this.$node(this._base); 71 56 if (!ts.isEntityName(left)) { 72 57 throw new Error('TypeAttrTsDsl: base must be an EntityName'); 73 58 } 74 - const right = 75 - this._right instanceof Symbol 76 - ? this.$maybeId(this._right.finalName) 77 - : this.$maybeId(this._right); 78 - return ts.factory.createQualifiedName(left, right); 59 + return ts.factory.createQualifiedName( 60 + left, 61 + // @ts-expect-error need to improve types 62 + this.$node(this._right), 63 + ); 79 64 } 80 65 }
+23 -36
packages/openapi-ts/src/ts-dsl/type/expr.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 2 - import { Symbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 3 import ts from 'typescript'; 4 4 5 - import { TsDsl, TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 import { TypeArgsMixin } from '../mixins/type-args'; 7 7 import { 8 8 registerLazyAccessTypeExprFactory, ··· 10 10 } from '../mixins/type-expr'; 11 11 import { TypeAttrTsDsl } from './attr'; 12 12 13 + export type TypeExprName = Symbol | string; 14 + export type TypeExprExpr = TypeExprName | TypeAttrTsDsl; 15 + 13 16 const Mixed = TypeArgsMixin(TypeExprMixin(TypeTsDsl<ts.TypeReferenceNode>)); 14 17 15 18 export class TypeExprTsDsl extends Mixed { 16 - protected _exprInput?: Symbol | string | TypeAttrTsDsl; 19 + protected _exprInput?: TypeExprExpr; 17 20 18 21 constructor(); 19 22 constructor(fn: (t: TypeExprTsDsl) => void); 20 - constructor(name: Symbol | string); 21 - constructor(name: Symbol | string, fn?: (t: TypeExprTsDsl) => void); 23 + constructor(name: TypeExprName); 24 + constructor(name: TypeExprName, fn?: (t: TypeExprTsDsl) => void); 22 25 constructor( 23 - name?: Symbol | string | ((t: TypeExprTsDsl) => void), 26 + name?: TypeExprName | ((t: TypeExprTsDsl) => void), 24 27 fn?: (t: TypeExprTsDsl) => void, 25 28 ) { 26 29 super(); ··· 32 35 } 33 36 } 34 37 38 + override analyze(ctx: AnalysisContext): void { 39 + super.analyze(ctx); 40 + if (isSymbol(this._exprInput)) { 41 + ctx.addDependency(this._exprInput); 42 + } else if (isTsDsl(this._exprInput)) { 43 + this._exprInput.analyze(ctx); 44 + } 45 + } 46 + 35 47 /** Accesses a nested type (e.g. `Foo.Bar`). */ 36 48 attr(right: string | ts.Identifier | TypeAttrTsDsl): this { 37 - this._exprInput = 38 - right instanceof TypeAttrTsDsl 39 - ? right.base(this._exprInput) 40 - : new TypeAttrTsDsl(this._exprInput!, right); 41 - this._exprInput.setParent(this); 49 + this._exprInput = isTsDsl(right) 50 + ? right.base(this._exprInput) 51 + : new TypeAttrTsDsl(this._exprInput!, right); 42 52 return this; 43 53 } 44 54 45 - override collectSymbols(out: Set<Symbol>): void { 46 - super.collectSymbols(out); 47 - if (this._exprInput && typeof this._exprInput !== 'string') { 48 - if (this._exprInput instanceof Symbol) { 49 - out.add(this._exprInput); 50 - } else { 51 - this._exprInput.collectSymbols(out); 52 - } 53 - } 54 - } 55 - 56 - override traverse(visitor: (node: SyntaxNode) => void): void { 57 - super.traverse(visitor); 58 - if (this._exprInput instanceof TsDsl) { 59 - this._exprInput.traverse(visitor); 60 - } 61 - } 62 - 63 55 protected override _render() { 64 56 if (!this._exprInput) throw new Error('TypeExpr must have an expression'); 65 - const typeName = 66 - typeof this._exprInput === 'string' || 67 - this._exprInput instanceof TypeAttrTsDsl 68 - ? this.$type(this._exprInput) 69 - : this._exprInput.finalName; 70 57 return ts.factory.createTypeReferenceNode( 71 58 // @ts-expect-error --- need to fix types 72 - typeName, 59 + this.$type(this._exprInput), 73 60 this.$generics(), 74 61 ); 75 62 }
+4 -3
packages/openapi-ts/src/ts-dsl/type/fromValue.ts
··· 1 1 import type ts from 'typescript'; 2 2 3 - import { TsDsl } from '../base'; 3 + import type { TsDsl } from '../base'; 4 + import { isTsDsl } from '../base'; 4 5 import { TypeLiteralTsDsl } from './literal'; 5 6 import { TypeObjectTsDsl } from './object'; 6 7 import { TypeTupleTsDsl } from './tuple'; 7 8 8 9 export const fromValue = (input: unknown): TsDsl<ts.TypeNode> => { 9 - if (input instanceof TsDsl) { 10 - return input; 10 + if (isTsDsl(input)) { 11 + return input as TsDsl<ts.TypeNode>; 11 12 } 12 13 13 14 if (input === null) {
+6 -5
packages/openapi-ts/src/ts-dsl/type/func.ts
··· 1 - import type { SyntaxNode } 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 { TypeTsDsl } from '../base'; ··· 14 14 export class TypeFuncTsDsl extends Mixed { 15 15 protected _returns?: TypeTsDsl; 16 16 17 + override analyze(ctx: AnalysisContext): void { 18 + super.analyze(ctx); 19 + this._returns?.analyze(ctx); 20 + } 21 + 17 22 /** Sets the return type. */ 18 23 returns(type: string | TypeTsDsl): this { 19 24 this._returns = type instanceof TypeTsDsl ? type : new TypeExprTsDsl(type); 20 25 return this; 21 - } 22 - 23 - override traverse(visitor: (node: SyntaxNode) => void): void { 24 - super.traverse(visitor); 25 26 } 26 27 27 28 protected override _render() {
+19 -16
packages/openapi-ts/src/ts-dsl/type/idx-sig.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 import { DocMixin } from '../mixins/doc'; 7 7 import { ReadonlyMixin } from '../mixins/modifiers'; 8 8 9 - type Type = string | MaybeTsDsl<ts.TypeNode>; 9 + export type TypeIdxSigName = string; 10 + export type TypeIdxSigType = string | MaybeTsDsl<ts.TypeNode>; 10 11 11 12 const Mixed = DocMixin(ReadonlyMixin(TypeTsDsl<ts.IndexSignatureDeclaration>)); 12 13 13 14 export class TypeIdxSigTsDsl extends Mixed { 14 - protected _key?: Type; 15 - protected _name: string; 16 - protected _type?: Type; 15 + protected _key?: TypeIdxSigType; 16 + protected _name: TypeIdxSigName; 17 + protected _type?: TypeIdxSigType; 17 18 18 - constructor(name: string, fn?: (i: TypeIdxSigTsDsl) => void) { 19 + constructor(name: TypeIdxSigName, fn?: (i: TypeIdxSigTsDsl) => void) { 19 20 super(); 20 21 this._name = name; 21 22 fn?.(this); 22 23 } 23 24 25 + override analyze(ctx: AnalysisContext): void { 26 + super.analyze(ctx); 27 + if (isTsDsl(this._key)) this._key.analyze(ctx); 28 + if (isTsDsl(this._type)) this._type.analyze(ctx); 29 + } 30 + 24 31 /** Returns true when all required builder calls are present. */ 25 32 get isValid(): boolean { 26 33 return this.missingRequiredCalls().length === 0; 27 34 } 28 35 29 36 /** Sets the key type: `[name: T]` */ 30 - key(type: Type): this { 37 + key(type: TypeIdxSigType): this { 31 38 this._key = type; 32 39 return this; 33 40 } 34 41 35 - override traverse(visitor: (node: SyntaxNode) => void): void { 36 - super.traverse(visitor); 37 - } 38 - 39 42 /** Sets the property type. */ 40 - type(type: Type): this { 43 + type(type: TypeIdxSigType): this { 41 44 this._type = type; 42 45 return this; 43 46 } ··· 60 63 } 61 64 62 65 $validate(): asserts this is this & { 63 - _key: Type; 64 - _name: string; 65 - _type: Type; 66 + _key: TypeIdxSigType; 67 + _name: TypeIdxSigName; 68 + _type: TypeIdxSigType; 66 69 } { 67 70 const missing = this.missingRequiredCalls(); 68 71 if (missing.length === 0) return;
+8 -14
packages/openapi-ts/src/ts-dsl/type/idx.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TsDsl, TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 import { 7 7 registerLazyAccessTypeIdxFactory, 8 8 TypeExprMixin, ··· 23 23 this.index(index); 24 24 } 25 25 26 + override analyze(ctx: AnalysisContext): void { 27 + super.analyze(ctx); 28 + if (isTsDsl(this._base)) this._base.analyze(ctx); 29 + if (isTsDsl(this._index)) this._index.analyze(ctx); 30 + } 31 + 26 32 base(base: Base): this { 27 33 this._base = base; 28 - if (this._base instanceof TsDsl) this._base.setParent(this); 29 34 return this; 30 35 } 31 36 32 37 index(index: Index): this { 33 38 this._index = index; 34 - if (this._index instanceof TsDsl) this._index.setParent(this); 35 39 return this; 36 - } 37 - 38 - override traverse(visitor: (node: SyntaxNode) => void): void { 39 - super.traverse(visitor); 40 - if (this._base instanceof TsDsl) { 41 - this._base.traverse(visitor); 42 - } 43 - if (this._index instanceof TsDsl) { 44 - this._index.traverse(visitor); 45 - } 46 40 } 47 41 48 42 protected override _render() {
+3 -3
packages/openapi-ts/src/ts-dsl/type/literal.ts
··· 1 - import type { SyntaxNode } 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 { TypeTsDsl } from '../base'; ··· 14 14 this.value = value; 15 15 } 16 16 17 - override traverse(visitor: (node: SyntaxNode) => void): void { 18 - super.traverse(visitor); 17 + override analyze(ctx: AnalysisContext): void { 18 + super.analyze(ctx); 19 19 } 20 20 21 21 protected override _render() {
+10 -6
packages/openapi-ts/src/ts-dsl/type/mapped.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 import { TokenTsDsl } from '../token'; 7 7 8 8 const Mixed = TypeTsDsl<ts.MappedTypeNode>; ··· 25 25 constructor(name?: string) { 26 26 super(); 27 27 this.name(name); 28 + } 29 + 30 + override analyze(ctx: AnalysisContext): void { 31 + super.analyze(ctx); 32 + this.questionToken?.analyze(ctx); 33 + this.readonlyToken?.analyze(ctx); 34 + if (isTsDsl(this._key)) this._key.analyze(ctx); 35 + if (isTsDsl(this._type)) this._type.analyze(ctx); 28 36 } 29 37 30 38 /** Returns true when all required builder calls are present. */ ··· 66 74 required(): this { 67 75 this.questionToken = new TokenTsDsl().minus(); 68 76 return this; 69 - } 70 - 71 - override traverse(visitor: (node: SyntaxNode) => void): void { 72 - super.traverse(visitor); 73 77 } 74 78 75 79 /** Sets the mapped value type: `[K in X]: ValueType` */
+8 -5
packages/openapi-ts/src/ts-dsl/type/object.ts
··· 1 - import type { SyntaxNode } 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 { TypeTsDsl } from '../base'; ··· 9 9 10 10 export class TypeObjectTsDsl extends Mixed { 11 11 protected props: Array<TypePropTsDsl | TypeIdxSigTsDsl> = []; 12 + 13 + override analyze(ctx: AnalysisContext): void { 14 + super.analyze(ctx); 15 + for (const prop of this.props) { 16 + prop.analyze(ctx); 17 + } 18 + } 12 19 13 20 /** Returns true if object has at least one property or spread. */ 14 21 hasProps(): boolean { ··· 32 39 const prop = new TypePropTsDsl(name, fn); 33 40 this.props.push(prop); 34 41 return this; 35 - } 36 - 37 - override traverse(visitor: (node: SyntaxNode) => void): void { 38 - super.traverse(visitor); 39 42 } 40 43 41 44 protected override _render() {
+5 -12
packages/openapi-ts/src/ts-dsl/type/operator.ts
··· 1 - import type { Symbol, SyntaxNode } 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'; 5 - import { TsDsl, TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 import { registerLazyAccessTypeOperatorFactory } from '../mixins/type-expr'; 7 7 8 8 type Op = ··· 29 29 protected _op?: Op; 30 30 protected _type?: Type; 31 31 32 - override collectSymbols(out: Set<Symbol>): void { 33 - super.collectSymbols(out); 32 + override analyze(ctx: AnalysisContext): void { 33 + super.analyze(ctx); 34 + if (isTsDsl(this._type)) this._type.analyze(ctx); 34 35 } 35 36 36 37 /** Shorthand: builds `keyof T`. */ ··· 53 54 return this; 54 55 } 55 56 56 - override traverse(visitor: (node: SyntaxNode) => void): void { 57 - super.traverse(visitor); 58 - if (this._type instanceof TsDsl) { 59 - this._type.traverse(visitor); 60 - } 61 - } 62 - 63 57 /** Sets the target type of the operator. */ 64 58 type(type: Type): this { 65 59 this._type = type; 66 - if (this._type instanceof TsDsl) this._type.setParent(this); 67 60 return this; 68 61 } 69 62
+12 -14
packages/openapi-ts/src/ts-dsl/type/or.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 2 - import { Symbol } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 3 3 import ts from 'typescript'; 4 4 5 - import { TsDsl, TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 7 7 type Type = Symbol | string | ts.TypeNode | TypeTsDsl; 8 8 ··· 16 16 this.types(...nodes); 17 17 } 18 18 19 - override traverse(visitor: (node: SyntaxNode) => void): void { 20 - super.traverse(visitor); 21 - for (const node of this._types) { 22 - if (node instanceof TsDsl) { 23 - node.traverse(visitor); 19 + override analyze(ctx: AnalysisContext): void { 20 + super.analyze(ctx); 21 + for (const t of this._types) { 22 + if (isSymbol(t)) { 23 + ctx.addDependency(t); 24 + } else if (isTsDsl(t)) { 25 + t.analyze(ctx); 24 26 } 25 27 } 26 28 } 27 29 28 30 types(...nodes: Array<Type>): this { 29 - for (const node of nodes) { 30 - if (node instanceof TsDsl) node.setParent(this); 31 - this._types.push(node); 32 - } 31 + this._types.push(...nodes); 33 32 return this; 34 33 } 35 34 ··· 37 36 const flat: Array<ts.TypeNode> = []; 38 37 39 38 for (const node of this._types) { 40 - const value = node instanceof Symbol ? node.finalName : node; 41 - const type = this.$type(value); 39 + const type = this.$type(node); 42 40 if (ts.isUnionTypeNode(type)) { 43 41 flat.push(...type.types); 44 42 } else {
+27 -20
packages/openapi-ts/src/ts-dsl/type/param.ts
··· 1 - import type { Symbol, SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TypeTsDsl } from '../base'; 6 + import { isTsDsl, TypeTsDsl } from '../base'; 7 + 8 + export type TypeParamName = Symbol | string; 9 + export type TypeParamExpr = Symbol | string | boolean | MaybeTsDsl<TypeTsDsl>; 6 10 7 11 const Mixed = TypeTsDsl<ts.TypeParameterDeclaration>; 8 12 9 13 export class TypeParamTsDsl extends Mixed { 10 - protected name?: Symbol | string; 11 - protected constraint?: string | MaybeTsDsl<TypeTsDsl> | boolean; 12 - protected defaultValue?: string | MaybeTsDsl<TypeTsDsl> | boolean; 14 + protected name?: TypeParamName; 15 + protected constraint?: TypeParamExpr; 16 + protected defaultValue?: TypeParamExpr; 13 17 14 - constructor(name?: Symbol | string, fn?: (name: TypeParamTsDsl) => void) { 18 + constructor(name?: TypeParamName, fn?: (name: TypeParamTsDsl) => void) { 15 19 super(); 16 20 this.name = name; 17 - if (name && typeof name !== 'string') { 18 - this.getRootSymbol().addDependency(name); 19 - } 20 21 fn?.(this); 21 22 } 22 23 23 - override collectSymbols(out: Set<Symbol>): void { 24 - console.log(out); 24 + override analyze(ctx: AnalysisContext): void { 25 + super.analyze(ctx); 26 + if (isSymbol(this.name)) ctx.addDependency(this.name); 27 + if (isSymbol(this.constraint)) { 28 + ctx.addDependency(this.constraint); 29 + } else if (isTsDsl(this.constraint)) { 30 + this.constraint.analyze(ctx); 31 + } 32 + if (isSymbol(this.defaultValue)) { 33 + ctx.addDependency(this.defaultValue); 34 + } else if (isTsDsl(this.defaultValue)) { 35 + this.defaultValue.analyze(ctx); 36 + } 25 37 } 26 38 27 - default(value: string | MaybeTsDsl<TypeTsDsl> | boolean): this { 39 + default(value: TypeParamExpr): this { 28 40 this.defaultValue = value; 29 41 return this; 30 42 } 31 43 32 - extends(constraint: string | MaybeTsDsl<TypeTsDsl> | boolean): this { 44 + extends(constraint: TypeParamExpr): this { 33 45 this.constraint = constraint; 34 46 return this; 35 - } 36 - 37 - override traverse(visitor: (node: SyntaxNode) => void): void { 38 - super.traverse(visitor); 39 47 } 40 48 41 49 protected override _render() { 42 50 if (!this.name) throw new Error('Missing type name'); 43 - const name = 44 - typeof this.name === 'string' ? this.name : this.name.finalName; 45 51 return ts.factory.createTypeParameterDeclaration( 46 52 undefined, 47 - this.$maybeId(name), 53 + // @ts-expect-error need to improve types 54 + this.$node(this.name), 48 55 this.$type(this.constraint), 49 56 this.$type(this.defaultValue), 50 57 );
+17 -8
packages/openapi-ts/src/ts-dsl/type/prop.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext, Symbol } from '@hey-api/codegen-core'; 2 + import { isSymbol } from '@hey-api/codegen-core'; 2 3 import ts from 'typescript'; 3 4 4 5 import type { MaybeTsDsl } from '../base'; 5 - import { TypeTsDsl } from '../base'; 6 + import { isTsDsl, TypeTsDsl } from '../base'; 6 7 import { DocMixin } from '../mixins/doc'; 7 8 import { ReadonlyMixin } from '../mixins/modifiers'; 8 9 import { OptionalMixin } from '../mixins/optional'; 9 10 import { TokenTsDsl } from '../token'; 10 11 import { safePropName } from '../utils/prop'; 11 12 13 + export type TypePropName = string; 14 + export type TypePropType = Symbol | string | MaybeTsDsl<ts.TypeNode>; 15 + 12 16 const Mixed = DocMixin(OptionalMixin(ReadonlyMixin(TypeTsDsl<ts.TypeElement>))); 13 17 14 18 export class TypePropTsDsl extends Mixed { 15 - protected name: string; 16 - protected _type?: string | MaybeTsDsl<ts.TypeNode>; 19 + protected name: TypePropName; 20 + protected _type?: TypePropType; 17 21 18 - constructor(name: string, fn: (p: TypePropTsDsl) => void) { 22 + constructor(name: TypePropName, fn: (p: TypePropTsDsl) => void) { 19 23 super(); 20 24 this.name = name; 21 25 fn(this); 22 26 } 23 27 24 - override traverse(visitor: (node: SyntaxNode) => void): void { 25 - super.traverse(visitor); 28 + override analyze(ctx: AnalysisContext): void { 29 + super.analyze(ctx); 30 + if (isSymbol(this._type)) { 31 + ctx.addDependency(this._type); 32 + } else if (isTsDsl(this._type)) { 33 + this._type.analyze(ctx); 34 + } 26 35 } 27 36 28 37 /** Sets the property type. */ 29 - type(type: string | MaybeTsDsl<ts.TypeNode>): this { 38 + type(type: TypePropType): this { 30 39 this._type = type; 31 40 return this; 32 41 }
+5 -4
packages/openapi-ts/src/ts-dsl/type/query.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 import { 7 7 registerLazyAccessTypeQueryFactory, 8 8 TypeExprMixin, ··· 18 18 this._expr = expr; 19 19 } 20 20 21 - override traverse(visitor: (node: SyntaxNode) => void): void { 22 - super.traverse(visitor); 21 + override analyze(ctx: AnalysisContext): void { 22 + super.analyze(ctx); 23 + if (isTsDsl(this._expr)) this._expr.analyze(ctx); 23 24 } 24 25 25 26 protected override _render() {
+9 -6
packages/openapi-ts/src/ts-dsl/type/template.ts
··· 1 - import type { SyntaxNode } 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'; 5 - import { TypeTsDsl } from '../base'; 5 + import { isTsDsl, TypeTsDsl } from '../base'; 6 6 7 7 const Mixed = TypeTsDsl<ts.TemplateLiteralTypeNode>; 8 8 ··· 14 14 if (value !== undefined) this.add(value); 15 15 } 16 16 17 + override analyze(ctx: AnalysisContext): void { 18 + super.analyze(ctx); 19 + for (const part of this.parts) { 20 + if (isTsDsl(part)) part.analyze(ctx); 21 + } 22 + } 23 + 17 24 /** Adds a raw string segment or embedded type expression. */ 18 25 add(part: string | MaybeTsDsl<ts.TypeNode>): this { 19 26 this.parts.push(part); 20 27 return this; 21 - } 22 - 23 - override traverse(visitor: (node: SyntaxNode) => void): void { 24 - super.traverse(visitor); 25 28 } 26 29 27 30 protected override _render() {
+9 -6
packages/openapi-ts/src/ts-dsl/type/tuple.ts
··· 1 - import type { SyntaxNode } from '@hey-api/codegen-core'; 1 + import type { AnalysisContext } from '@hey-api/codegen-core'; 2 2 import ts from 'typescript'; 3 3 4 - import { TypeTsDsl } from '../base'; 4 + import { isTsDsl, TypeTsDsl } from '../base'; 5 5 6 6 const Mixed = TypeTsDsl<ts.TupleTypeNode>; 7 7 ··· 13 13 this.elements(...nodes); 14 14 } 15 15 16 + override analyze(ctx: AnalysisContext): void { 17 + super.analyze(ctx); 18 + for (const t of this._elements) { 19 + if (isTsDsl(t)) t.analyze(ctx); 20 + } 21 + } 22 + 16 23 elements(...types: Array<string | ts.TypeNode | TypeTsDsl>): this { 17 24 this._elements.push(...types); 18 25 return this; 19 - } 20 - 21 - override traverse(visitor: (node: SyntaxNode) => void): void { 22 - super.traverse(visitor); 23 26 } 24 27 25 28 protected override _render() {
+264 -264
specs/3.1.x/openai.yaml
··· 4113 4113 } 4114 4114 description: > 4115 4115 **Starting a new project?** We recommend trying 4116 - [Responses](https://platform.openai.com/docs/api-reference/responses) 4116 + [Responses](https://platform.openai.com/docs/api-reference/responses) 4117 4117 4118 4118 to take advantage of the latest OpenAI platform features. Compare 4119 4119 ··· 4136 4136 4137 4137 response, particularly for newer reasoning models. Parameters that are only 4138 4138 4139 - supported for reasoning models are noted below. For the current state of 4139 + supported for reasoning models are noted below. For the current state of 4140 4140 4141 - unsupported parameters in reasoning models, 4141 + unsupported parameters in reasoning models, 4142 4142 4143 4143 [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). 4144 4144 /chat/completions/{completion_id}: ··· 14566 14566 ], 14567 14567 } 14568 14568 description: | 14569 - Deactivate certificates at the project level. You can atomically and 14569 + Deactivate certificates at the project level. You can atomically and 14570 14570 idempotently deactivate up to 10 certificates at a time. 14571 14571 /organization/projects/{project_id}/rate_limits: 14572 14572 get: ··· 16592 16592 "speed": 1.1, 16593 16593 "tracing": "auto", 16594 16594 "client_secret": { 16595 - "value": "ek_abc123", 16595 + "value": "ek_abc123", 16596 16596 "expires_at": 1234567890 16597 16597 } 16598 16598 } ··· 16739 16739 } 16740 16740 description: | 16741 16741 Create an ephemeral API token for use in client-side applications with the 16742 - Realtime API specifically for realtime transcriptions. 16742 + Realtime API specifically for realtime transcriptions. 16743 16743 Can be configured with the same session parameters as the `transcription_session.update` client event. 16744 16744 16745 16745 It responds with a session object, plus a `client_secret` key which contains ··· 18885 18885 puts(response) 18886 18886 description: | 18887 18887 Cancels a model response with the given ID. Only responses created with 18888 - the `background` parameter set to `true` can be cancelled. 18888 + the `background` parameter set to `true` can be cancelled. 18889 18889 [Learn more](https://platform.openai.com/docs/guides/background). 18890 18890 /responses/{response_id}/input_items: 18891 18891 get: ··· 23170 23170 File object. 23171 23171 23172 23172 23173 - For certain `purpose` values, the correct `mime_type` must be specified. 23173 + For certain `purpose` values, the correct `mime_type` must be specified. 23174 23174 23175 - Please refer to documentation for the 23175 + Please refer to documentation for the 23176 23176 23177 23177 [supported MIME types for your use 23178 23178 case](https://platform.openai.com/docs/assistants/tools/file-search#supported-files). ··· 23433 23433 23434 23434 puts(upload) 23435 23435 description: > 23436 - Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). 23436 + Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). 23437 23437 23438 23438 23439 23439 Within the returned Upload object, there is a nested ··· 23575 23575 description: > 23576 23576 Adds a [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an 23577 23577 [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. A Part represents a 23578 - chunk of bytes from the file you are trying to upload. 23578 + chunk of bytes from the file you are trying to upload. 23579 23579 23580 23580 23581 23581 Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB. ··· 25849 25849 responses: 25850 25850 '200': 25851 25851 description: | 25852 - Return a 200 status code to acknowledge receipt of the event. Non-200 25852 + Return a 200 status code to acknowledge receipt of the event. Non-200 25853 25853 status codes will be retried. 25854 25854 batch_completed: 25855 25855 post: ··· 25862 25862 responses: 25863 25863 '200': 25864 25864 description: | 25865 - Return a 200 status code to acknowledge receipt of the event. Non-200 25865 + Return a 200 status code to acknowledge receipt of the event. Non-200 25866 25866 status codes will be retried. 25867 25867 batch_expired: 25868 25868 post: ··· 25875 25875 responses: 25876 25876 '200': 25877 25877 description: | 25878 - Return a 200 status code to acknowledge receipt of the event. Non-200 25878 + Return a 200 status code to acknowledge receipt of the event. Non-200 25879 25879 status codes will be retried. 25880 25880 batch_failed: 25881 25881 post: ··· 25888 25888 responses: 25889 25889 '200': 25890 25890 description: | 25891 - Return a 200 status code to acknowledge receipt of the event. Non-200 25891 + Return a 200 status code to acknowledge receipt of the event. Non-200 25892 25892 status codes will be retried. 25893 25893 eval_run_canceled: 25894 25894 post: ··· 25901 25901 responses: 25902 25902 '200': 25903 25903 description: | 25904 - Return a 200 status code to acknowledge receipt of the event. Non-200 25904 + Return a 200 status code to acknowledge receipt of the event. Non-200 25905 25905 status codes will be retried. 25906 25906 eval_run_failed: 25907 25907 post: ··· 25914 25914 responses: 25915 25915 '200': 25916 25916 description: | 25917 - Return a 200 status code to acknowledge receipt of the event. Non-200 25917 + Return a 200 status code to acknowledge receipt of the event. Non-200 25918 25918 status codes will be retried. 25919 25919 eval_run_succeeded: 25920 25920 post: ··· 25927 25927 responses: 25928 25928 '200': 25929 25929 description: | 25930 - Return a 200 status code to acknowledge receipt of the event. Non-200 25930 + Return a 200 status code to acknowledge receipt of the event. Non-200 25931 25931 status codes will be retried. 25932 25932 fine_tuning_job_cancelled: 25933 25933 post: ··· 25940 25940 responses: 25941 25941 '200': 25942 25942 description: | 25943 - Return a 200 status code to acknowledge receipt of the event. Non-200 25943 + Return a 200 status code to acknowledge receipt of the event. Non-200 25944 25944 status codes will be retried. 25945 25945 fine_tuning_job_failed: 25946 25946 post: ··· 25953 25953 responses: 25954 25954 '200': 25955 25955 description: | 25956 - Return a 200 status code to acknowledge receipt of the event. Non-200 25956 + Return a 200 status code to acknowledge receipt of the event. Non-200 25957 25957 status codes will be retried. 25958 25958 fine_tuning_job_succeeded: 25959 25959 post: ··· 25966 25966 responses: 25967 25967 '200': 25968 25968 description: | 25969 - Return a 200 status code to acknowledge receipt of the event. Non-200 25969 + Return a 200 status code to acknowledge receipt of the event. Non-200 25970 25970 status codes will be retried. 25971 25971 response_cancelled: 25972 25972 post: ··· 25979 25979 responses: 25980 25980 '200': 25981 25981 description: | 25982 - Return a 200 status code to acknowledge receipt of the event. Non-200 25982 + Return a 200 status code to acknowledge receipt of the event. Non-200 25983 25983 status codes will be retried. 25984 25984 response_completed: 25985 25985 post: ··· 25992 25992 responses: 25993 25993 '200': 25994 25994 description: | 25995 - Return a 200 status code to acknowledge receipt of the event. Non-200 25995 + Return a 200 status code to acknowledge receipt of the event. Non-200 25996 25996 status codes will be retried. 25997 25997 response_failed: 25998 25998 post: ··· 26005 26005 responses: 26006 26006 '200': 26007 26007 description: | 26008 - Return a 200 status code to acknowledge receipt of the event. Non-200 26008 + Return a 200 status code to acknowledge receipt of the event. Non-200 26009 26009 status codes will be retried. 26010 26010 response_incomplete: 26011 26011 post: ··· 26018 26018 responses: 26019 26019 '200': 26020 26020 description: | 26021 - Return a 200 status code to acknowledge receipt of the event. Non-200 26021 + Return a 200 status code to acknowledge receipt of the event. Non-200 26022 26022 status codes will be retried. 26023 26023 components: 26024 26024 schemas: ··· 27592 27592 nullable: true 27593 27593 description: > 27594 27594 If a content parts array was provided, this is an array of `text` and `image_url` 27595 - parts. 27595 + parts. 27596 27596 27597 27597 Otherwise, null. 27598 27598 items: ··· 27822 27822 type: object 27823 27823 nullable: true 27824 27824 description: | 27825 - Data about a previous audio response from the model. 27825 + Data about a previous audio response from the model. 27826 27826 [Learn more](https://platform.openai.com/docs/guides/audio). 27827 27827 required: 27828 27828 - id ··· 27986 27986 filename: 27987 27987 type: string 27988 27988 description: | 27989 - The name of the file, used when passing the file to the model as a 27989 + The name of the file, used when passing the file to the model as a 27990 27990 string. 27991 27991 file_data: 27992 27992 type: string 27993 27993 description: | 27994 - The base64 encoded file data, used when passing the file to the model 27994 + The base64 encoded file data, used when passing the file to the model 27995 27995 as a string. 27996 27996 file_id: 27997 27997 type: string ··· 28528 28528 - click 28529 28529 default: click 28530 28530 description: | 28531 - Specifies the event type. For a click action, this property is 28531 + Specifies the event type. For a click action, this property is 28532 28532 always set to `click`. 28533 28533 x-stainless-const: true 28534 28534 button: ··· 28740 28740 propertyName: type 28741 28741 nullable: true 28742 28742 description: | 28743 - The outputs generated by the code interpreter, such as logs or images. 28743 + The outputs generated by the code interpreter, such as logs or images. 28744 28744 Can be null if no outputs are available. 28745 28745 required: 28746 28746 - type ··· 28915 28915 - computer_screenshot 28916 28916 default: computer_screenshot 28917 28917 description: | 28918 - Specifies the event type. For a computer screenshot, this property is 28918 + Specifies the event type. For a computer screenshot, this property is 28919 28919 always set to `computer_screenshot`. 28920 28920 x-stainless-const: true 28921 28921 image_url: ··· 28930 28930 type: object 28931 28931 title: Computer tool call 28932 28932 description: | 28933 - A tool call to a computer use tool. See the 28933 + A tool call to a computer use tool. See the 28934 28934 [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information. 28935 28935 properties: 28936 28936 type: ··· 28995 28995 acknowledged_safety_checks: 28996 28996 type: array 28997 28997 description: | 28998 - The safety checks reported by the API that have been acknowledged by the 28998 + The safety checks reported by the API that have been acknowledged by the 28999 28999 developer. 29000 29000 items: 29001 29001 $ref: '#/components/schemas/ComputerToolCallSafetyCheck' ··· 29916 29916 type: object 29917 29917 description: | 29918 29918 Represents a streamed chunk of a chat completion response returned 29919 - by the model, based on the provided input. 29919 + by the model, based on the provided input. 29920 29920 [Learn more](https://platform.openai.com/docs/guides/streaming-responses). 29921 29921 properties: 29922 29922 id: ··· 31354 31354 description: | 31355 31355 The image(s) to edit. Must be a supported image file or an array of images. 31356 31356 31357 - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less 31357 + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less 31358 31358 than 50MB. You can provide up to 16 images. 31359 31359 31360 - For `dall-e-2`, you can only provide one image, and it should be a square 31360 + For `dall-e-2`, you can only provide one image, and it should be a square 31361 31361 `png` file less than 4MB. 31362 31362 x-oaiMeta: 31363 31363 exampleFilePath: otter.png ··· 31386 31386 example: transparent 31387 31387 nullable: true 31388 31388 description: | 31389 - Allows to set transparency for the background of the generated image(s). 31390 - This parameter is only supported for `gpt-image-1`. Must be one of 31391 - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the 31389 + Allows to set transparency for the background of the generated image(s). 31390 + This parameter is only supported for `gpt-image-1`. Must be one of 31391 + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the 31392 31392 model will automatically determine the best background for the image. 31393 31393 31394 - If `transparent`, the output format needs to support transparency, so it 31394 + If `transparent`, the output format needs to support transparency, so it 31395 31395 should be set to either `png` (default value) or `webp`. 31396 31396 model: 31397 31397 anyOf: ··· 31461 31461 example: 100 31462 31462 nullable: true 31463 31463 description: | 31464 - The compression level (0-100%) for the generated images. This parameter 31465 - is only supported for `gpt-image-1` with the `webp` or `jpeg` output 31464 + The compression level (0-100%) for the generated images. This parameter 31465 + is only supported for `gpt-image-1` with the `webp` or `jpeg` output 31466 31466 formats, and defaults to 100. 31467 31467 user: 31468 31468 type: string ··· 31478 31478 example: false 31479 31479 nullable: true 31480 31480 description: > 31481 - Edit the image in streaming mode. Defaults to `false`. See the 31481 + Edit the image in streaming mode. Defaults to `false`. See the 31482 31482 31483 31483 [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more 31484 31484 information. ··· 31547 31547 example: medium 31548 31548 nullable: true 31549 31549 description: | 31550 - The quality of the image that will be generated. 31550 + The quality of the image that will be generated. 31551 31551 31552 31552 - `auto` (default value) will automatically select the best quality for the given model. 31553 31553 - `high`, `medium` and `low` are supported for `gpt-image-1`. ··· 31591 31591 example: false 31592 31592 nullable: true 31593 31593 description: > 31594 - Generate the image in streaming mode. Defaults to `false`. See the 31594 + Generate the image in streaming mode. Defaults to `false`. See the 31595 31595 31596 31596 [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more 31597 31597 information. ··· 31638 31638 example: transparent 31639 31639 nullable: true 31640 31640 description: | 31641 - Allows to set transparency for the background of the generated image(s). 31642 - This parameter is only supported for `gpt-image-1`. Must be one of 31643 - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the 31641 + Allows to set transparency for the background of the generated image(s). 31642 + This parameter is only supported for `gpt-image-1`. Must be one of 31643 + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the 31644 31644 model will automatically determine the best background for the image. 31645 31645 31646 - If `transparent`, the output format needs to support transparency, so it 31646 + If `transparent`, the output format needs to support transparency, so it 31647 31647 should be set to either `png` (default value) or `webp`. 31648 31648 style: 31649 31649 type: string ··· 32664 32664 CreateThreadRequest: 32665 32665 type: object 32666 32666 description: | 32667 - Options to create a new thread. If no thread is provided when running a 32667 + Options to create a new thread. If no thread is provided when running a 32668 32668 request, an empty thread will be created. 32669 32669 additionalProperties: false 32670 32670 properties: ··· 32853 32853 If set to true, the model response data will be streamed to the client 32854 32854 32855 32855 as it is generated using [server-sent 32856 - events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). 32856 + events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). 32857 32857 32858 32858 See the [Streaming section of the Speech-to-Text 32859 32859 guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) ··· 32883 32883 - segment 32884 32884 include: 32885 32885 description: | 32886 - Additional information to include in the transcription response. 32887 - `logprobs` will return the log probabilities of the tokens in the 32888 - response to understand the model's confidence in the transcription. 32889 - `logprobs` only works with response_format set to `json` and only with 32886 + Additional information to include in the transcription response. 32887 + `logprobs` will return the log probabilities of the tokens in the 32888 + response to understand the model's confidence in the transcription. 32889 + `logprobs` only works with response_format set to `json` and only with 32890 32890 the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`. 32891 32891 type: array 32892 32892 items: ··· 33573 33573 - double_click 33574 33574 default: double_click 33575 33575 description: | 33576 - Specifies the event type. For a double click action, this property is 33576 + Specifies the event type. For a double click action, this property is 33577 33577 always set to `double_click`. 33578 33578 x-stainless-const: true 33579 33579 x: ··· 33600 33600 - drag 33601 33601 default: drag 33602 33602 description: | 33603 - Specifies the event type. For a drag action, this property is 33603 + Specifies the event type. For a drag action, this property is 33604 33604 always set to `drag`. 33605 33605 x-stainless-const: true 33606 33606 path: ··· 35189 35189 type: object 35190 35190 title: File search tool call 35191 35191 description: | 35192 - The results of a file search tool call. See the 35192 + The results of a file search tool call. See the 35193 35193 [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information. 35194 35194 properties: 35195 35195 id: ··· 35206 35206 status: 35207 35207 type: string 35208 35208 description: | 35209 - The status of the file search tool call. One of `in_progress`, 35209 + The status of the file search tool call. One of `in_progress`, 35210 35210 `searching`, `incomplete` or `failed`, 35211 35211 enum: 35212 35212 - in_progress ··· 36146 36146 description: >- 36147 36147 The parameters the functions accepts, described as a JSON Schema object. See the 36148 36148 [guide](https://platform.openai.com/docs/guides/function-calling) for examples, and the [JSON Schema 36149 - reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. 36149 + reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. 36150 36150 36151 36151 36152 36152 Omitting `parameters` defines a function with an empty parameter list. ··· 36155 36155 type: object 36156 36156 title: Function tool call 36157 36157 description: > 36158 - A tool call to run a function. See the 36158 + A tool call to run a function. See the 36159 36159 36160 36160 [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more 36161 36161 information. ··· 36578 36578 - rouge_5 36579 36579 - rouge_l 36580 36580 description: | 36581 - The evaluation metric to use. One of `cosine`, `fuzzy_match`, `bleu`, 36582 - `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, 36581 + The evaluation metric to use. One of `cosine`, `fuzzy_match`, `bleu`, 36582 + `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, 36583 36583 or `rouge_l`. 36584 36584 required: 36585 36585 - type ··· 36991 36991 - high 36992 36992 - auto 36993 36993 description: | 36994 - The quality of the generated image. One of `low`, `medium`, `high`, 36994 + The quality of the generated image. One of `low`, `medium`, `high`, 36995 36995 or `auto`. Default: `auto`. 36996 36996 default: auto 36997 36997 size: ··· 37002 37002 - 1536x1024 37003 37003 - auto 37004 37004 description: | 37005 - The size of the generated image. One of `1024x1024`, `1024x1536`, 37005 + The size of the generated image. One of `1024x1024`, `1024x1536`, 37006 37006 `1536x1024`, or `auto`. Default: `auto`. 37007 37007 default: auto 37008 37008 output_format: ··· 37012 37012 - webp 37013 37013 - jpeg 37014 37014 description: | 37015 - The output format of the generated image. One of `png`, `webp`, or 37015 + The output format of the generated image. One of `png`, `webp`, or 37016 37016 `jpeg`. Default: `png`. 37017 37017 default: png 37018 37018 output_compression: ··· 37037 37037 - opaque 37038 37038 - auto 37039 37039 description: | 37040 - Background type for the generated image. One of `transparent`, 37040 + Background type for the generated image. One of `transparent`, 37041 37041 `opaque`, or `auto`. Default: `auto`. 37042 37042 default: auto 37043 37043 input_fidelity: ··· 37045 37045 input_image_mask: 37046 37046 type: object 37047 37047 description: | 37048 - Optional mask for inpainting. Contains `image_url` 37048 + Optional mask for inpainting. Contains `image_url` 37049 37049 (string, optional) and `file_id` (string, optional). 37050 37050 properties: 37051 37051 image_url: ··· 37284 37284 - type: object 37285 37285 title: Item 37286 37286 description: | 37287 - An item representing part of the context for the response to be 37287 + An item representing part of the context for the response to be 37288 37288 generated by the model. Can contain text, images, and audio inputs, 37289 37289 as well as previous assistant responses and tool call outputs. 37290 37290 $ref: '#/components/schemas/Item' ··· 37330 37330 type: array 37331 37331 title: Input item content list 37332 37332 description: | 37333 - A list of one or many input items to the model, containing different content 37333 + A list of one or many input items to the model, containing different content 37334 37334 types. 37335 37335 items: 37336 37336 $ref: '#/components/schemas/InputContent' ··· 37563 37563 - keypress 37564 37564 default: keypress 37565 37565 description: | 37566 - Specifies the event type. For a keypress action, this property is 37566 + Specifies the event type. For a keypress action, this property is 37567 37567 always set to `keypress`. 37568 37568 x-stainless-const: true 37569 37569 keys: ··· 38307 38307 type: object 38308 38308 title: MCP tool 38309 38309 description: | 38310 - Give the model access to additional tools via remote Model Context Protocol 38310 + Give the model access to additional tools via remote Model Context Protocol 38311 38311 (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). 38312 38312 properties: 38313 38313 type: ··· 38390 38390 - type: string 38391 38391 title: MCP tool approval setting 38392 38392 description: | 38393 - Specify a single approval policy for all tools. One of `always` or 38394 - `never`. When set to `always`, all tools will require approval. When 38393 + Specify a single approval policy for all tools. One of `always` or 38394 + `never`. When set to `always`, all tools will require approval. When 38395 38395 set to `never`, all tools will not require approval. 38396 38396 enum: 38397 38397 - always ··· 39116 39116 description: | 39117 39117 Set of 16 key-value pairs that can be attached to an object. This can be 39118 39118 useful for storing additional information about the object in a structured 39119 - format, and querying for objects via API or the dashboard. 39119 + format, and querying for objects via API or the dashboard. 39120 39120 39121 39121 Keys are strings with a maximum length of 64 characters. Values are strings 39122 39122 with a maximum length of 512 characters. ··· 39230 39230 This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` 39231 39231 instead to maintain caching optimizations. 39232 39232 39233 - A stable identifier for your end-users. 39233 + A stable identifier for your end-users. 39234 39234 39235 39235 Used to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and 39236 39236 prevent abuse. [Learn ··· 39240 39240 example: safety-identifier-1234 39241 39241 description: > 39242 39242 A stable identifier used to help detect users of your application that may be violating OpenAI's 39243 - usage policies. 39243 + usage policies. 39244 39244 39245 39245 The IDs should be a string that uniquely identifies each user. We recommend hashing their username 39246 39246 or email address, in order to avoid sending us any identifying information. [Learn ··· 39427 39427 - move 39428 39428 default: move 39429 39429 description: | 39430 - Specifies the event type. For a move action, this property is 39430 + Specifies the event type. For a move action, this property is 39431 39431 always set to `move`. 39432 39432 x-stainless-const: true 39433 39433 x: ··· 39646 39646 streaming responses that return partial images. Value must be between 0 and 3. 39647 39647 When set to 0, the response will be a single image sent in one streaming event. 39648 39648 39649 - Note that the final image may be sent before the full number of partial images 39649 + Note that the final image may be sent before the full number of partial images 39650 39650 are generated if the full image is generated more quickly. 39651 39651 PredictionContent: 39652 39652 type: object ··· 40230 40230 type: object 40231 40231 nullable: true 40232 40232 description: | 40233 - Reference to a prompt template and its variables. 40233 + Reference to a prompt template and its variables. 40234 40234 [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). 40235 40235 required: 40236 40236 - id ··· 40265 40265 RealtimeClientEventConversationItemCreate: 40266 40266 type: object 40267 40267 description: | 40268 - Add a new Item to the Conversation's context, including messages, function 40269 - calls, and function call responses. This event can be used both to populate a 40270 - "history" of the conversation and to add new items mid-stream, but has the 40268 + Add a new Item to the Conversation's context, including messages, function 40269 + calls, and function call responses. This event can be used both to populate a 40270 + "history" of the conversation and to add new items mid-stream, but has the 40271 40271 current limitation that it cannot populate assistant audio messages. 40272 40272 40273 - If successful, the server will respond with a `conversation.item.created` 40273 + If successful, the server will respond with a `conversation.item.created` 40274 40274 event, otherwise an `error` event will be sent. 40275 40275 properties: 40276 40276 event_id: ··· 40283 40283 previous_item_id: 40284 40284 type: string 40285 40285 description: | 40286 - The ID of the preceding item after which the new item will be inserted. 40286 + The ID of the preceding item after which the new item will be inserted. 40287 40287 If not set, the new item will be appended to the end of the conversation. 40288 40288 If set to `root`, the new item will be added to the beginning of the conversation. 40289 40289 If set to an existing ID, it allows an item to be inserted mid-conversation. If the ··· 40316 40316 RealtimeClientEventConversationItemDelete: 40317 40317 type: object 40318 40318 description: | 40319 - Send this event when you want to remove any item from the conversation 40320 - history. The server will respond with a `conversation.item.deleted` event, 40321 - unless the item does not exist in the conversation history, in which case the 40319 + Send this event when you want to remove any item from the conversation 40320 + history. The server will respond with a `conversation.item.deleted` event, 40321 + unless the item does not exist in the conversation history, in which case the 40322 40322 server will respond with an error. 40323 40323 properties: 40324 40324 event_id: ··· 40350 40350 conversation history. This is useful, for example, to inspect user audio after noise cancellation and 40351 40351 VAD. 40352 40352 40353 - The server will respond with a `conversation.item.retrieved` event, 40353 + The server will respond with a `conversation.item.retrieved` event, 40354 40354 40355 - unless the item does not exist in the conversation history, in which case the 40355 + unless the item does not exist in the conversation history, in which case the 40356 40356 40357 40357 server will respond with an error. 40358 40358 properties: ··· 40381 40381 RealtimeClientEventConversationItemTruncate: 40382 40382 type: object 40383 40383 description: | 40384 - Send this event to truncate a previous assistant message’s audio. The server 40385 - will produce audio faster than realtime, so this event is useful when the user 40386 - interrupts to truncate audio that has already been sent to the client but not 40387 - yet played. This will synchronize the server's understanding of the audio with 40384 + Send this event to truncate a previous assistant message’s audio. The server 40385 + will produce audio faster than realtime, so this event is useful when the user 40386 + interrupts to truncate audio that has already been sent to the client but not 40387 + yet played. This will synchronize the server's understanding of the audio with 40388 40388 the client's playback. 40389 40389 40390 - Truncating audio will delete the server-side text transcript to ensure there 40390 + Truncating audio will delete the server-side text transcript to ensure there 40391 40391 is not text in the context that hasn't been heard by the user. 40392 40392 40393 - If successful, the server will respond with a `conversation.item.truncated` 40393 + If successful, the server will respond with a `conversation.item.truncated` 40394 40394 event. 40395 40395 properties: 40396 40396 event_id: ··· 40403 40403 item_id: 40404 40404 type: string 40405 40405 description: | 40406 - The ID of the assistant message item to truncate. Only assistant message 40406 + The ID of the assistant message item to truncate. Only assistant message 40407 40407 items can be truncated. 40408 40408 content_index: 40409 40409 type: integer ··· 40411 40411 audio_end_ms: 40412 40412 type: integer 40413 40413 description: | 40414 - Inclusive duration up to which audio is truncated, in milliseconds. If 40415 - the audio_end_ms is greater than the actual audio duration, the server 40414 + Inclusive duration up to which audio is truncated, in milliseconds. If 40415 + the audio_end_ms is greater than the actual audio duration, the server 40416 40416 will respond with an error. 40417 40417 required: 40418 40418 - type ··· 40433 40433 RealtimeClientEventInputAudioBufferAppend: 40434 40434 type: object 40435 40435 description: | 40436 - Send this event to append audio bytes to the input audio buffer. The audio 40437 - buffer is temporary storage you can write to and later commit. In Server VAD 40438 - mode, the audio buffer is used to detect speech and the server will decide 40436 + Send this event to append audio bytes to the input audio buffer. The audio 40437 + buffer is temporary storage you can write to and later commit. In Server VAD 40438 + mode, the audio buffer is used to detect speech and the server will decide 40439 40439 when to commit. When Server VAD is disabled, you must commit the audio buffer 40440 40440 manually. 40441 40441 40442 - The client may choose how much audio to place in each event up to a maximum 40443 - of 15 MiB, for example streaming smaller chunks from the client may allow the 40444 - VAD to be more responsive. Unlike made other client events, the server will 40442 + The client may choose how much audio to place in each event up to a maximum 40443 + of 15 MiB, for example streaming smaller chunks from the client may allow the 40444 + VAD to be more responsive. Unlike made other client events, the server will 40445 40445 not send a confirmation response to this event. 40446 40446 properties: 40447 40447 event_id: ··· 40454 40454 audio: 40455 40455 type: string 40456 40456 description: | 40457 - Base64-encoded audio bytes. This must be in the format specified by the 40457 + Base64-encoded audio bytes. This must be in the format specified by the 40458 40458 `input_audio_format` field in the session configuration. 40459 40459 required: 40460 40460 - type ··· 40471 40471 RealtimeClientEventInputAudioBufferClear: 40472 40472 type: object 40473 40473 description: | 40474 - Send this event to clear the audio bytes in the buffer. The server will 40474 + Send this event to clear the audio bytes in the buffer. The server will 40475 40475 respond with an `input_audio_buffer.cleared` event. 40476 40476 properties: 40477 40477 event_id: ··· 40494 40494 RealtimeClientEventInputAudioBufferCommit: 40495 40495 type: object 40496 40496 description: | 40497 - Send this event to commit the user input audio buffer, which will create a 40498 - new user message item in the conversation. This event will produce an error 40499 - if the input audio buffer is empty. When in Server VAD mode, the client does 40500 - not need to send this event, the server will commit the audio buffer 40497 + Send this event to commit the user input audio buffer, which will create a 40498 + new user message item in the conversation. This event will produce an error 40499 + if the input audio buffer is empty. When in Server VAD mode, the client does 40500 + not need to send this event, the server will commit the audio buffer 40501 40501 automatically. 40502 40502 40503 - Committing the input audio buffer will trigger input audio transcription 40504 - (if enabled in session configuration), but it will not create a response 40505 - from the model. The server will respond with an `input_audio_buffer.committed` 40503 + Committing the input audio buffer will trigger input audio transcription 40504 + (if enabled in session configuration), but it will not create a response 40505 + from the model. The server will respond with an `input_audio_buffer.committed` 40506 40506 event. 40507 40507 properties: 40508 40508 event_id: ··· 40527 40527 description: > 40528 40528 **WebRTC Only:** Emit to cut off the current audio response. This will trigger the server to 40529 40529 40530 - stop generating audio and emit a `output_audio_buffer.cleared` event. This 40530 + stop generating audio and emit a `output_audio_buffer.cleared` event. This 40531 40531 40532 - event should be preceded by a `response.cancel` client event to stop the 40532 + event should be preceded by a `response.cancel` client event to stop the 40533 40533 40534 40534 generation of the current response. 40535 40535 ··· 40556 40556 RealtimeClientEventResponseCancel: 40557 40557 type: object 40558 40558 description: | 40559 - Send this event to cancel an in-progress response. The server will respond 40560 - with a `response.done` event with a status of `response.status=cancelled`. If 40559 + Send this event to cancel an in-progress response. The server will respond 40560 + with a `response.done` event with a status of `response.status=cancelled`. If 40561 40561 there is no response to cancel, the server will respond with an error. 40562 40562 properties: 40563 40563 event_id: ··· 40570 40570 response_id: 40571 40571 type: string 40572 40572 description: | 40573 - A specific response ID to cancel - if not provided, will cancel an 40573 + A specific response ID to cancel - if not provided, will cancel an 40574 40574 in-progress response in the default conversation. 40575 40575 required: 40576 40576 - type ··· 40585 40585 RealtimeClientEventResponseCreate: 40586 40586 type: object 40587 40587 description: | 40588 - This event instructs the server to create a Response, which means triggering 40589 - model inference. When in Server VAD mode, the server will create Responses 40588 + This event instructs the server to create a Response, which means triggering 40589 + model inference. When in Server VAD mode, the server will create Responses 40590 40590 automatically. 40591 40591 40592 - A Response will include at least one Item, and may have two, in which case 40593 - the second will be a function call. These Items will be appended to the 40592 + A Response will include at least one Item, and may have two, in which case 40593 + the second will be a function call. These Items will be appended to the 40594 40594 conversation history. 40595 40595 40596 - The server will respond with a `response.created` event, events for Items 40597 - and content created, and finally a `response.done` event to indicate the 40596 + The server will respond with a `response.created` event, events for Items 40597 + and content created, and finally a `response.done` event to indicate the 40598 40598 Response is complete. 40599 40599 40600 - The `response.create` event includes inference configuration like 40601 - `instructions`, and `temperature`. These fields will override the Session's 40600 + The `response.create` event includes inference configuration like 40601 + `instructions`, and `temperature`. These fields will override the Session's 40602 40602 configuration for this Response only. 40603 40603 properties: 40604 40604 event_id: ··· 40766 40766 id: 40767 40767 type: string 40768 40768 description: | 40769 - The unique ID of the item, this can be generated by the client to help 40770 - manage server-side context, but is not required because the server will 40769 + The unique ID of the item, this can be generated by the client to help 40770 + manage server-side context, but is not required because the server will 40771 40771 generate one if not provided. 40772 40772 type: 40773 40773 type: string ··· 40791 40791 - incomplete 40792 40792 - in_progress 40793 40793 description: | 40794 - The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect 40795 - on the conversation, but are accepted for consistency with the 40794 + The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect 40795 + on the conversation, but are accepted for consistency with the 40796 40796 `conversation.item.created` event. 40797 40797 role: 40798 40798 type: string ··· 40801 40801 - assistant 40802 40802 - system 40803 40803 description: | 40804 - The role of the message sender (`user`, `assistant`, `system`), only 40804 + The role of the message sender (`user`, `assistant`, `system`), only 40805 40805 applicable for `message` items. 40806 40806 content: 40807 40807 type: array 40808 40808 description: | 40809 - The content of the message, applicable for `message` items. 40809 + The content of the message, applicable for `message` items. 40810 40810 - Message items of role `system` support only `input_text` content 40811 - - Message items of role `user` support `input_text` and `input_audio` 40811 + - Message items of role `user` support `input_text` and `input_audio` 40812 40812 content 40813 40813 - Message items of role `assistant` support `text` content. 40814 40814 items: ··· 40816 40816 call_id: 40817 40817 type: string 40818 40818 description: | 40819 - The ID of the function call (for `function_call` and 40820 - `function_call_output` items). If passed on a `function_call_output` 40821 - item, the server will check that a `function_call` item with the same 40819 + The ID of the function call (for `function_call` and 40820 + `function_call_output` items). If passed on a `function_call_output` 40821 + item, the server will check that a `function_call` item with the same 40822 40822 ID exists in the conversation history. 40823 40823 name: 40824 40824 type: string ··· 40868 40868 - incomplete 40869 40869 - in_progress 40870 40870 description: | 40871 - The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect 40872 - on the conversation, but are accepted for consistency with the 40871 + The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect 40872 + on the conversation, but are accepted for consistency with the 40873 40873 `conversation.item.created` event. 40874 40874 role: 40875 40875 type: string ··· 40878 40878 - assistant 40879 40879 - system 40880 40880 description: | 40881 - The role of the message sender (`user`, `assistant`, `system`), only 40881 + The role of the message sender (`user`, `assistant`, `system`), only 40882 40882 applicable for `message` items. 40883 40883 content: 40884 40884 type: array 40885 40885 description: | 40886 - The content of the message, applicable for `message` items. 40886 + The content of the message, applicable for `message` items. 40887 40887 - Message items of role `system` support only `input_text` content 40888 - - Message items of role `user` support `input_text` and `input_audio` 40888 + - Message items of role `user` support `input_text` and `input_audio` 40889 40889 content 40890 40890 - Message items of role `assistant` support `text` content. 40891 40891 items: ··· 40921 40921 call_id: 40922 40922 type: string 40923 40923 description: | 40924 - The ID of the function call (for `function_call` and 40925 - `function_call_output` items). If passed on a `function_call_output` 40926 - item, the server will check that a `function_call` item with the same 40924 + The ID of the function call (for `function_call` and 40925 + `function_call_output` items). If passed on a `function_call_output` 40926 + item, the server will check that a `function_call` item with the same 40927 40927 ID exists in the conversation history. 40928 40928 name: 40929 40929 type: string ··· 40957 40957 - incomplete 40958 40958 - in_progress 40959 40959 description: | 40960 - The final status of the response (`completed`, `cancelled`, `failed`, or 40960 + The final status of the response (`completed`, `cancelled`, `failed`, or 40961 40961 `incomplete`, `in_progress`). 40962 40962 status_details: 40963 40963 type: object ··· 40971 40971 - incomplete 40972 40972 - failed 40973 40973 description: | 40974 - The type of error that caused the response to fail, corresponding 40975 - with the `status` field (`completed`, `cancelled`, `incomplete`, 40974 + The type of error that caused the response to fail, corresponding 40975 + with the `status` field (`completed`, `cancelled`, `incomplete`, 40976 40976 `failed`). 40977 40977 reason: 40978 40978 type: string ··· 40982 40982 - max_output_tokens 40983 40983 - content_filter 40984 40984 description: | 40985 - The reason the Response did not complete. For a `cancelled` Response, 40986 - one of `turn_detected` (the server VAD detected a new start of speech) 40987 - or `client_cancelled` (the client sent a cancel event). For an 40988 - `incomplete` Response, one of `max_output_tokens` or `content_filter` 40985 + The reason the Response did not complete. For a `cancelled` Response, 40986 + one of `turn_detected` (the server VAD detected a new start of speech) 40987 + or `client_cancelled` (the client sent a cancel event). For an 40988 + `incomplete` Response, one of `max_output_tokens` or `content_filter` 40989 40989 (the server-side safety filter activated and cut off the response). 40990 40990 error: 40991 40991 type: object 40992 40992 description: | 40993 - A description of the error that caused the response to fail, 40993 + A description of the error that caused the response to fail, 40994 40994 populated when the `status` is `failed`. 40995 40995 properties: 40996 40996 type: ··· 41009 41009 usage: 41010 41010 type: object 41011 41011 description: | 41012 - Usage statistics for the Response, this will correspond to billing. A 41013 - Realtime API session will maintain a conversation context and append new 41014 - Items to the Conversation, thus output from previous turns (text and 41012 + Usage statistics for the Response, this will correspond to billing. A 41013 + Realtime API session will maintain a conversation context and append new 41014 + Items to the Conversation, thus output from previous turns (text and 41015 41015 audio tokens) will become the input for later turns. 41016 41016 properties: 41017 41017 total_tokens: 41018 41018 type: integer 41019 41019 description: | 41020 - The total number of tokens in the Response including input and output 41020 + The total number of tokens in the Response including input and output 41021 41021 text and audio tokens. 41022 41022 input_tokens: 41023 41023 type: integer 41024 41024 description: | 41025 - The number of input tokens used in the Response, including text and 41025 + The number of input tokens used in the Response, including text and 41026 41026 audio tokens. 41027 41027 output_tokens: 41028 41028 type: integer 41029 41029 description: | 41030 - The number of output tokens sent in the Response, including text and 41030 + The number of output tokens sent in the Response, including text and 41031 41031 audio tokens. 41032 41032 input_token_details: 41033 41033 type: object ··· 41118 41118 instructions: 41119 41119 type: string 41120 41120 description: | 41121 - The default system instructions (i.e. system message) prepended to model 41122 - calls. This field allows the client to guide the model on desired 41123 - responses. The model can be instructed on response content and format, 41124 - (e.g. "be extremely succinct", "act friendly", "here are examples of good 41125 - responses") and on audio behavior (e.g. "talk quickly", "inject emotion 41126 - into your voice", "laugh frequently"). The instructions are not guaranteed 41127 - to be followed by the model, but they provide guidance to the model on the 41121 + The default system instructions (i.e. system message) prepended to model 41122 + calls. This field allows the client to guide the model on desired 41123 + responses. The model can be instructed on response content and format, 41124 + (e.g. "be extremely succinct", "act friendly", "here are examples of good 41125 + responses") and on audio behavior (e.g. "talk quickly", "inject emotion 41126 + into your voice", "laugh frequently"). The instructions are not guaranteed 41127 + to be followed by the model, but they provide guidance to the model on the 41128 41128 desired behavior. 41129 41129 41130 - Note that the server sets default instructions which will be used if this 41131 - field is not set and are visible in the `session.created` event at the 41130 + Note that the server sets default instructions which will be used if this 41131 + field is not set and are visible in the `session.created` event at the 41132 41132 start of the session. 41133 41133 voice: 41134 41134 $ref: '#/components/schemas/VoiceIdsShared' 41135 41135 description: | 41136 - The voice the model uses to respond. Voice cannot be changed during the 41137 - session once the model has responded with audio at least once. Current 41136 + The voice the model uses to respond. Voice cannot be changed during the 41137 + session once the model has responded with audio at least once. Current 41138 41138 voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, 41139 41139 `shimmer`, and `verse`. 41140 41140 output_audio_format: ··· 41163 41163 description: 41164 41164 type: string 41165 41165 description: | 41166 - The description of the function, including guidance on when and how 41167 - to call it, and guidance about what to tell the user when calling 41166 + The description of the function, including guidance on when and how 41167 + to call it, and guidance about what to tell the user when calling 41168 41168 (if anything). 41169 41169 parameters: 41170 41170 type: object ··· 41172 41172 tool_choice: 41173 41173 type: string 41174 41174 description: | 41175 - How the model chooses tools. Options are `auto`, `none`, `required`, or 41175 + How the model chooses tools. Options are `auto`, `none`, `required`, or 41176 41176 specify a function, like `{"type": "function", "function": {"name": "my_function"}}`. 41177 41177 temperature: 41178 41178 type: number ··· 41195 41195 Controls which conversation the response is added to. Currently supports 41196 41196 `auto` and `none`, with `auto` as the default value. The `auto` value 41197 41197 means that the contents of the response will be added to the default 41198 - conversation. Set this to `none` to create an out-of-band response which 41198 + conversation. Set this to `none` to create an out-of-band response which 41199 41199 will not add items to default conversation. 41200 41200 anyOf: 41201 41201 - type: string ··· 41297 41297 type: object 41298 41298 description: | 41299 41299 Returned when a conversation item is created. There are several scenarios that produce this event: 41300 - - The server is generating a Response, which if successful will produce 41301 - either one or two Items, which will be of type `message` 41300 + - The server is generating a Response, which if successful will produce 41301 + either one or two Items, which will be of type `message` 41302 41302 (role `assistant`) or type `function_call`. 41303 - - The input audio buffer has been committed, either by the client or the 41304 - server (in `server_vad` mode). The server will take the content of the 41303 + - The input audio buffer has been committed, either by the client or the 41304 + server (in `server_vad` mode). The server will take the content of the 41305 41305 input audio buffer and add it to a new user message Item. 41306 - - The client has sent a `conversation.item.create` event to add a new Item 41306 + - The client has sent a `conversation.item.create` event to add a new Item 41307 41307 to the Conversation. 41308 41308 properties: 41309 41309 event_id: ··· 41317 41317 type: string 41318 41318 nullable: true 41319 41319 description: | 41320 - The ID of the preceding item in the Conversation context, allows the 41321 - client to understand the order of the conversation. Can be `null` if the 41320 + The ID of the preceding item in the Conversation context, allows the 41321 + client to understand the order of the conversation. Can be `null` if the 41322 41322 item has no predecessor. 41323 41323 item: 41324 41324 $ref: '#/components/schemas/RealtimeConversationItem' ··· 41346 41346 RealtimeServerEventConversationItemDeleted: 41347 41347 type: object 41348 41348 description: | 41349 - Returned when an item in the conversation is deleted by the client with a 41350 - `conversation.item.delete` event. This event is used to synchronize the 41349 + Returned when an item in the conversation is deleted by the client with a 41350 + `conversation.item.delete` event. This event is used to synchronize the 41351 41351 server's understanding of the conversation history with the client's view. 41352 41352 properties: 41353 41353 event_id: ··· 41494 41494 RealtimeServerEventConversationItemInputAudioTranscriptionFailed: 41495 41495 type: object 41496 41496 description: | 41497 - Returned when input audio transcription is configured, and a transcription 41498 - request for a user message failed. These events are separate from other 41497 + Returned when input audio transcription is configured, and a transcription 41498 + request for a user message failed. These events are separate from other 41499 41499 `error` events so that the client can identify the related Item. 41500 41500 properties: 41501 41501 event_id: ··· 41597 41597 RealtimeServerEventConversationItemTruncated: 41598 41598 type: object 41599 41599 description: | 41600 - Returned when an earlier assistant audio message item is truncated by the 41601 - client with a `conversation.item.truncate` event. This event is used to 41600 + Returned when an earlier assistant audio message item is truncated by the 41601 + client with a `conversation.item.truncate` event. This event is used to 41602 41602 synchronize the server's understanding of the audio with the client's playback. 41603 41603 41604 - This action will truncate the audio and remove the server-side text transcript 41604 + This action will truncate the audio and remove the server-side text transcript 41605 41605 to ensure there is no text in the context that hasn't been heard by the user. 41606 41606 properties: 41607 41607 event_id: ··· 41641 41641 RealtimeServerEventError: 41642 41642 type: object 41643 41643 description: | 41644 - Returned when an error occurs, which could be a client problem or a server 41645 - problem. Most errors are recoverable and the session will stay open, we 41644 + Returned when an error occurs, which could be a client problem or a server 41645 + problem. Most errors are recoverable and the session will stay open, we 41646 41646 recommend to implementors to monitor and log error messages by default. 41647 41647 properties: 41648 41648 event_id: ··· 41701 41701 RealtimeServerEventInputAudioBufferCleared: 41702 41702 type: object 41703 41703 description: | 41704 - Returned when the input audio buffer is cleared by the client with a 41704 + Returned when the input audio buffer is cleared by the client with a 41705 41705 `input_audio_buffer.clear` event. 41706 41706 properties: 41707 41707 event_id: ··· 41725 41725 RealtimeServerEventInputAudioBufferCommitted: 41726 41726 type: object 41727 41727 description: | 41728 - Returned when an input audio buffer is committed, either by the client or 41728 + Returned when an input audio buffer is committed, either by the client or 41729 41729 automatically in server VAD mode. The `item_id` property is the ID of the user 41730 - message item that will be created, thus a `conversation.item.created` event 41730 + message item that will be created, thus a `conversation.item.created` event 41731 41731 will also be sent to the client. 41732 41732 properties: 41733 41733 event_id: ··· 41763 41763 RealtimeServerEventInputAudioBufferSpeechStarted: 41764 41764 type: object 41765 41765 description: | 41766 - Sent by the server when in `server_vad` mode to indicate that speech has been 41767 - detected in the audio buffer. This can happen any time audio is added to the 41768 - buffer (unless speech is already detected). The client may want to use this 41769 - event to interrupt audio playback or provide visual feedback to the user. 41766 + Sent by the server when in `server_vad` mode to indicate that speech has been 41767 + detected in the audio buffer. This can happen any time audio is added to the 41768 + buffer (unless speech is already detected). The client may want to use this 41769 + event to interrupt audio playback or provide visual feedback to the user. 41770 41770 41771 - The client should expect to receive a `input_audio_buffer.speech_stopped` event 41772 - when speech stops. The `item_id` property is the ID of the user message item 41773 - that will be created when speech stops and will also be included in the 41774 - `input_audio_buffer.speech_stopped` event (unless the client manually commits 41771 + The client should expect to receive a `input_audio_buffer.speech_stopped` event 41772 + when speech stops. The `item_id` property is the ID of the user message item 41773 + that will be created when speech stops and will also be included in the 41774 + `input_audio_buffer.speech_stopped` event (unless the client manually commits 41775 41775 the audio buffer during VAD activation). 41776 41776 properties: 41777 41777 event_id: ··· 41784 41784 audio_start_ms: 41785 41785 type: integer 41786 41786 description: | 41787 - Milliseconds from the start of all audio written to the buffer during the 41788 - session when speech was first detected. This will correspond to the 41789 - beginning of audio sent to the model, and thus includes the 41787 + Milliseconds from the start of all audio written to the buffer during the 41788 + session when speech was first detected. This will correspond to the 41789 + beginning of audio sent to the model, and thus includes the 41790 41790 `prefix_padding_ms` configured in the Session. 41791 41791 item_id: 41792 41792 type: string ··· 41810 41810 RealtimeServerEventInputAudioBufferSpeechStopped: 41811 41811 type: object 41812 41812 description: | 41813 - Returned in `server_vad` mode when the server detects the end of speech in 41814 - the audio buffer. The server will also send an `conversation.item.created` 41813 + Returned in `server_vad` mode when the server detects the end of speech in 41814 + the audio buffer. The server will also send an `conversation.item.created` 41815 41815 event with the user message item that is created from the audio buffer. 41816 41816 properties: 41817 41817 event_id: ··· 41824 41824 audio_end_ms: 41825 41825 type: integer 41826 41826 description: | 41827 - Milliseconds since the session started when speech stopped. This will 41828 - correspond to the end of audio sent to the model, and thus includes the 41827 + Milliseconds since the session started when speech stopped. This will 41828 + correspond to the end of audio sent to the model, and thus includes the 41829 41829 `min_silence_duration_ms` configured in the Session. 41830 41830 item_id: 41831 41831 type: string ··· 41955 41955 RealtimeServerEventRateLimitsUpdated: 41956 41956 type: object 41957 41957 description: | 41958 - Emitted at the beginning of a Response to indicate the updated rate limits. 41959 - When a Response is created some tokens will be "reserved" for the output 41960 - tokens, the rate limits shown here reflect that reservation, which is then 41958 + Emitted at the beginning of a Response to indicate the updated rate limits. 41959 + When a Response is created some tokens will be "reserved" for the output 41960 + tokens, the rate limits shown here reflect that reservation, which is then 41961 41961 adjusted accordingly once the Response is completed. 41962 41962 properties: 41963 41963 event_id: ··· 42378 42378 RealtimeServerEventResponseDone: 42379 42379 type: object 42380 42380 description: | 42381 - Returned when a Response is done streaming. Always emitted, no matter the 42382 - final state. The Response object included in the `response.done` event will 42381 + Returned when a Response is done streaming. Always emitted, no matter the 42382 + final state. The Response object included in the `response.done` event will 42383 42383 include all output Items in the Response but will omit the raw audio data. 42384 42384 properties: 42385 42385 event_id: ··· 42587 42587 RealtimeServerEventResponseOutputItemDone: 42588 42588 type: object 42589 42589 description: | 42590 - Returned when an Item is done streaming. Also emitted when a Response is 42590 + Returned when an Item is done streaming. Also emitted when a Response is 42591 42591 interrupted, incomplete, or cancelled. 42592 42592 properties: 42593 42593 event_id: ··· 42733 42733 RealtimeServerEventSessionCreated: 42734 42734 type: object 42735 42735 description: | 42736 - Returned when a Session is created. Emitted automatically when a new 42737 - connection is established as the first server event. This event will contain 42736 + Returned when a Session is created. Emitted automatically when a new 42737 + connection is established as the first server event. This event will contain 42738 42738 the default Session configuration. 42739 42739 properties: 42740 42740 event_id: ··· 42784 42784 RealtimeServerEventSessionUpdated: 42785 42785 type: object 42786 42786 description: | 42787 - Returned when a session is updated with a `session.update` event, unless 42787 + Returned when a session is updated with a `session.update` event, unless 42788 42788 there is an error. 42789 42789 properties: 42790 42790 event_id: ··· 42831 42831 RealtimeServerEventTranscriptionSessionUpdated: 42832 42832 type: object 42833 42833 description: | 42834 - Returned when a transcription session is updated with a `transcription_session.update` event, unless 42834 + Returned when a transcription session is updated with a `transcription_session.update` event, unless 42835 42835 there is an error. 42836 42836 properties: 42837 42837 event_id: ··· 43880 43880 A new Realtime transcription session configuration. 43881 43881 43882 43882 When a session is created on the server via REST API, the session object 43883 - also contains an ephemeral key. Default TTL for keys is 10 minutes. This 43883 + also contains an ephemeral key. Default TTL for keys is 10 minutes. This 43884 43884 property is not present when a session is updated via the WebSocket API. 43885 43885 properties: 43886 43886 client_secret: ··· 43948 43948 turn_detection: 43949 43949 type: object 43950 43950 description: | 43951 - Configuration for turn detection. Can be set to `null` to turn off. Server 43952 - VAD means that the model will detect the start and end of speech based on 43951 + Configuration for turn detection. Can be set to `null` to turn off. Server 43952 + VAD means that the model will detect the start and end of speech based on 43953 43953 audio volume and respond at the end of user speech. 43954 43954 properties: 43955 43955 type: ··· 43959 43959 threshold: 43960 43960 type: number 43961 43961 description: | 43962 - Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A 43963 - higher threshold will require louder audio to activate the model, and 43962 + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A 43963 + higher threshold will require louder audio to activate the model, and 43964 43964 thus might perform better in noisy environments. 43965 43965 prefix_padding_ms: 43966 43966 type: integer 43967 43967 description: | 43968 - Amount of audio to include before the VAD detected speech (in 43968 + Amount of audio to include before the VAD detected speech (in 43969 43969 milliseconds). Defaults to 300ms. 43970 43970 silence_duration_ms: 43971 43971 type: integer 43972 43972 description: | 43973 - Duration of silence to detect speech stop (in milliseconds). Defaults 43974 - to 500ms. With shorter values the model will respond more quickly, 43973 + Duration of silence to detect speech stop (in milliseconds). Defaults 43974 + to 500ms. With shorter values the model will respond more quickly, 43975 43975 but may jump in on short pauses from the user. 43976 43976 required: 43977 43977 - client_secret ··· 44044 44044 default: medium 44045 44045 nullable: true 44046 44046 description: | 44047 - Constrains effort on reasoning for 44047 + Constrains effort on reasoning for 44048 44048 [reasoning models](https://platform.openai.com/docs/guides/reasoning). 44049 44049 Currently supported values are `minimal`, `low`, `medium`, and `high`. Reducing 44050 44050 reasoning effort can result in faster responses and fewer tokens used ··· 44054 44054 description: | 44055 44055 A description of the chain of thought used by a reasoning model while generating 44056 44056 a response. Be sure to include these items in your `input` to the Responses API 44057 - for subsequent turns of a conversation if you are manually 44057 + for subsequent turns of a conversation if you are manually 44058 44058 [managing context](https://platform.openai.com/docs/guides/conversation-state). 44059 44059 title: Reasoning 44060 44060 properties: ··· 44151 44151 status: 44152 44152 type: string 44153 44153 description: | 44154 - The status of the response generation. One of `completed`, `failed`, 44154 + The status of the response generation. One of `completed`, `failed`, 44155 44155 `in_progress`, `cancelled`, `queued`, or `incomplete`. 44156 44156 enum: 44157 44157 - completed ··· 44185 44185 44186 44186 - The length and order of items in the `output` array is dependent 44187 44187 on the model's response. 44188 - - Rather than accessing the first item in the `output` array and 44188 + - Rather than accessing the first item in the `output` array and 44189 44189 assuming it's an `assistant` message with the content generated by 44190 44190 the model, you might consider using the `output_text` property where 44191 44191 supported in SDKs. ··· 44202 44202 anyOf: 44203 44203 - type: string 44204 44204 description: | 44205 - A text input to the model, equivalent to a text input with the 44205 + A text input to the model, equivalent to a text input with the 44206 44206 `developer` role. 44207 44207 - type: array 44208 44208 title: Input item list 44209 44209 description: | 44210 - A list of one or many input items to the model, containing 44210 + A list of one or many input items to the model, containing 44211 44211 different content types. 44212 44212 items: 44213 44213 $ref: '#/components/schemas/InputItem' ··· 44215 44215 type: string 44216 44216 nullable: true 44217 44217 description: | 44218 - SDK-only convenience property that contains the aggregated text output 44219 - from all `output_text` items in the `output` array, if any are present. 44218 + SDK-only convenience property that contains the aggregated text output 44219 + from all `output_text` items in the `output` array, if any are present. 44220 44220 Supported in the Python and JavaScript SDKs. 44221 44221 x-oaiSupportedSDKs: 44222 44222 - python ··· 45577 45577 "object": "response", 45578 45578 "created_at": 1740855869, 45579 45579 "status": "incomplete", 45580 - "error": null, 45580 + "error": null, 45581 45581 "incomplete_details": { 45582 45582 "reason": "max_tokens" 45583 45583 }, ··· 45658 45658 ResponseLogProb: 45659 45659 type: object 45660 45660 description: | 45661 - A logprob is the logarithmic probability that the model assigns to producing 45662 - a particular token at a given position in the sequence. Less-negative (higher) 45661 + A logprob is the logarithmic probability that the model assigns to producing 45662 + a particular token at a given position in the sequence. Less-negative (higher) 45663 45663 logprob values indicate greater model confidence in that token choice. 45664 45664 properties: 45665 45665 token: ··· 45994 45994 45995 45995 `["text"]` 45996 45996 45997 - The `gpt-4o-audio-preview` model can also be used to 45998 - [generate audio](https://platform.openai.com/docs/guides/audio). To request that this model generate 45997 + The `gpt-4o-audio-preview` model can also be used to 45998 + [generate audio](https://platform.openai.com/docs/guides/audio). To request that this model generate 45999 45999 both text and audio responses, you can use: 46000 46000 46001 46001 `["text", "audio"]` ··· 46946 46946 cached_tokens: 46947 46947 type: integer 46948 46948 description: | 46949 - The number of tokens that were retrieved from the cache. 46949 + The number of tokens that were retrieved from the cache. 46950 46950 [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching). 46951 46951 required: 46952 46952 - cached_tokens ··· 47120 47120 item: 47121 47121 type: object 47122 47122 description: > 47123 - The dataset item provided to the grader. This will be used to populate 47123 + The dataset item provided to the grader. This will be used to populate 47124 47124 47125 47125 the `item` namespace. See [the guide](https://platform.openai.com/docs/guides/graders) for more 47126 47126 details. 47127 47127 model_sample: 47128 47128 type: string 47129 47129 description: > 47130 - The model sample to be evaluated. This value will be used to populate 47130 + The model sample to be evaluated. This value will be used to populate 47131 47131 47132 47132 the `sample` namespace. See [the guide](https://platform.openai.com/docs/guides/graders) for more 47133 47133 details. 47134 47134 47135 - The `output_json` variable will be populated if the model sample is a 47135 + The `output_json` variable will be populated if the model sample is a 47136 47136 47137 47137 valid JSON string. 47138 47138 ··· 48425 48425 - screenshot 48426 48426 default: screenshot 48427 48427 description: | 48428 - Specifies the event type. For a screenshot action, this property is 48428 + Specifies the event type. For a screenshot action, this property is 48429 48429 always set to `screenshot`. 48430 48430 x-stainless-const: true 48431 48431 required: ··· 48442 48442 - scroll 48443 48443 default: scroll 48444 48444 description: | 48445 - Specifies the event type. For a scroll action, this property is 48445 + Specifies the event type. For a scroll action, this property is 48446 48446 always set to `scroll`. 48447 48447 x-stainless-const: true 48448 48448 x: ··· 48657 48657 description: | 48658 48658 An object specifying the format that the model must output. 48659 48659 48660 - Configuring `{ "type": "json_schema" }` enables Structured Outputs, 48661 - which ensures the model will match your supplied JSON schema. Learn more in the 48660 + Configuring `{ "type": "json_schema" }` enables Structured Outputs, 48661 + which ensures the model will match your supplied JSON schema. Learn more in the 48662 48662 [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). 48663 48663 48664 48664 The default format is `{ "type": "text" }` with no additional options. ··· 49281 49281 - type 49282 49282 default: type 49283 49283 description: | 49284 - Specifies the event type. For a type action, this property is 49284 + Specifies the event type. For a type action, this property is 49285 49285 always set to `type`. 49286 49286 x-stainless-const: true 49287 49287 text: ··· 49978 49978 type: integer 49979 49979 default: 300 49980 49980 description: | 49981 - Amount of audio to include before the VAD detected speech (in 49981 + Amount of audio to include before the VAD detected speech (in 49982 49982 milliseconds). 49983 49983 silence_duration_ms: 49984 49984 type: integer 49985 49985 default: 200 49986 49986 description: | 49987 49987 Duration of silence to detect speech stop (in milliseconds). 49988 - With shorter values the model will respond more quickly, 49988 + With shorter values the model will respond more quickly, 49989 49989 but may jump in on short pauses from the user. 49990 49990 threshold: 49991 49991 type: number 49992 49992 default: 0.5 49993 49993 description: | 49994 - Sensitivity threshold (0.0 to 1.0) for voice activity detection. A 49995 - higher threshold will require louder audio to activate the model, and 49994 + Sensitivity threshold (0.0 to 1.0) for voice activity detection. A 49995 + higher threshold will require louder audio to activate the model, and 49996 49996 thus might perform better in noisy environments. 49997 49997 ValidateGraderRequest: 49998 49998 type: object ··· 50044 50044 VectorStoreFileAttributes: 50045 50045 type: object 50046 50046 description: | 50047 - Set of 16 key-value pairs that can be attached to an object. This can be 50048 - useful for storing additional information about the object in a structured 50049 - format, and querying for objects via API or the dashboard. Keys are strings 50050 - with a maximum length of 64 characters. Values are strings with a maximum 50047 + Set of 16 key-value pairs that can be attached to an object. This can be 50048 + useful for storing additional information about the object in a structured 50049 + format, and querying for objects via API or the dashboard. Keys are strings 50050 + with a maximum length of 64 characters. Values are strings with a maximum 50051 50051 length of 512 characters, booleans, or numbers. 50052 50052 maxProperties: 16 50053 50053 propertyNames: ··· 50532 50532 - wait 50533 50533 default: wait 50534 50534 description: | 50535 - Specifies the event type. For a wait action, this property is 50535 + Specifies the event type. For a wait action, this property is 50536 50536 always set to `wait`. 50537 50537 x-stainless-const: true 50538 50538 required: ··· 50607 50607 WebSearchContextSize: 50608 50608 type: string 50609 50609 description: | 50610 - High level guidance for the amount of context window space to use for the 50610 + High level guidance for the amount of context window space to use for the 50611 50611 search. One of `low`, `medium`, or `high`. `medium` is the default. 50612 50612 enum: 50613 50613 - low ··· 50622 50622 country: 50623 50623 type: string 50624 50624 description: | 50625 - The two-letter 50625 + The two-letter 50626 50626 [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, 50627 50627 e.g. `US`. 50628 50628 region: ··· 50636 50636 timezone: 50637 50637 type: string 50638 50638 description: | 50639 - The [IANA timezone](https://timeapi.io/documentation/iana-timezones) 50639 + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) 50640 50640 of the user, e.g. `America/Los_Angeles`. 50641 50641 WebSearchToolCall: 50642 50642 type: object 50643 50643 title: Web search tool call 50644 50644 description: | 50645 - The results of a web search tool call. See the 50645 + The results of a web search tool call. See the 50646 50646 [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information. 50647 50647 properties: 50648 50648 id: ··· 52116 52116 transcript: 52117 52117 type: string 52118 52118 description: | 52119 - The transcript of the audio, used for `input_audio` and `audio` 52119 + The transcript of the audio, used for `input_audio` and `audio` 52120 52120 content types. 52121 52121 RealtimeConnectParams: 52122 52122 type: object ··· 52893 52893 title: Webhook Events 52894 52894 description: | 52895 52895 Webhooks are HTTP requests sent by OpenAI to a URL you specify when certain 52896 - events happen during the course of API usage. 52896 + events happen during the course of API usage. 52897 52897 52898 52898 [Learn more about webhooks](https://platform.openai.com/docs/guides/webhooks). 52899 52899 navigationGroup: webhooks