@vertz/codegen 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1343 @@
1
+ // src/config.ts
2
+ var VALID_GENERATORS = new Set(["typescript", "cli"]);
3
+ function defineCodegenConfig(config) {
4
+ return config;
5
+ }
6
+ function resolveCodegenConfig(config) {
7
+ return {
8
+ generators: config?.generators ?? ["typescript"],
9
+ outputDir: config?.outputDir ?? ".vertz/generated",
10
+ format: config?.format,
11
+ incremental: config?.incremental,
12
+ typescript: config?.typescript,
13
+ cli: config?.cli
14
+ };
15
+ }
16
+ function validateCodegenConfig(config) {
17
+ const errors = [];
18
+ if (config.generators.length === 0) {
19
+ errors.push("codegen.generators must contain at least one generator");
20
+ }
21
+ for (const gen of config.generators) {
22
+ if (!VALID_GENERATORS.has(gen)) {
23
+ errors.push(`codegen.generators contains unknown generator: "${gen}"`);
24
+ }
25
+ }
26
+ if (config.typescript?.publishable) {
27
+ const pub = config.typescript.publishable;
28
+ if (!pub.name) {
29
+ errors.push("codegen.typescript.publishable.name is required");
30
+ }
31
+ if (!pub.outputDir) {
32
+ errors.push("codegen.typescript.publishable.outputDir is required");
33
+ }
34
+ }
35
+ if (config.cli?.publishable) {
36
+ const pub = config.cli.publishable;
37
+ if (!pub.name) {
38
+ errors.push("codegen.cli.publishable.name is required");
39
+ }
40
+ if (!pub.outputDir) {
41
+ errors.push("codegen.cli.publishable.outputDir is required");
42
+ }
43
+ if (!pub.binName) {
44
+ errors.push("codegen.cli.publishable.binName is required");
45
+ }
46
+ }
47
+ return errors;
48
+ }
49
+ // src/format.ts
50
+ import { execFile } from "node:child_process";
51
+ import { accessSync } from "node:fs";
52
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
53
+ import { tmpdir } from "node:os";
54
+ import { dirname, join, resolve } from "node:path";
55
+ import { fileURLToPath } from "node:url";
56
+ var __filename2 = fileURLToPath(import.meta.url);
57
+ var __dirname2 = dirname(__filename2);
58
+ var BIOME_CONFIG = JSON.stringify({
59
+ $schema: "https://biomejs.dev/schemas/2.0.0/schema.json",
60
+ formatter: {
61
+ enabled: true,
62
+ indentStyle: "space",
63
+ indentWidth: 2,
64
+ lineWidth: 100
65
+ },
66
+ javascript: {
67
+ formatter: {
68
+ quoteStyle: "single",
69
+ semicolons: "always",
70
+ trailingCommas: "all"
71
+ }
72
+ },
73
+ linter: {
74
+ enabled: false
75
+ },
76
+ vcs: {
77
+ enabled: false
78
+ }
79
+ });
80
+ function findBiomeBin() {
81
+ const candidates = [
82
+ resolve(__dirname2, "..", "..", "..", "node_modules", ".bin", "biome"),
83
+ resolve(__dirname2, "..", "node_modules", ".bin", "biome"),
84
+ resolve(process.cwd(), "node_modules", ".bin", "biome")
85
+ ];
86
+ for (const candidate of candidates) {
87
+ try {
88
+ accessSync(candidate);
89
+ return candidate;
90
+ } catch {}
91
+ }
92
+ return "biome";
93
+ }
94
+ function spawnAsync(cmd, args) {
95
+ return new Promise((resolve2) => {
96
+ execFile(cmd, args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
97
+ resolve2({ stdout, stderr, exitCode: error ? 1 : 0 });
98
+ });
99
+ });
100
+ }
101
+ async function formatWithBiome(files) {
102
+ if (files.length === 0) {
103
+ return [];
104
+ }
105
+ const tempDir = join(tmpdir(), `vertz-codegen-format-${Date.now()}-${Math.random().toString(36).slice(2)}`);
106
+ try {
107
+ await mkdir(tempDir, { recursive: true });
108
+ await writeFile(join(tempDir, "biome.json"), BIOME_CONFIG, "utf-8");
109
+ for (const file of files) {
110
+ const filePath = join(tempDir, file.path);
111
+ const dir = dirname(filePath);
112
+ await mkdir(dir, { recursive: true });
113
+ await writeFile(filePath, file.content, "utf-8");
114
+ }
115
+ const biomeBin = findBiomeBin();
116
+ try {
117
+ await spawnAsync(biomeBin, ["format", "--write", `--config-path=${tempDir}`, tempDir]);
118
+ } catch {}
119
+ const formatted = [];
120
+ for (const file of files) {
121
+ const filePath = join(tempDir, file.path);
122
+ const content = await readFile(filePath, "utf-8");
123
+ formatted.push({ path: file.path, content });
124
+ }
125
+ return formatted;
126
+ } finally {
127
+ await rm(tempDir, { recursive: true, force: true }).catch(() => {});
128
+ }
129
+ }
130
+ // src/generate.ts
131
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
132
+ import { dirname as dirname3, join as join3 } from "node:path";
133
+
134
+ // src/utils/naming.ts
135
+ function splitWords(input) {
136
+ return input.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]+/).filter(Boolean);
137
+ }
138
+ function toPascalCase(input) {
139
+ return splitWords(input).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
140
+ }
141
+ function toCamelCase(input) {
142
+ const pascal = toPascalCase(input);
143
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
144
+ }
145
+ function toKebabCase(input) {
146
+ return splitWords(input).map((w) => w.toLowerCase()).join("-");
147
+ }
148
+ function toSnakeCase(input) {
149
+ return splitWords(input).map((w) => w.toLowerCase()).join("_");
150
+ }
151
+
152
+ // src/generators/typescript/emit-cli.ts
153
+ var FILE_HEADER = `// Generated by @vertz/codegen — do not edit
154
+ `;
155
+ function mapJsonSchemaTypeToCLI(type) {
156
+ if (type === "number" || type === "integer")
157
+ return "number";
158
+ if (type === "boolean")
159
+ return "boolean";
160
+ return "string";
161
+ }
162
+ function emitFieldDefinitions(schema, indent) {
163
+ const properties = schema.properties;
164
+ if (!properties)
165
+ return [];
166
+ const required = schema.required ?? [];
167
+ const lines = [];
168
+ for (const [key, propSchema] of Object.entries(properties)) {
169
+ const cliType = mapJsonSchemaTypeToCLI(propSchema.type);
170
+ const isRequired = required.includes(key);
171
+ const parts = [];
172
+ parts.push(`type: '${cliType}'`);
173
+ if (propSchema.description) {
174
+ parts.push(`description: '${propSchema.description}'`);
175
+ }
176
+ parts.push(`required: ${isRequired}`);
177
+ if (propSchema.enum) {
178
+ const enumValues = propSchema.enum.map((v) => `'${v}'`).join(", ");
179
+ parts.push(`enum: [${enumValues}]`);
180
+ }
181
+ lines.push(`${indent} ${key}: { ${parts.join(", ")} },`);
182
+ }
183
+ return lines;
184
+ }
185
+ function emitCommandDefinition(op) {
186
+ const lines = [];
187
+ lines.push("{");
188
+ lines.push(` method: '${op.method}',`);
189
+ lines.push(` path: '${op.path}',`);
190
+ lines.push(` description: '${op.description ?? op.operationId}',`);
191
+ if (op.params) {
192
+ const fields = emitFieldDefinitions(op.params, " ");
193
+ if (fields.length > 0) {
194
+ lines.push(" params: {");
195
+ lines.push(...fields);
196
+ lines.push(" },");
197
+ }
198
+ }
199
+ if (op.query) {
200
+ const fields = emitFieldDefinitions(op.query, " ");
201
+ if (fields.length > 0) {
202
+ lines.push(" query: {");
203
+ lines.push(...fields);
204
+ lines.push(" },");
205
+ }
206
+ }
207
+ if (op.body) {
208
+ const fields = emitFieldDefinitions(op.body, " ");
209
+ if (fields.length > 0) {
210
+ lines.push(" body: {");
211
+ lines.push(...fields);
212
+ lines.push(" },");
213
+ }
214
+ }
215
+ lines.push("}");
216
+ return lines.join(`
217
+ `);
218
+ }
219
+ function emitModuleCommands(module) {
220
+ if (module.operations.length === 0) {
221
+ return "{}";
222
+ }
223
+ const entries = [];
224
+ for (const op of module.operations) {
225
+ const commandName = toKebabCase(op.operationId);
226
+ const def = emitCommandDefinition(op);
227
+ const indented = def.split(`
228
+ `).map((line, i) => i === 0 ? line : ` ${line}`).join(`
229
+ `);
230
+ entries.push(` '${commandName}': ${indented},`);
231
+ }
232
+ return `{
233
+ ${entries.join(`
234
+ `)}
235
+ }`;
236
+ }
237
+ function emitManifestFile(ir) {
238
+ const sections = [FILE_HEADER];
239
+ sections.push("import type { CommandManifest } from '@vertz/cli-runtime';");
240
+ sections.push("");
241
+ if (ir.modules.length === 0) {
242
+ sections.push("export const commands: CommandManifest = {};");
243
+ } else {
244
+ const namespaceEntries = [];
245
+ for (const mod of ir.modules) {
246
+ const cmds = emitModuleCommands(mod);
247
+ const indented = cmds.split(`
248
+ `).map((line, i) => i === 0 ? line : ` ${line}`).join(`
249
+ `);
250
+ namespaceEntries.push(` ${mod.name}: ${indented},`);
251
+ }
252
+ sections.push(`export const commands: CommandManifest = {
253
+ ${namespaceEntries.join(`
254
+ `)}
255
+ };`);
256
+ }
257
+ return {
258
+ path: "cli/manifest.ts",
259
+ content: sections.join(`
260
+ `)
261
+ };
262
+ }
263
+ function emitBinEntryPoint(options) {
264
+ const lines = [];
265
+ lines.push("#!/usr/bin/env node");
266
+ lines.push(FILE_HEADER);
267
+ lines.push("import { createCLI } from '@vertz/cli-runtime';");
268
+ lines.push("import { commands } from './manifest';");
269
+ lines.push("");
270
+ lines.push("const cli = createCLI({");
271
+ lines.push(` name: '${options.cliName}',`);
272
+ lines.push(` version: '${options.cliVersion}',`);
273
+ lines.push(" commands,");
274
+ lines.push("});");
275
+ lines.push("");
276
+ lines.push("cli.run(process.argv.slice(2));");
277
+ return {
278
+ path: "cli/bin.ts",
279
+ content: lines.join(`
280
+ `)
281
+ };
282
+ }
283
+ function scaffoldCLIPackageJson(options) {
284
+ const pkg = {
285
+ name: options.packageName,
286
+ version: options.packageVersion ?? "0.0.0",
287
+ description: `CLI for ${options.cliName} — generated by @vertz/codegen`,
288
+ private: true,
289
+ bin: {
290
+ [options.cliName]: "./cli/bin.ts"
291
+ },
292
+ dependencies: {
293
+ "@vertz/cli-runtime": "*",
294
+ "@vertz/fetch": "*"
295
+ }
296
+ };
297
+ return {
298
+ path: "package.json",
299
+ content: JSON.stringify(pkg, null, 2)
300
+ };
301
+ }
302
+ function scaffoldCLIRootIndex() {
303
+ const lines = [FILE_HEADER];
304
+ lines.push("export { commands } from './cli/manifest';");
305
+ return {
306
+ path: "index.ts",
307
+ content: lines.join(`
308
+ `)
309
+ };
310
+ }
311
+
312
+ // src/utils/imports.ts
313
+ function mergeImports(imports) {
314
+ const seen = new Map;
315
+ for (const imp of imports) {
316
+ const key = `${imp.from}::${imp.name}::${imp.isType}::${imp.alias ?? ""}`;
317
+ if (!seen.has(key)) {
318
+ seen.set(key, imp);
319
+ }
320
+ }
321
+ return [...seen.values()].sort((a, b) => {
322
+ const fromCmp = a.from.localeCompare(b.from);
323
+ if (fromCmp !== 0)
324
+ return fromCmp;
325
+ return a.name.localeCompare(b.name);
326
+ });
327
+ }
328
+ function renderImports(imports) {
329
+ const grouped = new Map;
330
+ for (const imp of imports) {
331
+ let group = grouped.get(imp.from);
332
+ if (!group) {
333
+ group = { types: [], values: [] };
334
+ grouped.set(imp.from, group);
335
+ }
336
+ const nameStr = imp.alias ? `${imp.name} as ${imp.alias}` : imp.name;
337
+ if (imp.isType) {
338
+ group.types.push(nameStr);
339
+ } else {
340
+ group.values.push(nameStr);
341
+ }
342
+ }
343
+ const lines = [];
344
+ for (const [from, group] of grouped) {
345
+ if (group.types.length > 0) {
346
+ lines.push(`import type { ${group.types.join(", ")} } from '${from}';`);
347
+ }
348
+ if (group.values.length > 0) {
349
+ lines.push(`import { ${group.values.join(", ")} } from '${from}';`);
350
+ }
351
+ }
352
+ return lines.join(`
353
+ `);
354
+ }
355
+
356
+ // src/generators/typescript/emit-client.ts
357
+ var FILE_HEADER2 = `// Generated by @vertz/codegen — do not edit
358
+ `;
359
+ function emitSDKConfig(auth) {
360
+ const imports = [{ from: "@vertz/fetch", name: "FetchClientConfig", isType: true }];
361
+ const fields = [];
362
+ for (const scheme of auth.schemes) {
363
+ if (scheme.type === "bearer") {
364
+ fields.push(` /** Bearer token or function returning a token. */
365
+ token?: string | (() => string | Promise<string>);`);
366
+ } else if (scheme.type === "apiKey") {
367
+ fields.push(` /** API key or function returning a key. */
368
+ apiKey?: string | (() => string | Promise<string>);`);
369
+ }
370
+ }
371
+ if (fields.length === 0) {
372
+ return {
373
+ content: "export interface SDKConfig extends FetchClientConfig {}",
374
+ imports
375
+ };
376
+ }
377
+ const content = `export interface SDKConfig extends FetchClientConfig {
378
+ ${fields.join(`
379
+ `)}
380
+ }`;
381
+ return { content, imports };
382
+ }
383
+ function emitAuthStrategyBuilder(auth) {
384
+ const imports = [{ from: "@vertz/fetch", name: "AuthStrategy", isType: true }];
385
+ const lines = [];
386
+ lines.push("const authStrategies: AuthStrategy[] = [...(config.authStrategies ?? [])];");
387
+ for (const scheme of auth.schemes) {
388
+ if (scheme.type === "bearer") {
389
+ lines.push(`if (config.token) {
390
+ authStrategies.push({ type: 'bearer', token: config.token });
391
+ }`);
392
+ } else if (scheme.type === "apiKey") {
393
+ lines.push(`if (config.apiKey) {
394
+ authStrategies.push({ type: 'apiKey', key: config.apiKey, location: '${scheme.in}', name: '${scheme.paramName}' });
395
+ }`);
396
+ }
397
+ }
398
+ return { content: lines.join(`
399
+ `), imports };
400
+ }
401
+ function buildPathExpression(path) {
402
+ if (!path.includes(":")) {
403
+ return `'${path}'`;
404
+ }
405
+ const interpolated = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "${input.params.$1}");
406
+ return `\`${interpolated}\``;
407
+ }
408
+ function buildRequestOptions(op) {
409
+ const opts = [];
410
+ if (op.query) {
411
+ opts.push("query: input?.query");
412
+ }
413
+ if (op.body) {
414
+ opts.push("body: input.body");
415
+ }
416
+ if (op.headers) {
417
+ opts.push("headers: input?.headers");
418
+ }
419
+ if (opts.length === 0) {
420
+ return "";
421
+ }
422
+ return `, { ${opts.join(", ")} }`;
423
+ }
424
+ function hasOperationInput(op) {
425
+ return !!(op.params || op.query || op.body || op.headers);
426
+ }
427
+ function isInputOptional(op) {
428
+ return !op.params && !op.body;
429
+ }
430
+ function emitOperationMethod(op) {
431
+ const imports = [];
432
+ const methodName = toCamelCase(op.operationId);
433
+ const inputTypeName = `${toPascalCase(op.operationId)}Input`;
434
+ const responseTypeName = `${toPascalCase(op.operationId)}Response`;
435
+ const hasInput = hasOperationInput(op);
436
+ if (hasInput) {
437
+ const optional = isInputOptional(op);
438
+ const inputParam = optional ? `input?: ${inputTypeName}` : `input: ${inputTypeName}`;
439
+ imports.push({ from: "../types", name: inputTypeName, isType: true });
440
+ imports.push({ from: "../types", name: responseTypeName, isType: true });
441
+ const pathExpr2 = buildPathExpression(op.path);
442
+ const reqOpts = buildRequestOptions(op);
443
+ const content2 = `${methodName}(${inputParam}): Promise<SDKResult<${responseTypeName}>> {
444
+ return client.request('${op.method}', ${pathExpr2}${reqOpts});
445
+ }`;
446
+ return { content: content2, imports };
447
+ }
448
+ imports.push({ from: "../types", name: responseTypeName, isType: true });
449
+ const pathExpr = buildPathExpression(op.path);
450
+ const content = `${methodName}(): Promise<SDKResult<${responseTypeName}>> {
451
+ return client.request('${op.method}', ${pathExpr});
452
+ }`;
453
+ return { content, imports };
454
+ }
455
+ function emitStreamingMethod(op) {
456
+ const imports = [];
457
+ const methodName = toCamelCase(op.operationId);
458
+ const inputTypeName = `${toPascalCase(op.operationId)}Input`;
459
+ const eventTypeName = `${toPascalCase(op.operationId)}Event`;
460
+ const format = op.streaming?.format ?? "sse";
461
+ imports.push({ from: "../types", name: eventTypeName, isType: true });
462
+ const hasInput = hasOperationInput(op);
463
+ const pathExpr = buildPathExpression(op.path);
464
+ const streamOpts = [];
465
+ streamOpts.push(`method: '${op.method}'`);
466
+ streamOpts.push(`path: ${pathExpr}`);
467
+ if (op.query) {
468
+ streamOpts.push("query: input?.query");
469
+ }
470
+ streamOpts.push(`format: '${format}'`);
471
+ const optsStr = streamOpts.join(`,
472
+ `);
473
+ if (hasInput) {
474
+ const optional = isInputOptional(op);
475
+ const inputParam = optional ? `input?: ${inputTypeName}` : `input: ${inputTypeName}`;
476
+ imports.push({ from: "../types", name: inputTypeName, isType: true });
477
+ const content2 = `async *${methodName}(${inputParam}): AsyncGenerator<${eventTypeName}> {
478
+ yield* client.requestStream<${eventTypeName}>({
479
+ ${optsStr},
480
+ });
481
+ }`;
482
+ return { content: content2, imports };
483
+ }
484
+ const content = `async *${methodName}(): AsyncGenerator<${eventTypeName}> {
485
+ yield* client.requestStream<${eventTypeName}>({
486
+ ${optsStr},
487
+ });
488
+ }`;
489
+ return { content, imports };
490
+ }
491
+ function emitModuleFile(module) {
492
+ const fragments = [];
493
+ for (const op of module.operations) {
494
+ if (op.streaming) {
495
+ fragments.push(emitStreamingMethod(op));
496
+ } else {
497
+ fragments.push(emitOperationMethod(op));
498
+ }
499
+ }
500
+ const allImports = mergeImports(fragments.flatMap((f) => f.imports));
501
+ const importBlock = renderImports(allImports);
502
+ const factoryName = `create${toPascalCase(module.name)}Module`;
503
+ const methods = fragments.map((f) => ` ${f.content}`).join(`,
504
+ `);
505
+ const sections = [FILE_HEADER2];
506
+ if (importBlock) {
507
+ sections.push(importBlock);
508
+ sections.push("");
509
+ }
510
+ sections.push(`export function ${factoryName}(client: FetchClient) {
511
+ return {
512
+ ${methods},
513
+ };
514
+ }`);
515
+ const fetchClientImport = "import { FetchClient } from '@vertz/fetch';";
516
+ const content = sections.join(`
517
+ `);
518
+ const withFetchImport = content.replace(FILE_HEADER2, `${FILE_HEADER2}
519
+ ${fetchClientImport}
520
+ `);
521
+ return {
522
+ path: `modules/${module.name}.ts`,
523
+ content: withFetchImport
524
+ };
525
+ }
526
+ function emitClientFile(ir) {
527
+ const sections = [FILE_HEADER2];
528
+ const imports = [];
529
+ imports.push({ from: "@vertz/fetch", name: "FetchClient", isType: false });
530
+ const configFragment = emitSDKConfig(ir.auth);
531
+ imports.push(...configFragment.imports);
532
+ const authFragment = emitAuthStrategyBuilder(ir.auth);
533
+ imports.push(...authFragment.imports);
534
+ for (const mod of ir.modules) {
535
+ const factoryName = `create${toPascalCase(mod.name)}Module`;
536
+ imports.push({ from: `./modules/${mod.name}`, name: factoryName, isType: false });
537
+ }
538
+ const allImports = mergeImports(imports);
539
+ const importBlock = renderImports(allImports);
540
+ if (importBlock) {
541
+ sections.push(importBlock);
542
+ sections.push("");
543
+ }
544
+ sections.push(`export interface SDKResult<T> {
545
+ data: T;
546
+ status: number;
547
+ headers: Headers;
548
+ }`);
549
+ sections.push("");
550
+ sections.push(configFragment.content);
551
+ sections.push("");
552
+ const moduleEntries = ir.modules.map((mod) => ` ${toCamelCase(mod.name)}: create${toPascalCase(mod.name)}Module(client)`).join(`,
553
+ `);
554
+ const createClientBody = [
555
+ "export function createClient(config: SDKConfig) {",
556
+ ` ${authFragment.content.split(`
557
+ `).join(`
558
+ `)}`,
559
+ "",
560
+ " const client = new FetchClient({",
561
+ " ...config,",
562
+ " authStrategies,",
563
+ " });",
564
+ "",
565
+ " return {",
566
+ moduleEntries,
567
+ " };",
568
+ "}"
569
+ ].join(`
570
+ `);
571
+ sections.push(createClientBody);
572
+ return {
573
+ path: "client.ts",
574
+ content: sections.join(`
575
+ `)
576
+ };
577
+ }
578
+
579
+ // src/generators/typescript/emit-sdk.ts
580
+ var FILE_HEADER3 = `// Generated by @vertz/codegen — do not edit
581
+ `;
582
+ function jsonSchemaToSchemaCall(schema) {
583
+ const type = schema.type;
584
+ if (type === "string")
585
+ return "s.string()";
586
+ if (type === "number")
587
+ return "s.number()";
588
+ if (type === "integer")
589
+ return "s.int()";
590
+ if (type === "boolean")
591
+ return "s.boolean()";
592
+ if (type === "array") {
593
+ const items = schema.items;
594
+ if (items) {
595
+ return `s.array(${jsonSchemaToSchemaCall(items)})`;
596
+ }
597
+ return "s.array(s.unknown())";
598
+ }
599
+ if (type === "object") {
600
+ const properties = schema.properties;
601
+ if (!properties || Object.keys(properties).length === 0) {
602
+ return "s.object({})";
603
+ }
604
+ const required = schema.required ?? [];
605
+ const fields = Object.entries(properties).map(([key, propSchema]) => {
606
+ const call = jsonSchemaToSchemaCall(propSchema);
607
+ if (required.includes(key)) {
608
+ return ` ${key}: ${call}`;
609
+ }
610
+ return ` ${key}: ${call}.optional()`;
611
+ });
612
+ return `s.object({
613
+ ${fields.join(`,
614
+ `)},
615
+ })`;
616
+ }
617
+ return "s.unknown()";
618
+ }
619
+ function emitSchemaReExports(schemas) {
620
+ const sections = [FILE_HEADER3];
621
+ if (schemas.length > 0) {
622
+ sections.push("import { s } from '@vertz/schema';");
623
+ sections.push("");
624
+ }
625
+ for (const schema of schemas) {
626
+ const validatorName = `${schema.name}Schema`;
627
+ const schemaCall = jsonSchemaToSchemaCall(schema.jsonSchema);
628
+ sections.push(`export const ${validatorName} = ${schemaCall};`);
629
+ }
630
+ return {
631
+ path: "schemas.ts",
632
+ content: sections.join(`
633
+ `)
634
+ };
635
+ }
636
+ function emitBarrelIndex(ir) {
637
+ const lines = [FILE_HEADER3];
638
+ lines.push("export { createClient } from './client';");
639
+ lines.push("export type { SDKConfig, SDKResult } from './client';");
640
+ for (const mod of ir.modules) {
641
+ lines.push(`export * from './types/${mod.name}';`);
642
+ }
643
+ if (ir.schemas.length > 0) {
644
+ lines.push("export * from './types/shared';");
645
+ }
646
+ if (ir.schemas.length > 0) {
647
+ lines.push("export * from './schemas';");
648
+ }
649
+ return {
650
+ path: "index.ts",
651
+ content: lines.join(`
652
+ `)
653
+ };
654
+ }
655
+ function emitPackageJson(ir, options) {
656
+ const dependencies = {
657
+ "@vertz/fetch": "*"
658
+ };
659
+ if (ir.schemas.length > 0) {
660
+ dependencies["@vertz/schema"] = "*";
661
+ }
662
+ const pkg = {
663
+ name: options.packageName,
664
+ version: options.packageVersion ?? "0.0.0",
665
+ description: "Generated by @vertz/codegen",
666
+ private: true,
667
+ main: "index.ts",
668
+ types: "index.ts",
669
+ dependencies
670
+ };
671
+ return {
672
+ path: "package.json",
673
+ content: JSON.stringify(pkg, null, 2)
674
+ };
675
+ }
676
+
677
+ // src/json-schema-converter.ts
678
+ var PRIMITIVE_MAP = {
679
+ string: "string",
680
+ number: "number",
681
+ integer: "number",
682
+ boolean: "boolean",
683
+ null: "null"
684
+ };
685
+ function jsonSchemaToTS(schema, ctx) {
686
+ const context = ctx ?? {
687
+ namedTypes: new Map,
688
+ resolving: new Set
689
+ };
690
+ const type = convert(schema, context);
691
+ return { type, extractedTypes: context.namedTypes };
692
+ }
693
+ function convert(schema, _ctx) {
694
+ if (schema.$defs && typeof schema.$defs === "object") {
695
+ const defs = schema.$defs;
696
+ for (const [name, defSchema] of Object.entries(defs)) {
697
+ if (!_ctx.namedTypes.has(name)) {
698
+ _ctx.resolving.add(name);
699
+ const typeStr = convert(defSchema, _ctx);
700
+ _ctx.resolving.delete(name);
701
+ _ctx.namedTypes.set(name, typeStr);
702
+ }
703
+ }
704
+ }
705
+ if (typeof schema.$ref === "string") {
706
+ if (!schema.$ref.startsWith("#")) {
707
+ throw new Error(`External $ref is not supported: ${schema.$ref}`);
708
+ }
709
+ return refToName(schema.$ref);
710
+ }
711
+ if (schema.const !== undefined) {
712
+ return toLiteral(schema.const);
713
+ }
714
+ if (Array.isArray(schema.enum)) {
715
+ return schema.enum.map((v) => toLiteral(v)).join(" | ");
716
+ }
717
+ if (Array.isArray(schema.oneOf)) {
718
+ return schema.oneOf.map((s) => convert(s, _ctx)).join(" | ");
719
+ }
720
+ if (Array.isArray(schema.anyOf)) {
721
+ return schema.anyOf.map((s) => convert(s, _ctx)).join(" | ");
722
+ }
723
+ if (Array.isArray(schema.allOf)) {
724
+ return schema.allOf.map((s) => convert(s, _ctx)).join(" & ");
725
+ }
726
+ if (Array.isArray(schema.type)) {
727
+ return schema.type.map((t) => PRIMITIVE_MAP[t] ?? t).join(" | ");
728
+ }
729
+ if (typeof schema.type === "string") {
730
+ const type = schema.type;
731
+ if (type === "array") {
732
+ if (Array.isArray(schema.prefixItems)) {
733
+ const items = schema.prefixItems.map((s) => convert(s, _ctx));
734
+ return `[${items.join(", ")}]`;
735
+ }
736
+ if (schema.items && typeof schema.items === "object") {
737
+ const itemType = convert(schema.items, _ctx);
738
+ return itemType.includes(" | ") ? `(${itemType})[]` : `${itemType}[]`;
739
+ }
740
+ return "unknown[]";
741
+ }
742
+ if (type === "object") {
743
+ if (schema.additionalProperties && typeof schema.additionalProperties === "object" && !schema.properties) {
744
+ const valueType = convert(schema.additionalProperties, _ctx);
745
+ return `Record<string, ${valueType}>`;
746
+ }
747
+ if (schema.properties && typeof schema.properties === "object") {
748
+ const props = schema.properties;
749
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
750
+ const parts = [];
751
+ for (const [key, propSchema] of Object.entries(props)) {
752
+ const propType = convert(propSchema, _ctx);
753
+ const optional = required.has(key) ? "" : "?";
754
+ parts.push(`${key}${optional}: ${propType}`);
755
+ }
756
+ return `{ ${parts.join("; ")} }`;
757
+ }
758
+ return "Record<string, unknown>";
759
+ }
760
+ return PRIMITIVE_MAP[type] ?? "unknown";
761
+ }
762
+ return "unknown";
763
+ }
764
+ function refToName(ref) {
765
+ const segments = ref.split("/");
766
+ return segments[segments.length - 1] ?? "unknown";
767
+ }
768
+ function toLiteral(value) {
769
+ if (typeof value === "string")
770
+ return `'${value}'`;
771
+ if (typeof value === "number" || typeof value === "boolean")
772
+ return String(value);
773
+ if (value === null)
774
+ return "null";
775
+ return "unknown";
776
+ }
777
+
778
+ // src/generators/typescript/emit-types.ts
779
+ var FILE_HEADER4 = `// Generated by @vertz/codegen — do not edit
780
+ `;
781
+ function emitTypeDeclaration(name, tsType) {
782
+ if (tsType.startsWith("{")) {
783
+ return `export interface ${name} ${tsType}`;
784
+ }
785
+ return `export type ${name} = ${tsType};`;
786
+ }
787
+ function emitInterfaceFromSchema(schema) {
788
+ const result = jsonSchemaToTS(schema.jsonSchema);
789
+ const lines = [];
790
+ for (const [name, tsType] of result.extractedTypes) {
791
+ lines.push(emitTypeDeclaration(name, tsType));
792
+ lines.push("");
793
+ }
794
+ const jsdocParts = [];
795
+ if (schema.annotations.description) {
796
+ jsdocParts.push(schema.annotations.description);
797
+ }
798
+ if (schema.annotations.deprecated) {
799
+ jsdocParts.push("@deprecated");
800
+ }
801
+ if (jsdocParts.length > 0) {
802
+ lines.push(`/** ${jsdocParts.join(`
803
+ * `)} */`);
804
+ }
805
+ lines.push(emitTypeDeclaration(schema.name, result.type));
806
+ return { content: lines.join(`
807
+ `), imports: [] };
808
+ }
809
+ function emitOperationInputType(op) {
810
+ const typeName = `${toPascalCase(op.operationId)}Input`;
811
+ const imports = [];
812
+ const slots = [];
813
+ if (op.params) {
814
+ const ref = op.schemaRefs.params;
815
+ if (ref) {
816
+ slots.push(`params: ${ref}`);
817
+ imports.push({ from: "", name: ref, isType: true });
818
+ } else {
819
+ const result = jsonSchemaToTS(op.params);
820
+ slots.push(`params: ${result.type}`);
821
+ }
822
+ }
823
+ if (op.query) {
824
+ const ref = op.schemaRefs.query;
825
+ if (ref) {
826
+ slots.push(`query?: ${ref}`);
827
+ imports.push({ from: "", name: ref, isType: true });
828
+ } else {
829
+ const result = jsonSchemaToTS(op.query);
830
+ slots.push(`query?: ${result.type}`);
831
+ }
832
+ }
833
+ if (op.body) {
834
+ const ref = op.schemaRefs.body;
835
+ if (ref) {
836
+ slots.push(`body: ${ref}`);
837
+ imports.push({ from: "", name: ref, isType: true });
838
+ } else {
839
+ const result = jsonSchemaToTS(op.body);
840
+ slots.push(`body: ${result.type}`);
841
+ }
842
+ }
843
+ if (op.headers) {
844
+ const ref = op.schemaRefs.headers;
845
+ if (ref) {
846
+ slots.push(`headers?: ${ref}`);
847
+ imports.push({ from: "", name: ref, isType: true });
848
+ } else {
849
+ const result = jsonSchemaToTS(op.headers);
850
+ slots.push(`headers?: ${result.type}`);
851
+ }
852
+ }
853
+ if (slots.length === 0) {
854
+ return { content: "", imports: [] };
855
+ }
856
+ const lines = [];
857
+ lines.push(`/** Input for ${op.operationId} */`);
858
+ lines.push(`export interface ${typeName} { ${slots.join("; ")} }`);
859
+ return { content: lines.join(`
860
+ `), imports };
861
+ }
862
+ function emitOperationResponseType(op) {
863
+ const typeName = `${toPascalCase(op.operationId)}Response`;
864
+ const imports = [];
865
+ if (!op.response) {
866
+ return {
867
+ content: `export type ${typeName} = void;`,
868
+ imports: []
869
+ };
870
+ }
871
+ const ref = op.schemaRefs.response;
872
+ if (ref) {
873
+ imports.push({ from: "", name: ref, isType: true });
874
+ return {
875
+ content: `export type ${typeName} = ${ref};`,
876
+ imports
877
+ };
878
+ }
879
+ const result = jsonSchemaToTS(op.response);
880
+ return {
881
+ content: emitTypeDeclaration(typeName, result.type),
882
+ imports: []
883
+ };
884
+ }
885
+ function emitStreamingEventType(op) {
886
+ if (!op.streaming) {
887
+ return { content: "", imports: [] };
888
+ }
889
+ const typeName = `${toPascalCase(op.operationId)}Event`;
890
+ if (!op.streaming.eventSchema) {
891
+ return {
892
+ content: `export type ${typeName} = unknown;`,
893
+ imports: []
894
+ };
895
+ }
896
+ const result = jsonSchemaToTS(op.streaming.eventSchema);
897
+ return {
898
+ content: emitTypeDeclaration(typeName, result.type),
899
+ imports: []
900
+ };
901
+ }
902
+ function emitModuleTypesFile(module, schemas) {
903
+ const sections = [FILE_HEADER4];
904
+ for (const schema of schemas) {
905
+ const fragment = emitInterfaceFromSchema(schema);
906
+ if (fragment.content) {
907
+ sections.push(fragment.content);
908
+ }
909
+ }
910
+ for (const op of module.operations) {
911
+ const inputFragment = emitOperationInputType(op);
912
+ if (inputFragment.content) {
913
+ sections.push(inputFragment.content);
914
+ }
915
+ const responseFragment = emitOperationResponseType(op);
916
+ if (responseFragment.content) {
917
+ sections.push(responseFragment.content);
918
+ }
919
+ const streamingFragment = emitStreamingEventType(op);
920
+ if (streamingFragment.content) {
921
+ sections.push(streamingFragment.content);
922
+ }
923
+ }
924
+ return {
925
+ path: `types/${module.name}.ts`,
926
+ content: sections.join(`
927
+
928
+ `)
929
+ };
930
+ }
931
+ function emitSharedTypesFile(schemas) {
932
+ const sections = [FILE_HEADER4];
933
+ for (const schema of schemas) {
934
+ const fragment = emitInterfaceFromSchema(schema);
935
+ if (fragment.content) {
936
+ sections.push(fragment.content);
937
+ }
938
+ }
939
+ return {
940
+ path: "types/shared.ts",
941
+ content: sections.join(`
942
+
943
+ `)
944
+ };
945
+ }
946
+
947
+ // src/generators/typescript/emit-routes.ts
948
+ var FILE_HEADER5 = `// Generated by @vertz/codegen — do not edit
949
+ `;
950
+ function paramsToTS(op) {
951
+ if (!op.params) {
952
+ return "Record<string, never>";
953
+ }
954
+ const ref = op.schemaRefs.params;
955
+ if (ref) {
956
+ return ref;
957
+ }
958
+ const result = jsonSchemaToTS(op.params);
959
+ return result.type;
960
+ }
961
+ function queryToTS(op) {
962
+ if (!op.query) {
963
+ return "Record<string, never>";
964
+ }
965
+ const ref = op.schemaRefs.query;
966
+ if (ref) {
967
+ return ref;
968
+ }
969
+ const result = jsonSchemaToTS(op.query);
970
+ return result.type;
971
+ }
972
+ function bodyToTS(op) {
973
+ if (!op.body) {
974
+ return "never";
975
+ }
976
+ const ref = op.schemaRefs.body;
977
+ if (ref) {
978
+ return ref;
979
+ }
980
+ const result = jsonSchemaToTS(op.body);
981
+ return result.type;
982
+ }
983
+ function headersToTS(op) {
984
+ if (!op.headers) {
985
+ return "Record<string, never>";
986
+ }
987
+ const ref = op.schemaRefs.headers;
988
+ if (ref) {
989
+ return ref;
990
+ }
991
+ const result = jsonSchemaToTS(op.headers);
992
+ return result.type;
993
+ }
994
+ function responseToTS(op) {
995
+ if (!op.response) {
996
+ return "void";
997
+ }
998
+ const ref = op.schemaRefs.response;
999
+ if (ref) {
1000
+ return ref;
1001
+ }
1002
+ const result = jsonSchemaToTS(op.response);
1003
+ return result.type;
1004
+ }
1005
+ function emitRouteEntry(op) {
1006
+ const routeKey = `${op.method} ${op.path}`;
1007
+ const params = paramsToTS(op);
1008
+ const query = queryToTS(op);
1009
+ const body = bodyToTS(op);
1010
+ const headers = headersToTS(op);
1011
+ const response = responseToTS(op);
1012
+ return ` '${routeKey}': { params: ${params}; query: ${query}; body: ${body}; headers: ${headers}; response: ${response} };`;
1013
+ }
1014
+ function emitRouteMapType(ir) {
1015
+ const sections = [FILE_HEADER5];
1016
+ sections.push("export interface AppRouteMap {");
1017
+ const operations = ir.modules.flatMap((mod) => mod.operations);
1018
+ if (operations.length === 0) {
1019
+ sections.push(" [key: string]: never;");
1020
+ } else {
1021
+ const entries = operations.map((op) => emitRouteEntry(op));
1022
+ sections.push(entries.join(`
1023
+ `));
1024
+ }
1025
+ sections.push("}");
1026
+ return {
1027
+ path: "types/routes.ts",
1028
+ content: sections.join(`
1029
+ `)
1030
+ };
1031
+ }
1032
+
1033
+ // src/incremental.ts
1034
+ import { mkdir as mkdir2, readdir, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
1035
+ import { dirname as dirname2, join as join2, relative } from "node:path";
1036
+
1037
+ // src/hasher.ts
1038
+ import { createHash } from "node:crypto";
1039
+ function hashContent(content) {
1040
+ return createHash("sha256").update(content, "utf-8").digest("hex");
1041
+ }
1042
+
1043
+ // src/incremental.ts
1044
+ async function collectFiles(dir, baseDir) {
1045
+ const results = [];
1046
+ let entries;
1047
+ try {
1048
+ entries = await readdir(dir, { withFileTypes: true });
1049
+ } catch {
1050
+ return results;
1051
+ }
1052
+ for (const entry of entries) {
1053
+ const fullPath = join2(dir, entry.name);
1054
+ if (entry.isDirectory()) {
1055
+ results.push(...await collectFiles(fullPath, baseDir));
1056
+ } else if (entry.isFile()) {
1057
+ results.push(relative(baseDir, fullPath));
1058
+ }
1059
+ }
1060
+ return results;
1061
+ }
1062
+ async function writeIncremental(files, outputDir, options) {
1063
+ const written = [];
1064
+ const skipped = [];
1065
+ const removed = [];
1066
+ await mkdir2(outputDir, { recursive: true });
1067
+ const generatedPaths = new Set(files.map((f) => f.path));
1068
+ for (const file of files) {
1069
+ const filePath = join2(outputDir, file.path);
1070
+ const dir = dirname2(filePath);
1071
+ await mkdir2(dir, { recursive: true });
1072
+ let existingContent;
1073
+ try {
1074
+ existingContent = await readFile2(filePath, "utf-8");
1075
+ } catch {}
1076
+ if (existingContent !== undefined && hashContent(existingContent) === hashContent(file.content)) {
1077
+ skipped.push(file.path);
1078
+ } else {
1079
+ await writeFile2(filePath, file.content, "utf-8");
1080
+ written.push(file.path);
1081
+ }
1082
+ }
1083
+ if (options?.clean) {
1084
+ const existingFiles = await collectFiles(outputDir, outputDir);
1085
+ for (const existing of existingFiles) {
1086
+ if (!generatedPaths.has(existing)) {
1087
+ await rm2(join2(outputDir, existing), { force: true });
1088
+ removed.push(existing);
1089
+ }
1090
+ }
1091
+ }
1092
+ return { written, skipped, removed };
1093
+ }
1094
+
1095
+ // src/ir-adapter.ts
1096
+ function adaptIR(appIR) {
1097
+ const rawSchemas = appIR.schemas.filter((s) => s.isNamed && s.jsonSchema);
1098
+ const nameCount = new Map;
1099
+ for (const s of rawSchemas) {
1100
+ nameCount.set(s.name, (nameCount.get(s.name) ?? 0) + 1);
1101
+ }
1102
+ const renameMap = new Map;
1103
+ const schemas = rawSchemas.map((s) => {
1104
+ const isCollision = (nameCount.get(s.name) ?? 0) > 1;
1105
+ const resolvedName = isCollision ? `${toPascalCase(s.moduleName)}${s.name}` : s.name;
1106
+ renameMap.set(`${s.moduleName}:${s.name}`, resolvedName);
1107
+ return {
1108
+ name: resolvedName,
1109
+ jsonSchema: s.jsonSchema,
1110
+ annotations: {
1111
+ namingParts: {
1112
+ operation: s.namingConvention.operation,
1113
+ entity: s.namingConvention.entity,
1114
+ part: s.namingConvention.part
1115
+ }
1116
+ }
1117
+ };
1118
+ });
1119
+ const modules = appIR.modules.map((mod) => ({
1120
+ name: mod.name,
1121
+ operations: mod.routers.flatMap((router) => router.routes.map((route) => {
1122
+ const resolveRef = (ref) => {
1123
+ if (!ref || ref.kind !== "named")
1124
+ return;
1125
+ return renameMap.get(`${mod.name}:${ref.schemaName}`) ?? ref.schemaName;
1126
+ };
1127
+ const schemaRefs = {
1128
+ params: resolveRef(route.params),
1129
+ query: resolveRef(route.query),
1130
+ body: resolveRef(route.body),
1131
+ headers: resolveRef(route.headers),
1132
+ response: resolveRef(route.response)
1133
+ };
1134
+ return {
1135
+ operationId: route.operationId,
1136
+ method: route.method,
1137
+ path: route.fullPath,
1138
+ description: route.description,
1139
+ tags: route.tags,
1140
+ params: route.params?.jsonSchema,
1141
+ query: route.query?.jsonSchema,
1142
+ body: route.body?.jsonSchema,
1143
+ headers: route.headers?.jsonSchema,
1144
+ response: route.response?.jsonSchema,
1145
+ schemaRefs
1146
+ };
1147
+ }))
1148
+ }));
1149
+ const slotNames = {
1150
+ params: "Params",
1151
+ query: "Query",
1152
+ body: "Body",
1153
+ headers: "Headers",
1154
+ response: "Response"
1155
+ };
1156
+ const inlineSchemas = [];
1157
+ for (const mod of appIR.modules) {
1158
+ for (const router of mod.routers) {
1159
+ for (const route of router.routes) {
1160
+ for (const [slot, suffix] of Object.entries(slotNames)) {
1161
+ const ref = route[slot];
1162
+ if (ref && typeof ref === "object" && "kind" in ref && ref.kind === "inline" && ref.jsonSchema) {
1163
+ const name = `${toPascalCase(route.operationId)}${suffix}`;
1164
+ inlineSchemas.push({
1165
+ name,
1166
+ jsonSchema: ref.jsonSchema,
1167
+ annotations: { namingParts: {} }
1168
+ });
1169
+ }
1170
+ }
1171
+ }
1172
+ }
1173
+ }
1174
+ const allSchemas = [...schemas, ...inlineSchemas].sort((a, b) => a.name.localeCompare(b.name));
1175
+ const sortedModules = modules.map((m) => ({
1176
+ ...m,
1177
+ operations: [...m.operations].sort((a, b) => a.operationId.localeCompare(b.operationId))
1178
+ })).sort((a, b) => a.name.localeCompare(b.name));
1179
+ return {
1180
+ basePath: appIR.app.basePath,
1181
+ version: appIR.app.version,
1182
+ modules: sortedModules,
1183
+ schemas: allSchemas,
1184
+ auth: { schemes: [] }
1185
+ };
1186
+ }
1187
+
1188
+ // src/generate.ts
1189
+ function runTypescriptGenerator(ir, config) {
1190
+ const files = [];
1191
+ const moduleSchemaNames = new Set;
1192
+ for (const mod of ir.modules) {
1193
+ for (const op of mod.operations) {
1194
+ for (const ref of Object.values(op.schemaRefs)) {
1195
+ if (ref)
1196
+ moduleSchemaNames.add(ref);
1197
+ }
1198
+ }
1199
+ }
1200
+ const sharedSchemas = ir.schemas.filter((s) => !moduleSchemaNames.has(s.name));
1201
+ for (const mod of ir.modules) {
1202
+ const moduleRefNames = new Set;
1203
+ for (const op of mod.operations) {
1204
+ for (const ref of Object.values(op.schemaRefs)) {
1205
+ if (ref)
1206
+ moduleRefNames.add(ref);
1207
+ }
1208
+ }
1209
+ const moduleSchemas = ir.schemas.filter((s) => moduleRefNames.has(s.name));
1210
+ files.push(emitModuleTypesFile(mod, moduleSchemas));
1211
+ }
1212
+ if (sharedSchemas.length > 0) {
1213
+ files.push(emitSharedTypesFile(sharedSchemas));
1214
+ }
1215
+ files.push(emitRouteMapType(ir));
1216
+ for (const mod of ir.modules) {
1217
+ files.push(emitModuleFile(mod));
1218
+ }
1219
+ files.push(emitClientFile(ir));
1220
+ if (ir.schemas.length > 0) {
1221
+ files.push(emitSchemaReExports(ir.schemas));
1222
+ }
1223
+ files.push(emitBarrelIndex(ir));
1224
+ if (config.typescript?.publishable) {
1225
+ files.push(emitPackageJson(ir, {
1226
+ packageName: config.typescript.publishable.name,
1227
+ packageVersion: config.typescript.publishable.version
1228
+ }));
1229
+ }
1230
+ return files;
1231
+ }
1232
+ function runCLIGenerator(ir) {
1233
+ const files = [];
1234
+ files.push(emitManifestFile(ir));
1235
+ return files;
1236
+ }
1237
+ function generateSync(ir, config) {
1238
+ const files = [];
1239
+ const generators = [];
1240
+ for (const gen of config.generators) {
1241
+ if (gen === "typescript") {
1242
+ generators.push("typescript");
1243
+ files.push(...runTypescriptGenerator(ir, config));
1244
+ } else if (gen === "cli") {
1245
+ generators.push("cli");
1246
+ files.push(...runCLIGenerator(ir));
1247
+ }
1248
+ }
1249
+ return {
1250
+ files,
1251
+ ir,
1252
+ fileCount: files.length,
1253
+ generators
1254
+ };
1255
+ }
1256
+ async function generate(appIR, config) {
1257
+ const ir = adaptIR(appIR);
1258
+ const result = generateSync(ir, config);
1259
+ let { files } = result;
1260
+ const shouldFormat = config.format !== false;
1261
+ if (shouldFormat) {
1262
+ files = await formatWithBiome(files);
1263
+ }
1264
+ const useIncremental = config.incremental !== false;
1265
+ let incrementalResult;
1266
+ if (useIncremental) {
1267
+ incrementalResult = await writeIncremental(files, config.outputDir);
1268
+ } else {
1269
+ await mkdir3(config.outputDir, { recursive: true });
1270
+ for (const file of files) {
1271
+ const filePath = join3(config.outputDir, file.path);
1272
+ const dir = dirname3(filePath);
1273
+ await mkdir3(dir, { recursive: true });
1274
+ await writeFile3(filePath, file.content, "utf-8");
1275
+ }
1276
+ }
1277
+ return {
1278
+ files,
1279
+ ir,
1280
+ fileCount: files.length,
1281
+ generators: result.generators,
1282
+ incremental: incrementalResult
1283
+ };
1284
+ }
1285
+ // src/pipeline.ts
1286
+ function createCodegenPipeline() {
1287
+ return {
1288
+ validate(config) {
1289
+ return validateCodegenConfig(config);
1290
+ },
1291
+ generate(ir, config) {
1292
+ const resolved = resolveCodegenConfig(config);
1293
+ return generateSync(ir, resolved);
1294
+ },
1295
+ resolveOutputDir(config) {
1296
+ const resolved = resolveCodegenConfig(config);
1297
+ return resolved.outputDir;
1298
+ },
1299
+ resolveConfig(config) {
1300
+ return resolveCodegenConfig(config);
1301
+ }
1302
+ };
1303
+ }
1304
+ export {
1305
+ writeIncremental,
1306
+ validateCodegenConfig,
1307
+ toSnakeCase,
1308
+ toPascalCase,
1309
+ toKebabCase,
1310
+ toCamelCase,
1311
+ scaffoldCLIRootIndex,
1312
+ scaffoldCLIPackageJson,
1313
+ resolveCodegenConfig,
1314
+ renderImports,
1315
+ mergeImports,
1316
+ jsonSchemaToTS,
1317
+ hashContent,
1318
+ generate,
1319
+ formatWithBiome,
1320
+ emitStreamingMethod,
1321
+ emitStreamingEventType,
1322
+ emitSharedTypesFile,
1323
+ emitSchemaReExports,
1324
+ emitSDKConfig,
1325
+ emitRouteMapType,
1326
+ emitPackageJson,
1327
+ emitOperationResponseType,
1328
+ emitOperationMethod,
1329
+ emitOperationInputType,
1330
+ emitModuleTypesFile,
1331
+ emitModuleFile,
1332
+ emitModuleCommands,
1333
+ emitManifestFile,
1334
+ emitInterfaceFromSchema,
1335
+ emitCommandDefinition,
1336
+ emitClientFile,
1337
+ emitBinEntryPoint,
1338
+ emitBarrelIndex,
1339
+ emitAuthStrategyBuilder,
1340
+ defineCodegenConfig,
1341
+ createCodegenPipeline,
1342
+ adaptIR
1343
+ };