@contractkit/plugin-typescript 0.16.1

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.
Files changed (60) hide show
  1. package/.turbo/turbo-build$colon$ci.log +35 -0
  2. package/.turbo/turbo-build.log +15 -0
  3. package/.turbo/turbo-test$colon$ci.log +81 -0
  4. package/.turbo/turbo-test.log +19 -0
  5. package/CHANGELOG.md +151 -0
  6. package/README.md +153 -0
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/clover.xml +1882 -0
  10. package/coverage/coverage-final.json +9 -0
  11. package/coverage/favicon.png +0 -0
  12. package/coverage/index.html +131 -0
  13. package/coverage/prettify.css +1 -0
  14. package/coverage/prettify.js +2 -0
  15. package/coverage/sort-arrow-sprite.png +0 -0
  16. package/coverage/sorter.js +210 -0
  17. package/coverage/src/codegen-contract.ts.html +3331 -0
  18. package/coverage/src/codegen-operation.ts.html +2530 -0
  19. package/coverage/src/codegen-plain-types.ts.html +901 -0
  20. package/coverage/src/codegen-sdk.ts.html +2797 -0
  21. package/coverage/src/index.html +206 -0
  22. package/coverage/src/index.ts.html +1360 -0
  23. package/coverage/src/path-utils.ts.html +649 -0
  24. package/coverage/src/ts-render.ts.html +592 -0
  25. package/coverage/tests/helpers.ts.html +826 -0
  26. package/coverage/tests/index.html +116 -0
  27. package/dist/codegen-contract.d.ts +56 -0
  28. package/dist/codegen-contract.d.ts.map +1 -0
  29. package/dist/codegen-operation.d.ts +25 -0
  30. package/dist/codegen-operation.d.ts.map +1 -0
  31. package/dist/codegen-plain-types.d.ts +10 -0
  32. package/dist/codegen-plain-types.d.ts.map +1 -0
  33. package/dist/codegen-sdk.d.ts +38 -0
  34. package/dist/codegen-sdk.d.ts.map +1 -0
  35. package/dist/index.d.ts +77 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +3162 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/path-utils.d.ts +15 -0
  40. package/dist/path-utils.d.ts.map +1 -0
  41. package/dist/ts-render.d.ts +20 -0
  42. package/dist/ts-render.d.ts.map +1 -0
  43. package/eslint.config.js +6 -0
  44. package/package.json +43 -0
  45. package/src/codegen-contract.ts +1082 -0
  46. package/src/codegen-operation.ts +815 -0
  47. package/src/codegen-plain-types.ts +272 -0
  48. package/src/codegen-sdk.ts +904 -0
  49. package/src/index.ts +425 -0
  50. package/src/path-utils.ts +188 -0
  51. package/src/ts-render.ts +169 -0
  52. package/tests/codegen-contract.test.ts +1004 -0
  53. package/tests/codegen-operation.test.ts +939 -0
  54. package/tests/codegen-plain-types.test.ts +636 -0
  55. package/tests/codegen-sdk.test.ts +1500 -0
  56. package/tests/codegen-server.test.ts +192 -0
  57. package/tests/helpers.ts +247 -0
  58. package/tests/pipeline.test.ts +372 -0
  59. package/tsconfig.json +9 -0
  60. package/vitest.config.ts +14 -0
package/dist/index.js ADDED
@@ -0,0 +1,3162 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/index.ts
5
+ import { resolve as resolve2, join as join2, relative as relative6, dirname as dirname6, basename as basename3 } from "path";
6
+
7
+ // src/codegen-contract.ts
8
+ import { relative, dirname } from "path";
9
+ import { collectTypeRefs, computeModelsWithOutput as ckComputeModelsWithOutput, collectExternalOutputRefs as ckCollectExternalOutputRefs } from "@contractkit/core";
10
+ function modeToWrapper(mode) {
11
+ switch (mode) {
12
+ case "strict":
13
+ return "z.strictObject";
14
+ case "strip":
15
+ return "z.object";
16
+ case "loose":
17
+ return "z.looseObject";
18
+ }
19
+ }
20
+ __name(modeToWrapper, "modeToWrapper");
21
+ function computeModelsWithInput(models, externalModelsWithInput = /* @__PURE__ */ new Set()) {
22
+ const result = /* @__PURE__ */ new Set();
23
+ for (const model of models) {
24
+ if (model.fields.some((f) => f.visibility !== "normal")) {
25
+ result.add(model.name);
26
+ }
27
+ }
28
+ let changed = true;
29
+ while (changed) {
30
+ changed = false;
31
+ for (const model of models) {
32
+ if (result.has(model.name)) continue;
33
+ const refs = /* @__PURE__ */ new Set();
34
+ for (const field of model.fields) {
35
+ collectTypeRefs(field.type, refs);
36
+ }
37
+ if (model.bases) for (const b of model.bases) refs.add(b);
38
+ if (model.type) collectTypeRefs(model.type, refs);
39
+ for (const ref of refs) {
40
+ if (result.has(ref) || externalModelsWithInput.has(ref)) {
41
+ result.add(model.name);
42
+ changed = true;
43
+ break;
44
+ }
45
+ }
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+ __name(computeModelsWithInput, "computeModelsWithInput");
51
+ function generateComments(model, outPath) {
52
+ const lines = [];
53
+ lines.push("/**");
54
+ if (model.deprecated) {
55
+ lines.push(` * @deprecated`);
56
+ }
57
+ if (model.description) {
58
+ lines.push(` * ${model.description}`);
59
+ }
60
+ const relPath = outPath ? relative(dirname(outPath), model.loc.file) : model.loc.file;
61
+ lines.push(` * generated from [${model.name}](file://./${relPath}#L${model.loc.line})`);
62
+ lines.push("*/");
63
+ return lines;
64
+ }
65
+ __name(generateComments, "generateComments");
66
+ function generateContract(root, context) {
67
+ const needsDateTime = rootNeedsDateTime(root);
68
+ const needsDuration = rootNeedsScalar(root, "duration");
69
+ const needsInterval = rootNeedsScalar(root, "interval");
70
+ const needsBinary = rootNeedsScalar(root, "binary");
71
+ const needsDatetime = rootNeedsScalar(root, "datetime");
72
+ const needsJson = rootNeedsScalar(root, "json");
73
+ const externalRefs = collectExternalRefs(root);
74
+ const lines = [];
75
+ const externalModelsWithInput = context?.modelsWithInput ?? /* @__PURE__ */ new Set();
76
+ const localModelsWithInput = computeModelsWithInput(root.models, externalModelsWithInput);
77
+ const allModelsWithInput = /* @__PURE__ */ new Set([
78
+ ...localModelsWithInput,
79
+ ...externalModelsWithInput
80
+ ]);
81
+ const externalModelsWithOutput = context?.modelsWithOutput ?? /* @__PURE__ */ new Set();
82
+ const localModelsWithOutput = ckComputeModelsWithOutput(root.models, externalModelsWithOutput);
83
+ const allModelsWithOutput = /* @__PURE__ */ new Set([
84
+ ...localModelsWithOutput,
85
+ ...externalModelsWithOutput
86
+ ]);
87
+ const externalInputRefs = allModelsWithInput.size > 0 ? collectExternalInputRefs(root, allModelsWithInput) : [];
88
+ const externalOutputRefs = allModelsWithOutput.size > 0 ? ckCollectExternalOutputRefs(root, allModelsWithOutput) : [];
89
+ const allExternalRefs = [
90
+ .../* @__PURE__ */ new Set([
91
+ ...externalRefs,
92
+ ...externalInputRefs,
93
+ ...externalOutputRefs
94
+ ])
95
+ ].sort();
96
+ lines.push(`import { z } from 'zod';`);
97
+ const luxonImports = [];
98
+ if (needsDateTime) luxonImports.push("DateTime");
99
+ if (needsDuration) luxonImports.push("Duration");
100
+ if (needsInterval) luxonImports.push("Interval");
101
+ if (luxonImports.length > 0) lines.push(`import { ${luxonImports.join(", ")} } from 'luxon';`);
102
+ for (const ref of allExternalRefs) {
103
+ const importPath = resolveImportPath(ref, context);
104
+ lines.push(`import { ${ref} } from '${importPath}';`);
105
+ }
106
+ lines.push("");
107
+ if (needsBinary) {
108
+ lines.push(`const _ZodBinary = z.custom<Buffer>((val) => Buffer.isBuffer(val), { error: 'Must be binary data' });`);
109
+ }
110
+ if (needsDatetime) {
111
+ lines.push(`const _ZodDatetime = z.preprocess((val) => typeof val === 'string' ? DateTime.fromISO(val) : val, z.custom<DateTime>((val) => val instanceof DateTime && val.isValid, { message: 'Must be in ISO 8601 format' }));`);
112
+ }
113
+ if (needsInterval) {
114
+ lines.push(`const _ZodInterval = z.preprocess((val) => typeof val === 'string' ? Interval.fromISO(val) : val, z.custom<Interval>((val) => val instanceof Interval && val.isValid, { message: 'Must be an ISO 8601 interval' })).transform(val => val.toISO()!);`);
115
+ }
116
+ if (needsJson) {
117
+ lines.push(`type _JsonValue = string | number | boolean | null | _JsonValue[] | { [key: string]: _JsonValue };`);
118
+ lines.push(`const _ZodJson: z.ZodType<_JsonValue> = z.lazy(() => z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(_ZodJson), z.record(z.string(), _ZodJson)]));`);
119
+ }
120
+ if (needsBinary || needsDatetime || needsInterval || needsJson) lines.push("");
121
+ const modelsWithWriteonly = new Set(root.models.filter((m) => m.fields.some((f) => f.visibility === "writeonly")).map((m) => m.name));
122
+ const modelMap = new Map(root.models.map((m) => [
123
+ m.name,
124
+ m
125
+ ]));
126
+ for (const model of topoSortModels(root.models)) {
127
+ lines.push(...generateModel(model, context?.currentOutPath, allModelsWithInput, modelsWithWriteonly, modelMap, allModelsWithOutput));
128
+ lines.push("");
129
+ }
130
+ return lines.join("\n");
131
+ }
132
+ __name(generateContract, "generateContract");
133
+ function flattenFormatChain(model, modelMap) {
134
+ if (!model.bases || model.bases.length === 0) return model;
135
+ const firstBase = model.bases[0];
136
+ const parent = modelMap.get(firstBase);
137
+ if (!parent) return model;
138
+ const flatParent = flattenFormatChain(parent, modelMap);
139
+ const parentHasFormat = flatParent.inputCase !== void 0 && flatParent.inputCase !== "camel" || flatParent.outputCase !== void 0 && flatParent.outputCase !== "camel";
140
+ if (!parentHasFormat) return model;
141
+ const merged = /* @__PURE__ */ new Map();
142
+ for (const f of flatParent.fields) merged.set(f.name, f);
143
+ for (const f of model.fields) merged.set(f.name, f);
144
+ return {
145
+ ...model,
146
+ bases: void 0,
147
+ fields: [
148
+ ...merged.values()
149
+ ],
150
+ inputCase: model.inputCase ?? flatParent.inputCase,
151
+ outputCase: model.outputCase ?? flatParent.outputCase,
152
+ mode: model.mode ?? flatParent.mode
153
+ };
154
+ }
155
+ __name(flattenFormatChain, "flattenFormatChain");
156
+ function generateModel(model, outPath, modelsWithInput, modelsWithWriteonly, modelMap, modelsWithOutput) {
157
+ if (model.type) {
158
+ return generateTypeAlias(model, outPath, modelsWithInput, modelsWithOutput);
159
+ }
160
+ const effective = modelMap ? flattenFormatChain(model, modelMap) : model;
161
+ const needsInputSplit = effective.fields.some((f) => f.visibility !== "normal") || (modelsWithInput?.has(effective.name) ?? false);
162
+ const lines = needsInputSplit ? generateThreeSchemaModel(effective, outPath, modelsWithInput, modelsWithWriteonly) : generateSimpleModel(effective, outPath);
163
+ if (modelsWithOutput?.has(effective.name)) {
164
+ lines.push(`export type ${effective.name}Output = z.output<typeof ${effective.name}>;`);
165
+ }
166
+ return lines;
167
+ }
168
+ __name(generateModel, "generateModel");
169
+ function generateTypeAlias(model, outPath, modelsWithInput, modelsWithOutput) {
170
+ const lines = [];
171
+ lines.push(...generateComments(model, outPath));
172
+ lines.push(`export const ${model.name} = ${renderType(model.type)};`);
173
+ lines.push(`export type ${model.name} = z.infer<typeof ${model.name}>;`);
174
+ if (modelsWithInput?.has(model.name)) {
175
+ lines.push(`export const ${model.name}Input = ${renderInputType(model.type, modelsWithInput)};`);
176
+ lines.push(`export type ${model.name}Input = z.infer<typeof ${model.name}Input>;`);
177
+ }
178
+ if (modelsWithOutput?.has(model.name)) {
179
+ lines.push(`export type ${model.name}Output = z.output<typeof ${model.name}>;`);
180
+ }
181
+ return lines;
182
+ }
183
+ __name(generateTypeAlias, "generateTypeAlias");
184
+ function generateSimpleModel(model, outPath) {
185
+ const lines = [];
186
+ lines.push(...generateComments(model, outPath));
187
+ const wrapper = modeToWrapper(model.mode ?? "strict");
188
+ const { inputCase, outputCase } = model;
189
+ const hasInputTransform = !!inputCase && inputCase !== "camel";
190
+ const hasOutputTransform = !!outputCase && outputCase !== "camel";
191
+ if (hasInputTransform || hasOutputTransform) {
192
+ const inputBody = inputCase === "snake" ? renderFieldsAsSnakeCase(model.fields, model.mode) : inputCase === "pascal" ? renderFieldsAsPascalCase(model.fields, model.mode) : renderFields(model.fields, model.mode);
193
+ lines.push(`export const ${model.name} = ${wrapper}({`);
194
+ lines.push(...inputBody.map((l) => ` ${l}`));
195
+ lines.push(`}).transform(data => ({`);
196
+ for (const field of model.fields) {
197
+ const inputKey = applyCase(field.name, inputCase);
198
+ const outputKey = applyCase(field.name, outputCase);
199
+ lines.push(` ${quoteKey(outputKey)}: data.${inputKey},`);
200
+ }
201
+ lines.push(`}));`);
202
+ const typeSource = hasOutputTransform && !hasInputTransform ? "input" : "output";
203
+ lines.push(`export type ${model.name} = z.${typeSource}<typeof ${model.name}>;`);
204
+ return lines;
205
+ }
206
+ const body = renderFields(model.fields, model.mode);
207
+ const bases = model.bases ?? [];
208
+ if (bases.length > 0) {
209
+ const head = bases[0];
210
+ const tail = bases.slice(1).map((b) => `.extend(${b}.shape)`).join("");
211
+ lines.push(`export const ${model.name} = ${head}${tail}.extend({`);
212
+ lines.push(...body.map((l) => ` ${l}`));
213
+ lines.push(`});`);
214
+ } else {
215
+ lines.push(`export const ${model.name} = ${wrapper}({`);
216
+ lines.push(...body.map((l) => ` ${l}`));
217
+ lines.push(`});`);
218
+ }
219
+ lines.push(`export type ${model.name} = z.infer<typeof ${model.name}>;`);
220
+ return lines;
221
+ }
222
+ __name(generateSimpleModel, "generateSimpleModel");
223
+ function buildExtendChain(bases, resolveName) {
224
+ const head = resolveName(bases[0]);
225
+ const tail = bases.slice(1).map((b) => `.extend(${resolveName(b)}.shape)`).join("");
226
+ return {
227
+ head,
228
+ tail
229
+ };
230
+ }
231
+ __name(buildExtendChain, "buildExtendChain");
232
+ function generateThreeSchemaModel(model, outPath, modelsWithInput, modelsWithWriteonly) {
233
+ const lines = [];
234
+ const name = model.name;
235
+ lines.push(...generateComments(model, outPath));
236
+ const wrapper = modeToWrapper(model.mode ?? "strict");
237
+ const allFields = model.fields;
238
+ const hasWriteonly = allFields.some((f) => f.visibility === "writeonly");
239
+ const bases = model.bases ?? [];
240
+ if (hasWriteonly) {
241
+ const baseBody = renderFields(allFields, model.mode);
242
+ if (bases.length > 0) {
243
+ const { head, tail } = buildExtendChain(bases, (b) => modelsWithWriteonly?.has(b) ? `${b}Base` : b);
244
+ lines.push(`const ${name}Base = ${head}${tail}.extend({`);
245
+ } else {
246
+ lines.push(`const ${name}Base = ${wrapper}({`);
247
+ }
248
+ lines.push(...baseBody.map((l) => ` ${l}`));
249
+ lines.push(`});`);
250
+ lines.push("");
251
+ }
252
+ const readFields = allFields.filter((f) => f.visibility !== "writeonly");
253
+ const readBody = renderFields(readFields, model.mode);
254
+ if (bases.length > 0) {
255
+ const { head, tail } = buildExtendChain(bases, (b) => b);
256
+ lines.push(`export const ${name} = ${head}${tail}.extend({`);
257
+ } else {
258
+ lines.push(`export const ${name} = ${wrapper}({`);
259
+ }
260
+ lines.push(...readBody.map((l) => ` ${l}`));
261
+ lines.push(`});`);
262
+ lines.push(`export type ${name} = z.infer<typeof ${name}>;`);
263
+ lines.push("");
264
+ const writeFields = allFields.filter((f) => f.visibility !== "readonly");
265
+ const writeBody = modelsWithInput ? renderInputFields(writeFields, modelsWithInput, model.mode) : renderFields(writeFields, model.mode);
266
+ if (bases.length > 0) {
267
+ const { head, tail } = buildExtendChain(bases, (b) => modelsWithInput?.has(b) ? `${b}Input` : b);
268
+ lines.push(`export const ${name}Input = ${head}${tail}.extend({`);
269
+ } else {
270
+ lines.push(`export const ${name}Input = ${wrapper}({`);
271
+ }
272
+ lines.push(...writeBody.map((l) => ` ${l}`));
273
+ lines.push(`});`);
274
+ lines.push(`export type ${name}Input = z.infer<typeof ${name}Input>;`);
275
+ return lines;
276
+ }
277
+ __name(generateThreeSchemaModel, "generateThreeSchemaModel");
278
+ function camelToSnake(s) {
279
+ return s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
280
+ }
281
+ __name(camelToSnake, "camelToSnake");
282
+ function camelToPascal(s) {
283
+ return s.charAt(0).toUpperCase() + s.slice(1);
284
+ }
285
+ __name(camelToPascal, "camelToPascal");
286
+ function applyCase(name, caseTransform) {
287
+ if (!caseTransform || caseTransform === "camel") return name;
288
+ if (caseTransform === "snake") return camelToSnake(name);
289
+ return camelToPascal(name);
290
+ }
291
+ __name(applyCase, "applyCase");
292
+ function renderFields(fields, defaultMode) {
293
+ return fields.flatMap((f) => renderField(f, defaultMode));
294
+ }
295
+ __name(renderFields, "renderFields");
296
+ function renderFieldsAsPascalCase(fields, defaultMode) {
297
+ return fields.map((f) => {
298
+ const pascalKey = camelToPascal(f.name);
299
+ let expr = renderType(f.type, "pascal", defaultMode);
300
+ if (f.default !== void 0) {
301
+ if (f.nullable) expr += ".nullable()";
302
+ const dv = typeof f.default === "string" ? `"${escapeString(f.default)}"` : String(f.default);
303
+ expr += `.default(${dv})`;
304
+ } else if (f.optional) {
305
+ expr += ".nullish()";
306
+ } else if (f.nullable) {
307
+ expr += ".nullable()";
308
+ }
309
+ if (f.description) expr += `.describe("${escapeString(f.description)}")`;
310
+ return `${quoteKey(pascalKey)}: ${expr},`;
311
+ });
312
+ }
313
+ __name(renderFieldsAsPascalCase, "renderFieldsAsPascalCase");
314
+ function renderFieldsAsSnakeCase(fields, defaultMode) {
315
+ return fields.map((f) => {
316
+ const snakeKey = camelToSnake(f.name);
317
+ let expr = renderType(f.type, "snake", defaultMode);
318
+ if (f.default !== void 0) {
319
+ if (f.nullable) expr += ".nullable()";
320
+ const dv = typeof f.default === "string" ? `"${escapeString(f.default)}"` : String(f.default);
321
+ expr += `.default(${dv})`;
322
+ } else if (f.optional) {
323
+ expr += ".nullish()";
324
+ } else if (f.nullable) {
325
+ expr += ".nullable()";
326
+ }
327
+ if (f.description) expr += `.describe("${escapeString(f.description)}")`;
328
+ return `${quoteKey(snakeKey)}: ${expr},`;
329
+ });
330
+ }
331
+ __name(renderFieldsAsSnakeCase, "renderFieldsAsSnakeCase");
332
+ function renderField(field, defaultMode) {
333
+ const lines = [];
334
+ if (field.deprecated) lines.push("/** @deprecated */");
335
+ let expr = renderType(field.type, void 0, defaultMode);
336
+ if (field.nullable) expr += ".nullable()";
337
+ if (field.default !== void 0) {
338
+ const dv = typeof field.default === "string" ? `"${escapeString(field.default)}"` : String(field.default);
339
+ expr += `.default(${dv})`;
340
+ } else if (field.optional) {
341
+ expr += ".optional()";
342
+ }
343
+ if (field.description) expr += `.describe("${escapeString(field.description)}")`;
344
+ lines.push(`${quoteKey(field.name)}: ${expr},`);
345
+ return lines;
346
+ }
347
+ __name(renderField, "renderField");
348
+ function renderType(type, parseCaseTransform, defaultMode) {
349
+ switch (type.kind) {
350
+ case "scalar":
351
+ return renderScalar(type);
352
+ case "array":
353
+ return renderArray(type, parseCaseTransform, defaultMode);
354
+ case "tuple":
355
+ return renderTuple(type);
356
+ case "record":
357
+ return renderRecord(type);
358
+ case "enum":
359
+ return renderEnum(type);
360
+ case "literal":
361
+ return renderLiteral(type);
362
+ case "union":
363
+ return renderUnion(type, parseCaseTransform, defaultMode);
364
+ case "discriminatedUnion":
365
+ return renderDiscriminatedUnion(type, parseCaseTransform, defaultMode);
366
+ case "intersection":
367
+ return renderIntersection(type, parseCaseTransform, defaultMode);
368
+ case "ref":
369
+ return type.name;
370
+ case "lazy":
371
+ return `z.lazy(() => ${renderType(type.inner, parseCaseTransform, defaultMode)})`;
372
+ case "inlineObject":
373
+ return renderInlineObject(type, parseCaseTransform, defaultMode);
374
+ default:
375
+ return "z.unknown()";
376
+ }
377
+ }
378
+ __name(renderType, "renderType");
379
+ function renderRegexLiteral(source) {
380
+ const body = source.replace(/\//g, "\\/");
381
+ if (regexHasAnchor(source)) return `/${body}/`;
382
+ return `/^${body}$/`;
383
+ }
384
+ __name(renderRegexLiteral, "renderRegexLiteral");
385
+ function regexHasAnchor(source) {
386
+ if (source.startsWith("^")) return true;
387
+ if (!source.endsWith("$")) return false;
388
+ let i = source.length - 2;
389
+ let backslashes = 0;
390
+ while (i >= 0 && source[i] === "\\") {
391
+ backslashes++;
392
+ i--;
393
+ }
394
+ return backslashes % 2 === 0;
395
+ }
396
+ __name(regexHasAnchor, "regexHasAnchor");
397
+ function renderScalar(s) {
398
+ switch (s.name) {
399
+ case "string": {
400
+ let e = "z.string()";
401
+ if (s.min !== void 0 && s.max !== void 0) e += `.min(${s.min}).max(${s.max})`;
402
+ else if (s.min !== void 0) e += `.min(${s.min})`;
403
+ else if (s.max !== void 0) e += `.max(${s.max})`;
404
+ if (s.len !== void 0) e += `.length(${s.len})`;
405
+ if (s.regex) e += `.regex(${renderRegexLiteral(s.regex)})`;
406
+ return e;
407
+ }
408
+ case "number": {
409
+ let e = "z.coerce.number()";
410
+ if (s.min !== void 0) e += `.min(${s.min})`;
411
+ if (s.max !== void 0) e += `.max(${s.max})`;
412
+ return e;
413
+ }
414
+ case "int": {
415
+ let e = "z.coerce.number().int()";
416
+ if (s.min !== void 0) e += `.min(${s.min})`;
417
+ if (s.max !== void 0) e += `.max(${s.max})`;
418
+ return e;
419
+ }
420
+ case "bigint": {
421
+ let inner = "z.bigint()";
422
+ if (s.min !== void 0) inner += `.min(${s.min}n)`;
423
+ if (s.max !== void 0) inner += `.max(${s.max}n)`;
424
+ return `z.preprocess((val) => typeof val === 'string' ? BigInt(val.replace(/n$/, '')) : val, ${inner})`;
425
+ }
426
+ case "boolean":
427
+ return `z.preprocess((v) => v === 'true' ? true : v === 'false' ? false : v, z.boolean())`;
428
+ case "date": {
429
+ const fmt = s.format ?? "yyyy-MM-dd";
430
+ return `z.preprocess((val) => typeof val === 'string' ? DateTime.fromFormat(val, '${escapeString(fmt)}') : val, z.custom<DateTime>((val) => val instanceof DateTime && val.isValid, { message: 'Must be a date in format ${escapeString(fmt)}' }))`;
431
+ }
432
+ case "time": {
433
+ const fmt = s.format ?? "HH:mm:ss";
434
+ return `z.preprocess((val) => typeof val === 'string' ? DateTime.fromFormat(val, '${escapeString(fmt)}') : val, z.custom<DateTime>((val) => val instanceof DateTime && val.isValid, { message: 'Must be a time in format ${escapeString(fmt)}' }))`;
435
+ }
436
+ case "datetime":
437
+ return "_ZodDatetime";
438
+ case "interval":
439
+ return "_ZodInterval";
440
+ case "duration": {
441
+ const validParts = [
442
+ `val instanceof Duration && val.isValid`
443
+ ];
444
+ if (s.min !== void 0) validParts.push(`val.toMillis() >= Duration.fromISO('${s.min}').toMillis()`);
445
+ if (s.max !== void 0) validParts.push(`val.toMillis() <= Duration.fromISO('${s.max}').toMillis()`);
446
+ const validation = validParts.join(" && ");
447
+ let message = "Must be an ISO 8601 duration";
448
+ if (s.min !== void 0 && s.max !== void 0) message += ` between ${s.min} and ${s.max}`;
449
+ else if (s.min !== void 0) message += ` of at least ${s.min}`;
450
+ else if (s.max !== void 0) message += ` of at most ${s.max}`;
451
+ return `z.preprocess((val) => typeof val === 'string' ? Duration.fromISO(val) : val, z.custom<Duration>((val) => ${validation}, { message: '${message}' }))`;
452
+ }
453
+ case "email":
454
+ return "z.email()";
455
+ case "url":
456
+ return "z.url()";
457
+ case "uuid":
458
+ return "z.uuid()";
459
+ case "unknown":
460
+ return "z.unknown()";
461
+ case "null":
462
+ return "z.null()";
463
+ case "object":
464
+ return "z.record(z.string(), z.unknown())";
465
+ case "binary":
466
+ return "_ZodBinary";
467
+ case "json":
468
+ return "_ZodJson";
469
+ default:
470
+ return "z.unknown()";
471
+ }
472
+ }
473
+ __name(renderScalar, "renderScalar");
474
+ function renderArray(a, parseCaseTransform, defaultMode) {
475
+ let e = `z.array(${renderType(a.item, parseCaseTransform, defaultMode)})`;
476
+ if (a.min !== void 0) e += `.min(${a.min})`;
477
+ if (a.max !== void 0) e += `.max(${a.max})`;
478
+ return e;
479
+ }
480
+ __name(renderArray, "renderArray");
481
+ function renderTuple(t) {
482
+ return `z.tuple([${t.items.map((i) => renderType(i)).join(", ")}])`;
483
+ }
484
+ __name(renderTuple, "renderTuple");
485
+ function renderRecord(r) {
486
+ return `z.record(${renderType(r.key)}, ${renderType(r.value)})`;
487
+ }
488
+ __name(renderRecord, "renderRecord");
489
+ function renderEnum(e) {
490
+ const vals = e.values.map((v) => `"${v}"`).join(", ");
491
+ return `z.enum([${vals}])`;
492
+ }
493
+ __name(renderEnum, "renderEnum");
494
+ function renderLiteral(l) {
495
+ if (typeof l.value === "string") return `z.literal("${escapeString(l.value)}")`;
496
+ return `z.literal(${l.value})`;
497
+ }
498
+ __name(renderLiteral, "renderLiteral");
499
+ function renderUnion(u, parseCaseTransform, defaultMode) {
500
+ return `z.union([${u.members.map((m) => renderType(m, parseCaseTransform, defaultMode)).join(", ")}])`;
501
+ }
502
+ __name(renderUnion, "renderUnion");
503
+ function renderDiscriminatedUnion(u, parseCaseTransform, defaultMode) {
504
+ return `z.discriminatedUnion("${escapeString(u.discriminator)}", [${u.members.map((m) => renderType(m, parseCaseTransform, defaultMode)).join(", ")}])`;
505
+ }
506
+ __name(renderDiscriminatedUnion, "renderDiscriminatedUnion");
507
+ function renderIntersection(i, parseCaseTransform, defaultMode) {
508
+ const [first, ...rest] = i.members;
509
+ if (first && first.kind === "ref" && rest.length > 0 && rest.every((m) => m.kind === "inlineObject")) {
510
+ const allFields = rest.flatMap((m) => m.fields);
511
+ const fieldLines = parseCaseTransform === "snake" ? renderFieldsAsSnakeCase(allFields, defaultMode).map((l) => ` ${l}`).join("\n") : parseCaseTransform === "pascal" ? renderFieldsAsPascalCase(allFields, defaultMode).map((l) => ` ${l}`).join("\n") : allFields.flatMap((f) => renderField(f, defaultMode)).map((l) => ` ${l}`).join("\n");
512
+ return `${first.name}.extend({
513
+ ${fieldLines}
514
+ })`;
515
+ }
516
+ let expr = renderType(first, parseCaseTransform, defaultMode);
517
+ for (const member of rest) {
518
+ expr += `.and(${renderType(member, parseCaseTransform, defaultMode)})`;
519
+ }
520
+ return expr;
521
+ }
522
+ __name(renderIntersection, "renderIntersection");
523
+ function renderInlineObject(o, parseCaseTransform, defaultMode) {
524
+ const wrapper = modeToWrapper(o.mode ?? defaultMode ?? "strict");
525
+ if (parseCaseTransform === "snake") {
526
+ const snakeLines = renderFieldsAsSnakeCase(o.fields, defaultMode);
527
+ const joined = snakeLines.map((l) => ` ${l}`).join("\n");
528
+ const transformEntries = o.fields.map((f) => {
529
+ const snakeKey = camelToSnake(f.name);
530
+ const val = f.optional ? `data.${snakeKey} ?? undefined` : `data.${snakeKey}`;
531
+ return ` ${quoteKey(f.name)}: ${val},`;
532
+ }).join("\n");
533
+ return `${wrapper}({
534
+ ${joined}
535
+ }).transform(data => ({
536
+ ${transformEntries}
537
+ }))`;
538
+ }
539
+ if (parseCaseTransform === "pascal") {
540
+ const pascalLines = renderFieldsAsPascalCase(o.fields, defaultMode);
541
+ const joined = pascalLines.map((l) => ` ${l}`).join("\n");
542
+ const transformEntries = o.fields.map((f) => {
543
+ const pascalKey = camelToPascal(f.name);
544
+ const val = f.optional ? `data.${pascalKey} ?? undefined` : `data.${pascalKey}`;
545
+ return ` ${quoteKey(f.name)}: ${val},`;
546
+ }).join("\n");
547
+ return `${wrapper}({
548
+ ${joined}
549
+ }).transform(data => ({
550
+ ${transformEntries}
551
+ }))`;
552
+ }
553
+ const fields = o.fields.flatMap((f) => renderField(f, defaultMode)).map((l) => ` ${l}`).join("\n");
554
+ return `${wrapper}({
555
+ ${fields}
556
+ })`;
557
+ }
558
+ __name(renderInlineObject, "renderInlineObject");
559
+ function renderInputScalar(s) {
560
+ return renderScalar(s);
561
+ }
562
+ __name(renderInputScalar, "renderInputScalar");
563
+ function renderInputType(type, modelsWithInput, defaultMode) {
564
+ switch (type.kind) {
565
+ case "scalar":
566
+ return renderInputScalar(type);
567
+ case "ref":
568
+ return modelsWithInput?.has(type.name) ? `${type.name}Input` : type.name;
569
+ case "array": {
570
+ let e = `z.array(${renderInputType(type.item, modelsWithInput, defaultMode)})`;
571
+ if (type.min !== void 0) e += `.min(${type.min})`;
572
+ if (type.max !== void 0) e += `.max(${type.max})`;
573
+ return e;
574
+ }
575
+ case "tuple":
576
+ return `z.tuple([${type.items.map((i) => renderInputType(i, modelsWithInput, defaultMode)).join(", ")}])`;
577
+ case "record":
578
+ return `z.record(${renderInputType(type.key, modelsWithInput, defaultMode)}, ${renderInputType(type.value, modelsWithInput, defaultMode)})`;
579
+ case "union":
580
+ return `z.union([${type.members.map((m) => renderInputType(m, modelsWithInput, defaultMode)).join(", ")}])`;
581
+ case "discriminatedUnion":
582
+ return `z.discriminatedUnion("${escapeString(type.discriminator)}", [${type.members.map((m) => renderInputType(m, modelsWithInput, defaultMode)).join(", ")}])`;
583
+ case "intersection": {
584
+ const [first, ...rest] = type.members;
585
+ if (first && first.kind === "ref" && rest.length > 0 && rest.every((m) => m.kind === "inlineObject")) {
586
+ const base = modelsWithInput?.has(first.name) ? `${first.name}Input` : first.name;
587
+ const allFields = rest.flatMap((m) => m.fields);
588
+ const fieldLines = allFields.map((f) => ` ${renderInputField(f, modelsWithInput ?? /* @__PURE__ */ new Set(), defaultMode)}`).join("\n");
589
+ return `${base}.extend({
590
+ ${fieldLines}
591
+ })`;
592
+ }
593
+ let expr = renderInputType(first, modelsWithInput, defaultMode);
594
+ for (const member of rest) {
595
+ expr += `.and(${renderInputType(member, modelsWithInput, defaultMode)})`;
596
+ }
597
+ return expr;
598
+ }
599
+ case "lazy":
600
+ return `z.lazy(() => ${renderInputType(type.inner, modelsWithInput, defaultMode)})`;
601
+ case "inlineObject": {
602
+ const fields = type.fields.flatMap((f) => renderInputField(f, modelsWithInput ?? /* @__PURE__ */ new Set(), defaultMode)).map((l) => ` ${l}`).join("\n");
603
+ return `${modeToWrapper(type.mode ?? defaultMode ?? "strict")}({
604
+ ${fields}
605
+ })`;
606
+ }
607
+ default:
608
+ return renderType(type, void 0, defaultMode);
609
+ }
610
+ }
611
+ __name(renderInputType, "renderInputType");
612
+ function renderInputField(field, modelsWithInput, defaultMode) {
613
+ const lines = [];
614
+ if (field.deprecated) lines.push("/** @deprecated */");
615
+ let expr = renderInputType(field.type, modelsWithInput, defaultMode);
616
+ if (field.nullable) expr += ".nullable()";
617
+ if (field.default !== void 0) {
618
+ const dv = typeof field.default === "string" ? `"${escapeString(field.default)}"` : String(field.default);
619
+ expr += `.default(${dv})`;
620
+ } else if (field.optional) {
621
+ expr += ".optional()";
622
+ }
623
+ if (field.description) expr += `.describe("${escapeString(field.description)}")`;
624
+ lines.push(`${quoteKey(field.name)}: ${expr},`);
625
+ return lines;
626
+ }
627
+ __name(renderInputField, "renderInputField");
628
+ function renderInputFields(fields, modelsWithInput, defaultMode) {
629
+ return fields.flatMap((f) => renderInputField(f, modelsWithInput, defaultMode));
630
+ }
631
+ __name(renderInputFields, "renderInputFields");
632
+ function renderQueryType(type, modelsWithInput, defaultMode) {
633
+ switch (type.kind) {
634
+ case "array": {
635
+ const inner = modelsWithInput ? renderInputType(type, modelsWithInput, defaultMode) : renderType(type, void 0, defaultMode);
636
+ return `z.preprocess((v) => typeof v === 'string' ? v.split(',') : v, ${inner})`;
637
+ }
638
+ case "inlineObject": {
639
+ const fields = type.fields.map((f) => ` ${renderQueryField(f, modelsWithInput, defaultMode)}`).join("\n");
640
+ return `${modeToWrapper(type.mode ?? defaultMode ?? "strict")}({
641
+ ${fields}
642
+ })`;
643
+ }
644
+ case "intersection": {
645
+ const [first, ...rest] = type.members;
646
+ if (first && first.kind === "ref" && rest.length > 0 && rest.every((m) => m.kind === "inlineObject")) {
647
+ const base = modelsWithInput?.has(first.name) ? `${first.name}Input` : first.name;
648
+ const allFields = rest.flatMap((m) => m.fields);
649
+ const fieldLines = allFields.map((f) => ` ${renderQueryField(f, modelsWithInput, defaultMode)}`).join("\n");
650
+ return `${base}.extend({
651
+ ${fieldLines}
652
+ })`;
653
+ }
654
+ let expr = renderQueryType(first, modelsWithInput, defaultMode);
655
+ for (const member of rest) {
656
+ expr += `.and(${renderQueryType(member, modelsWithInput, defaultMode)})`;
657
+ }
658
+ return expr;
659
+ }
660
+ case "ref":
661
+ return modelsWithInput?.has(type.name) ? `${type.name}Input` : type.name;
662
+ default:
663
+ return modelsWithInput ? renderInputType(type, modelsWithInput, defaultMode) : renderType(type, void 0, defaultMode);
664
+ }
665
+ }
666
+ __name(renderQueryType, "renderQueryType");
667
+ function renderQueryField(field, modelsWithInput, defaultMode) {
668
+ let expr = field.type.kind === "array" ? renderQueryType(field.type, modelsWithInput, defaultMode) : modelsWithInput ? renderInputType(field.type, modelsWithInput, defaultMode) : renderType(field.type, void 0, defaultMode);
669
+ if (field.nullable) expr += ".nullable()";
670
+ if (field.default !== void 0) {
671
+ const dv = typeof field.default === "string" ? `"${escapeString(field.default)}"` : String(field.default);
672
+ expr += `.default(${dv})`;
673
+ } else if (field.optional) {
674
+ expr += ".optional()";
675
+ }
676
+ if (field.description) expr += `.describe("${escapeString(field.description)}")`;
677
+ return `${quoteKey(field.name)}: ${expr},`;
678
+ }
679
+ __name(renderQueryField, "renderQueryField");
680
+ function isValidIdentifier(name) {
681
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
682
+ }
683
+ __name(isValidIdentifier, "isValidIdentifier");
684
+ function quoteKey(name) {
685
+ return isValidIdentifier(name) ? name : `'${name}'`;
686
+ }
687
+ __name(quoteKey, "quoteKey");
688
+ function escapeString(s) {
689
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
690
+ }
691
+ __name(escapeString, "escapeString");
692
+ function rootNeedsDateTime(root) {
693
+ return root.models.some((m) => m.type && typeNeedsDateTime(m.type) || m.fields.some((f) => typeNeedsDateTime(f.type)));
694
+ }
695
+ __name(rootNeedsDateTime, "rootNeedsDateTime");
696
+ function typeNeedsScalar(type, name) {
697
+ switch (type.kind) {
698
+ case "scalar":
699
+ return type.name === name;
700
+ case "array":
701
+ return typeNeedsScalar(type.item, name);
702
+ case "tuple":
703
+ return type.items.some((i) => typeNeedsScalar(i, name));
704
+ case "record":
705
+ return typeNeedsScalar(type.key, name) || typeNeedsScalar(type.value, name);
706
+ case "union":
707
+ return type.members.some((m) => typeNeedsScalar(m, name));
708
+ case "discriminatedUnion":
709
+ return type.members.some((m) => typeNeedsScalar(m, name));
710
+ case "intersection":
711
+ return type.members.some((m) => typeNeedsScalar(m, name));
712
+ case "lazy":
713
+ return typeNeedsScalar(type.inner, name);
714
+ case "inlineObject":
715
+ return type.fields.some((f) => typeNeedsScalar(f.type, name));
716
+ default:
717
+ return false;
718
+ }
719
+ }
720
+ __name(typeNeedsScalar, "typeNeedsScalar");
721
+ function rootNeedsScalar(root, name) {
722
+ return root.models.some((m) => m.type && typeNeedsScalar(m.type, name) || m.fields.some((f) => typeNeedsScalar(f.type, name)));
723
+ }
724
+ __name(rootNeedsScalar, "rootNeedsScalar");
725
+ function typeNeedsDateTime(type) {
726
+ switch (type.kind) {
727
+ case "scalar":
728
+ return type.name === "date" || type.name === "time" || type.name === "datetime";
729
+ case "array":
730
+ return typeNeedsDateTime(type.item);
731
+ case "union":
732
+ return type.members.some(typeNeedsDateTime);
733
+ case "discriminatedUnion":
734
+ return type.members.some(typeNeedsDateTime);
735
+ case "intersection":
736
+ return type.members.some(typeNeedsDateTime);
737
+ case "inlineObject":
738
+ return type.fields.some((f) => typeNeedsDateTime(f.type));
739
+ default:
740
+ return false;
741
+ }
742
+ }
743
+ __name(typeNeedsDateTime, "typeNeedsDateTime");
744
+ function collectExternalRefs(root) {
745
+ const localNames = new Set(root.models.map((m) => m.name));
746
+ const refs = /* @__PURE__ */ new Set();
747
+ for (const model of root.models) {
748
+ if (model.bases?.[0] && !localNames.has(model.bases?.[0])) refs.add(model.bases?.[0]);
749
+ if (model.type) collectTypeRefs(model.type, refs);
750
+ for (const field of model.fields) {
751
+ collectTypeRefs(field.type, refs);
752
+ }
753
+ }
754
+ for (const name of localNames) refs.delete(name);
755
+ return [
756
+ ...refs
757
+ ].sort();
758
+ }
759
+ __name(collectExternalRefs, "collectExternalRefs");
760
+ function collectExternalInputRefs(root, modelsWithInput) {
761
+ const localNames = new Set(root.models.map((m) => m.name));
762
+ const refs = /* @__PURE__ */ new Set();
763
+ for (const model of root.models) {
764
+ if (!modelsWithInput.has(model.name)) continue;
765
+ if (model.type) {
766
+ collectInputTypeRefs(model.type, refs, modelsWithInput);
767
+ continue;
768
+ }
769
+ if (model.bases?.[0] && modelsWithInput.has(model.bases?.[0]) && !localNames.has(model.bases?.[0])) {
770
+ refs.add(`${model.bases?.[0]}Input`);
771
+ }
772
+ const writeFields = model.fields.filter((f) => f.visibility !== "readonly");
773
+ for (const field of writeFields) {
774
+ collectInputTypeRefs(field.type, refs, modelsWithInput);
775
+ }
776
+ }
777
+ for (const name of localNames) {
778
+ refs.delete(`${name}Input`);
779
+ }
780
+ return [
781
+ ...refs
782
+ ].sort();
783
+ }
784
+ __name(collectExternalInputRefs, "collectExternalInputRefs");
785
+ function collectInputTypeRefs(type, out, modelsWithInput) {
786
+ switch (type.kind) {
787
+ case "ref":
788
+ if (modelsWithInput.has(type.name)) out.add(`${type.name}Input`);
789
+ break;
790
+ case "array":
791
+ collectInputTypeRefs(type.item, out, modelsWithInput);
792
+ break;
793
+ case "tuple":
794
+ type.items.forEach((i) => collectInputTypeRefs(i, out, modelsWithInput));
795
+ break;
796
+ case "record":
797
+ collectInputTypeRefs(type.key, out, modelsWithInput);
798
+ collectInputTypeRefs(type.value, out, modelsWithInput);
799
+ break;
800
+ case "union":
801
+ type.members.forEach((m) => collectInputTypeRefs(m, out, modelsWithInput));
802
+ break;
803
+ case "discriminatedUnion":
804
+ type.members.forEach((m) => collectInputTypeRefs(m, out, modelsWithInput));
805
+ break;
806
+ case "intersection":
807
+ type.members.forEach((m) => collectInputTypeRefs(m, out, modelsWithInput));
808
+ break;
809
+ case "lazy":
810
+ collectInputTypeRefs(type.inner, out, modelsWithInput);
811
+ break;
812
+ case "inlineObject":
813
+ type.fields.forEach((f) => collectInputTypeRefs(f.type, out, modelsWithInput));
814
+ break;
815
+ }
816
+ }
817
+ __name(collectInputTypeRefs, "collectInputTypeRefs");
818
+ function topoSortModels(models) {
819
+ const localNames = new Set(models.map((m) => m.name));
820
+ const modelMap = new Map(models.map((m) => [
821
+ m.name,
822
+ m
823
+ ]));
824
+ const deps = /* @__PURE__ */ new Map();
825
+ for (const model of models) {
826
+ const refs = /* @__PURE__ */ new Set();
827
+ if (model.bases?.[0] && localNames.has(model.bases?.[0])) refs.add(model.bases?.[0]);
828
+ if (model.type) collectTypeRefs(model.type, refs);
829
+ for (const field of model.fields) {
830
+ collectTypeRefs(field.type, refs);
831
+ }
832
+ const localDeps = /* @__PURE__ */ new Set();
833
+ for (const r of refs) {
834
+ if (localNames.has(r) && r !== model.name) localDeps.add(r);
835
+ }
836
+ deps.set(model.name, localDeps);
837
+ }
838
+ const inDegree = /* @__PURE__ */ new Map();
839
+ for (const name of localNames) inDegree.set(name, 0);
840
+ for (const [, d] of deps) {
841
+ for (const dep of d) {
842
+ inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);
843
+ }
844
+ }
845
+ const remaining = /* @__PURE__ */ new Map();
846
+ for (const [name, d] of deps) {
847
+ remaining.set(name, new Set(d));
848
+ }
849
+ const queue = [];
850
+ for (const name of localNames) {
851
+ if (remaining.get(name).size === 0) queue.push(name);
852
+ }
853
+ const sorted = [];
854
+ while (queue.length > 0) {
855
+ const name = queue.shift();
856
+ sorted.push(modelMap.get(name));
857
+ for (const [other, rem] of remaining) {
858
+ if (rem.delete(name) && rem.size === 0) {
859
+ queue.push(other);
860
+ }
861
+ }
862
+ }
863
+ for (const model of models) {
864
+ if (!sorted.includes(model)) sorted.push(model);
865
+ }
866
+ return sorted;
867
+ }
868
+ __name(topoSortModels, "topoSortModels");
869
+ function resolveImportPath(refName, context) {
870
+ if (context) {
871
+ const refOutPath = context.modelOutPaths.get(refName);
872
+ if (refOutPath) {
873
+ const fromDir = dirname(context.currentOutPath);
874
+ let rel = relative(fromDir, refOutPath);
875
+ rel = rel.replace(/\.ts$/, ".js");
876
+ if (!rel.startsWith(".")) rel = "./" + rel;
877
+ return rel;
878
+ }
879
+ }
880
+ const moduleName = pascalToDotCase(refName);
881
+ return `./${moduleName}.js`;
882
+ }
883
+ __name(resolveImportPath, "resolveImportPath");
884
+ function pascalToDotCase(name) {
885
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1.$2").toLowerCase();
886
+ }
887
+ __name(pascalToDotCase, "pascalToDotCase");
888
+
889
+ // src/codegen-operation.ts
890
+ import { resolveModifiers, resolveSecurity, SECURITY_NONE, classifyContentType } from "@contractkit/core";
891
+
892
+ // src/ts-render.ts
893
+ var JSON_VALUE_TYPE_DECL = "export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };";
894
+ function quoteKey2(name) {
895
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `'${name}'`;
896
+ }
897
+ __name(quoteKey2, "quoteKey");
898
+ function headerNameToProperty(name) {
899
+ const parts = name.split(/[-_]/).filter(Boolean);
900
+ return parts.map((p, i) => {
901
+ const lower = p.toLowerCase();
902
+ return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
903
+ }).join("");
904
+ }
905
+ __name(headerNameToProperty, "headerNameToProperty");
906
+ function renderTsType(type) {
907
+ switch (type.kind) {
908
+ case "scalar":
909
+ return renderTsScalar(type.name);
910
+ case "array": {
911
+ const inner = renderTsType(type.item);
912
+ const needsParens = type.item.kind === "union" || type.item.kind === "discriminatedUnion" || type.item.kind === "intersection" || type.item.kind === "enum";
913
+ return needsParens ? `(${inner})[]` : `${inner}[]`;
914
+ }
915
+ case "tuple":
916
+ return `[${type.items.map(renderTsType).join(", ")}]`;
917
+ case "record":
918
+ return `Record<${renderTsType(type.key)}, ${renderTsType(type.value)}>`;
919
+ case "enum":
920
+ return type.values.map((v) => `'${v}'`).join(" | ");
921
+ case "literal":
922
+ return typeof type.value === "string" ? `'${type.value}'` : String(type.value);
923
+ case "union":
924
+ return type.members.map(renderTsType).join(" | ");
925
+ case "discriminatedUnion":
926
+ return type.members.map(renderTsType).join(" | ");
927
+ case "intersection":
928
+ return type.members.map(renderTsType).join(" & ");
929
+ case "ref":
930
+ return type.name;
931
+ case "lazy":
932
+ return renderTsType(type.inner);
933
+ case "inlineObject":
934
+ return renderTsInlineObject(type.fields);
935
+ default:
936
+ return "unknown";
937
+ }
938
+ }
939
+ __name(renderTsType, "renderTsType");
940
+ function renderTsScalar(name) {
941
+ switch (name) {
942
+ case "string":
943
+ case "email":
944
+ case "url":
945
+ case "uuid":
946
+ return "string";
947
+ case "number":
948
+ case "int":
949
+ return "number";
950
+ case "bigint":
951
+ return "bigint";
952
+ case "boolean":
953
+ return "boolean";
954
+ case "date":
955
+ case "datetime":
956
+ case "duration":
957
+ case "interval":
958
+ return "string";
959
+ case "null":
960
+ return "null";
961
+ case "unknown":
962
+ return "unknown";
963
+ case "object":
964
+ return "Record<string, unknown>";
965
+ case "binary":
966
+ return "Blob";
967
+ case "json":
968
+ return "JsonValue";
969
+ default:
970
+ return "unknown";
971
+ }
972
+ }
973
+ __name(renderTsScalar, "renderTsScalar");
974
+ function renderTsInlineObject(fields) {
975
+ const entries = fields.map((f) => {
976
+ const opt = f.optional ? "?" : "";
977
+ return `${quoteKey2(f.name)}${opt}: ${renderTsType(f.type)}`;
978
+ });
979
+ return `{ ${entries.join("; ")} }`;
980
+ }
981
+ __name(renderTsInlineObject, "renderTsInlineObject");
982
+ function renderInputTsType(type, modelsWithInput) {
983
+ if (!modelsWithInput || modelsWithInput.size === 0) return renderTsType(type);
984
+ switch (type.kind) {
985
+ case "ref":
986
+ return modelsWithInput.has(type.name) ? `${type.name}Input` : type.name;
987
+ case "array": {
988
+ const inner = renderInputTsType(type.item, modelsWithInput);
989
+ const needsParens = type.item.kind === "union" || type.item.kind === "discriminatedUnion" || type.item.kind === "intersection" || type.item.kind === "enum";
990
+ return needsParens ? `(${inner})[]` : `${inner}[]`;
991
+ }
992
+ case "intersection":
993
+ return type.members.map((m) => renderInputTsType(m, modelsWithInput)).join(" & ");
994
+ case "union":
995
+ return type.members.map((m) => renderInputTsType(m, modelsWithInput)).join(" | ");
996
+ case "discriminatedUnion":
997
+ return type.members.map((m) => renderInputTsType(m, modelsWithInput)).join(" | ");
998
+ case "inlineObject":
999
+ return `{ ${type.fields.map((f) => `${quoteKey2(f.name)}${f.optional ? "?" : ""}: ${renderInputTsType(f.type, modelsWithInput)}`).join("; ")} }`;
1000
+ case "lazy":
1001
+ return renderInputTsType(type.inner, modelsWithInput);
1002
+ default:
1003
+ return renderTsType(type);
1004
+ }
1005
+ }
1006
+ __name(renderInputTsType, "renderInputTsType");
1007
+ function renderOutputTsType(type, modelsWithOutput) {
1008
+ if (!modelsWithOutput || modelsWithOutput.size === 0) return renderTsType(type);
1009
+ switch (type.kind) {
1010
+ case "ref":
1011
+ return modelsWithOutput.has(type.name) ? `${type.name}Output` : type.name;
1012
+ case "array": {
1013
+ const inner = renderOutputTsType(type.item, modelsWithOutput);
1014
+ const needsParens = type.item.kind === "union" || type.item.kind === "discriminatedUnion" || type.item.kind === "intersection" || type.item.kind === "enum";
1015
+ return needsParens ? `(${inner})[]` : `${inner}[]`;
1016
+ }
1017
+ case "intersection":
1018
+ return type.members.map((m) => renderOutputTsType(m, modelsWithOutput)).join(" & ");
1019
+ case "union":
1020
+ return type.members.map((m) => renderOutputTsType(m, modelsWithOutput)).join(" | ");
1021
+ case "discriminatedUnion":
1022
+ return type.members.map((m) => renderOutputTsType(m, modelsWithOutput)).join(" | ");
1023
+ case "inlineObject":
1024
+ return `{ ${type.fields.map((f) => `${quoteKey2(f.name)}${f.optional ? "?" : ""}: ${renderOutputTsType(f.type, modelsWithOutput)}`).join("; ")} }`;
1025
+ case "lazy":
1026
+ return renderOutputTsType(type.inner, modelsWithOutput);
1027
+ default:
1028
+ return renderTsType(type);
1029
+ }
1030
+ }
1031
+ __name(renderOutputTsType, "renderOutputTsType");
1032
+
1033
+ // src/codegen-operation.ts
1034
+ import { basename, dirname as dirname2, relative as relative2 } from "path";
1035
+ function bodyParserToken(contentType) {
1036
+ switch (classifyContentType(contentType)) {
1037
+ case "urlencoded":
1038
+ return "urlencoded";
1039
+ case "multipart":
1040
+ return "multipart";
1041
+ case "text":
1042
+ return "text";
1043
+ case "binary":
1044
+ return "text";
1045
+ default:
1046
+ return "json";
1047
+ }
1048
+ }
1049
+ __name(bodyParserToken, "bodyParserToken");
1050
+ function bodyTypesStructurallyEqual(a, b) {
1051
+ if (a.kind !== b.kind) return false;
1052
+ switch (a.kind) {
1053
+ case "scalar": {
1054
+ const bb = b;
1055
+ return a.name === bb.name && a.min === bb.min && a.max === bb.max && a.len === bb.len && a.regex === bb.regex && a.format === bb.format;
1056
+ }
1057
+ case "array": {
1058
+ const bb = b;
1059
+ return a.min === bb.min && a.max === bb.max && bodyTypesStructurallyEqual(a.item, bb.item);
1060
+ }
1061
+ case "tuple": {
1062
+ const bb = b;
1063
+ return a.items.length === bb.items.length && a.items.every((x, i) => bodyTypesStructurallyEqual(x, bb.items[i]));
1064
+ }
1065
+ case "record": {
1066
+ const bb = b;
1067
+ return bodyTypesStructurallyEqual(a.key, bb.key) && bodyTypesStructurallyEqual(a.value, bb.value);
1068
+ }
1069
+ case "enum": {
1070
+ const bb = b;
1071
+ return a.values.length === bb.values.length && a.values.every((v, i) => v === bb.values[i]);
1072
+ }
1073
+ case "literal": {
1074
+ const bb = b;
1075
+ return a.value === bb.value;
1076
+ }
1077
+ case "union":
1078
+ case "intersection": {
1079
+ const bb = b;
1080
+ return a.members.length === bb.members.length && a.members.every((m, i) => bodyTypesStructurallyEqual(m, bb.members[i]));
1081
+ }
1082
+ case "discriminatedUnion": {
1083
+ const bb = b;
1084
+ return a.discriminator === bb.discriminator && a.members.length === bb.members.length && a.members.every((m, i) => bodyTypesStructurallyEqual(m, bb.members[i]));
1085
+ }
1086
+ case "ref": {
1087
+ const bb = b;
1088
+ return a.name === bb.name && !!a.lazy === !!bb.lazy;
1089
+ }
1090
+ case "lazy": {
1091
+ const bb = b;
1092
+ return bodyTypesStructurallyEqual(a.inner, bb.inner);
1093
+ }
1094
+ case "inlineObject": {
1095
+ const bb = b;
1096
+ if (a.mode !== bb.mode) return false;
1097
+ if (a.fields.length !== bb.fields.length) return false;
1098
+ return a.fields.every((f, i) => {
1099
+ const g = bb.fields[i];
1100
+ return f.name === g.name && f.optional === g.optional && f.nullable === g.nullable && f.visibility === g.visibility && f.default === g.default && !!f.deprecated === !!g.deprecated && bodyTypesStructurallyEqual(f.type, g.type);
1101
+ });
1102
+ }
1103
+ }
1104
+ }
1105
+ __name(bodyTypesStructurallyEqual, "bodyTypesStructurallyEqual");
1106
+ function generateOp(root, options = {}) {
1107
+ const types = collectTypes(root, options.modelsWithInput, options.modelsWithOutput);
1108
+ const services = collectServices(root);
1109
+ const routerName = deriveRouterName(root.file);
1110
+ const needsParseAndValidate = routeNeedsValidation(root);
1111
+ const body = [];
1112
+ const needsSignature = fileNeedsSignature(root);
1113
+ const needsSecurity = fileNeedsSecurity(root);
1114
+ const koaImports = [
1115
+ "ServerKitRouter",
1116
+ "bodyParserMiddleware"
1117
+ ];
1118
+ if (needsSecurity) koaImports.push("requireSecurity");
1119
+ if (needsSignature) koaImports.push("requireSignature");
1120
+ body.push(`import { ${koaImports.join(", ")} } from '@maroonedsoftware/koa';`);
1121
+ for (const svc of services) {
1122
+ const modulePath = root.services?.[svc] ?? root.meta[svc] ?? deriveModulePath(svc, options.servicePathTemplate);
1123
+ body.push(`import { ${svc} } from '${modulePath}';`);
1124
+ }
1125
+ if (types.length > 0) {
1126
+ body.push(...generateTypeImports(types, root.file, options));
1127
+ }
1128
+ if (opNeedsDateTime(root)) {
1129
+ body.push(`import { DateTime } from 'luxon';`);
1130
+ }
1131
+ if (needsParseAndValidate) {
1132
+ body.push(`import { parseAndValidate } from '@maroonedsoftware/zod';`);
1133
+ }
1134
+ const helpers = [];
1135
+ if (opNeedsScalar(root, "binary")) {
1136
+ helpers.push(`const _ZodBinary = z.custom<Buffer>((val) => Buffer.isBuffer(val), { error: 'Must be binary data' });`);
1137
+ }
1138
+ if (opNeedsScalar(root, "datetime")) {
1139
+ helpers.push(`const _ZodDatetime = z.preprocess((val) => typeof val === 'string' ? DateTime.fromISO(val) : val, z.custom<DateTime>((val) => val instanceof DateTime && val.isValid, { message: 'Must be in ISO 8601 format' }));`);
1140
+ }
1141
+ if (opNeedsScalar(root, "json")) {
1142
+ helpers.push(`type _JsonValue = string | number | boolean | null | _JsonValue[] | { [key: string]: _JsonValue };`);
1143
+ helpers.push(`const _ZodJson: z.ZodType<_JsonValue> = z.lazy(() => z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(_ZodJson), z.record(z.string(), _ZodJson)]));`);
1144
+ }
1145
+ const lines = [];
1146
+ lines.push("");
1147
+ lines.push("/**");
1148
+ const relFile = options.outPath ? relative2(dirname2(options.outPath), root.file) : root.file;
1149
+ lines.push(` * generated from [${basename(root.file)}](file://./${relFile})`);
1150
+ lines.push("*/");
1151
+ lines.push(`export const ${routerName} = ServerKitRouter();`);
1152
+ lines.push("");
1153
+ const includeInternal = options.includeInternal ?? true;
1154
+ for (const route of root.routes) {
1155
+ for (const op of route.operations) {
1156
+ if (!includeInternal && resolveModifiers(route, op).includes("internal")) continue;
1157
+ lines.push(...generateHandler(route, op, root, options));
1158
+ lines.push("");
1159
+ }
1160
+ }
1161
+ const allContent = [
1162
+ ...body,
1163
+ ...helpers.length ? [
1164
+ "",
1165
+ ...helpers
1166
+ ] : [],
1167
+ ...lines
1168
+ ].join("\n");
1169
+ const needsZod = /\bz\./.test(allContent);
1170
+ return (needsZod ? `import { z } from 'zod';
1171
+ ` : "") + allContent;
1172
+ }
1173
+ __name(generateOp, "generateOp");
1174
+ function generateHandler(route, op, root, options) {
1175
+ const lines = [];
1176
+ const file = root.file;
1177
+ const outPath = options.outPath;
1178
+ const modelsWithInput = options.modelsWithInput;
1179
+ lines.push("/**");
1180
+ const desc = op.description ?? route.description;
1181
+ if (desc) {
1182
+ lines.push(` * ${desc}`);
1183
+ }
1184
+ const relFile = outPath ? relative2(dirname2(outPath), file) : file;
1185
+ lines.push(` * from [${basename(file)}](file://./${relFile}#L${op.loc.line})`);
1186
+ const effectiveSecurity = resolveSecurity(route, op, root);
1187
+ if (effectiveSecurity === SECURITY_NONE) {
1188
+ lines.push(` * anonymous access, no security required`);
1189
+ }
1190
+ const mods = resolveModifiers(route, op);
1191
+ if (mods.includes("internal")) lines.push(` * @internal`);
1192
+ if (mods.includes("deprecated")) lines.push(` * @deprecated`);
1193
+ lines.push("*/");
1194
+ const method = op.method;
1195
+ const path = route.path.replace(/\{(\w+)\}/g, ":$1");
1196
+ const bodies = op.request?.bodies ?? [];
1197
+ const hasBody = bodies.length > 0;
1198
+ const isSingleMultipart = bodies.length === 1 && bodies[0].contentType === "multipart/form-data";
1199
+ const middlewares = [];
1200
+ if (effectiveSecurity !== SECURITY_NONE) {
1201
+ const roles = effectiveSecurity && effectiveSecurity.roles?.length ? `roles: [${effectiveSecurity.roles.map((r) => `'${r}'`).join(", ")}]` : "";
1202
+ middlewares.push(`requireSecurity({ ${roles} })`);
1203
+ }
1204
+ if (hasBody) {
1205
+ const parserTokens = Array.from(new Set(bodies.map((b) => bodyParserToken(b.contentType))));
1206
+ const tokensExpr = parserTokens.map((t) => `'${t}'`).join(", ");
1207
+ middlewares.push(`bodyParserMiddleware([${tokensExpr}])`);
1208
+ }
1209
+ if (op.signature) {
1210
+ middlewares.push(`requireSignature('${op.signature}')`);
1211
+ }
1212
+ const middlewareStr = middlewares.length > 0 ? `, ${middlewares.join(", ")},` : ",";
1213
+ lines.push(`${deriveRouterName(file)}.${method}('${path}'${middlewareStr} async (ctx, next) => {`);
1214
+ lines.push(...generateParamValidation(route.params, "ctx.params", "params", route.paramsMode ?? "strict", "", modelsWithInput));
1215
+ lines.push(...generateParamValidation(op.query, "ctx.query", "query", op.queryMode ?? "strict", "", modelsWithInput));
1216
+ lines.push(...generateParamValidation(op.headers, "ctx.headers", "headers", op.headersMode ?? "strip", "", modelsWithInput));
1217
+ if (hasBody && op.request) {
1218
+ if (isSingleMultipart) {
1219
+ lines.push(` const multipartBody = ctx.body as MultipartBody;`);
1220
+ lines.push("");
1221
+ } else if (bodies.length === 1) {
1222
+ lines.push(` const body = await parseAndValidate(ctx.body, ${renderInputType(bodies[0].bodyType, modelsWithInput)});`);
1223
+ lines.push("");
1224
+ } else if (bodies.every((b) => bodyTypesStructurallyEqual(b.bodyType, bodies[0].bodyType))) {
1225
+ lines.push(` const body = await parseAndValidate(ctx.body, ${renderInputType(bodies[0].bodyType, modelsWithInput)});`);
1226
+ lines.push("");
1227
+ } else {
1228
+ const annotation = bodies.map((b) => b.contentType === "multipart/form-data" ? "MultipartBody" : `z.infer<typeof ${renderInputType(b.bodyType, modelsWithInput)}>`).join(" | ");
1229
+ lines.push(` let body!: ${annotation};`);
1230
+ lines.push(` switch (ctx.request.type) {`);
1231
+ for (const b of bodies) {
1232
+ lines.push(` case '${b.contentType}':`);
1233
+ if (b.contentType === "multipart/form-data") {
1234
+ lines.push(` body = ctx.body as MultipartBody;`);
1235
+ } else {
1236
+ lines.push(` body = await parseAndValidate(ctx.body, ${renderInputType(b.bodyType, modelsWithInput)});`);
1237
+ }
1238
+ lines.push(` break;`);
1239
+ }
1240
+ lines.push(` }`);
1241
+ lines.push("");
1242
+ }
1243
+ }
1244
+ const primaryResponse = op.responses.find((r) => r.bodyType) ?? op.responses[0];
1245
+ const serviceParts = inferService(op, route, file);
1246
+ const respHeaders = primaryResponse?.headers ?? [];
1247
+ const hasRespHeaders = respHeaders.length > 0;
1248
+ const headersAnnotation = hasRespHeaders ? `{ ${respHeaders.map((h) => `${quoteKey2(headerNameToProperty(h.name))}${h.optional ? "?" : ""}: ${renderOutputTsType(h.type, options.modelsWithOutput)}`).join("; ")} }` : "";
1249
+ if (primaryResponse?.bodyType) {
1250
+ const { annotation, prelude } = formatTypeAnnotation(primaryResponse.bodyType, options.modelsWithOutput);
1251
+ if (prelude) {
1252
+ lines.push(` ${prelude}`);
1253
+ }
1254
+ lines.push(` const service = ctx.container.get(${serviceParts.className});`);
1255
+ if (hasRespHeaders) {
1256
+ lines.push(` const result: { body: ${annotation}; headers: ${headersAnnotation} } = await service.${serviceParts.methodName}(${buildArgs(route, op)});`);
1257
+ } else {
1258
+ lines.push(` const result: ${annotation} = await service.${serviceParts.methodName}(${buildArgs(route, op)});`);
1259
+ }
1260
+ } else {
1261
+ lines.push(` const service = ctx.container.get(${serviceParts.className});`);
1262
+ if (hasRespHeaders) {
1263
+ lines.push(` const result: { headers: ${headersAnnotation} } = await service.${serviceParts.methodName}(${buildArgs(route, op)});`);
1264
+ } else {
1265
+ lines.push(` await service.${serviceParts.methodName}(${buildArgs(route, op)});`);
1266
+ }
1267
+ }
1268
+ lines.push("");
1269
+ lines.push(` ctx.status = ${primaryResponse?.statusCode ?? 200};`);
1270
+ if (hasRespHeaders) {
1271
+ for (const h of respHeaders) {
1272
+ const accessor = `result.headers[${JSON.stringify(headerNameToProperty(h.name))}]`;
1273
+ if (h.optional) {
1274
+ lines.push(` if (${accessor} !== undefined) ctx.set('${h.name}', String(${accessor}));`);
1275
+ } else {
1276
+ lines.push(` ctx.set('${h.name}', String(${accessor}));`);
1277
+ }
1278
+ }
1279
+ }
1280
+ if (primaryResponse?.bodyType && primaryResponse.contentType) {
1281
+ lines.push(` ctx.type = '${primaryResponse.contentType}';`);
1282
+ lines.push(` ctx.body = ${hasRespHeaders ? "result.body" : "result"};`);
1283
+ }
1284
+ lines.push("");
1285
+ lines.push(` await next();`);
1286
+ lines.push(`});`);
1287
+ return lines;
1288
+ }
1289
+ __name(generateHandler, "generateHandler");
1290
+ function inferService(op, route, file) {
1291
+ if (op.service) {
1292
+ const [cls = "", method] = op.service.split(".");
1293
+ return {
1294
+ className: cls,
1295
+ methodName: method ?? "handle"
1296
+ };
1297
+ }
1298
+ const baseName = deriveBaseName(file);
1299
+ const className = `${baseName}Service`;
1300
+ const methodName = inferMethodName(op.method, route.path);
1301
+ return {
1302
+ className,
1303
+ methodName
1304
+ };
1305
+ }
1306
+ __name(inferService, "inferService");
1307
+ function inferMethodName(method, path) {
1308
+ const hasParam = path.includes("{");
1309
+ switch (method) {
1310
+ case "get":
1311
+ return hasParam ? "getById" : "list";
1312
+ case "post":
1313
+ return "create";
1314
+ case "put":
1315
+ return "replace";
1316
+ case "patch":
1317
+ return "update";
1318
+ case "delete":
1319
+ return "delete";
1320
+ default:
1321
+ return "handle";
1322
+ }
1323
+ }
1324
+ __name(inferMethodName, "inferMethodName");
1325
+ function buildArgs(route, op) {
1326
+ const args = [];
1327
+ if (route.params) {
1328
+ if (route.params.kind === "params") {
1329
+ args.push(...route.params.nodes.map((p) => p.name));
1330
+ } else {
1331
+ args.push("params");
1332
+ }
1333
+ }
1334
+ if (op.request && op.request.bodies.length > 0) {
1335
+ const bodies = op.request.bodies;
1336
+ const isSingleMultipart = bodies.length === 1 && bodies[0].contentType === "multipart/form-data";
1337
+ args.push(isSingleMultipart ? "multipartBody" : "body");
1338
+ }
1339
+ if (op.query) args.push("query");
1340
+ if (op.headers) args.push("headers");
1341
+ return args.join(", ");
1342
+ }
1343
+ __name(buildArgs, "buildArgs");
1344
+ function formatTypeAnnotation(bodyType, modelsWithOutput) {
1345
+ if (bodyType.kind === "array") {
1346
+ const inner = formatTypeAnnotation(bodyType.item, modelsWithOutput);
1347
+ return {
1348
+ annotation: `${inner.annotation}[]`,
1349
+ prelude: inner.prelude
1350
+ };
1351
+ }
1352
+ if (bodyType.kind === "ref") {
1353
+ const name = modelsWithOutput?.has(bodyType.name) ? `${bodyType.name}Output` : bodyType.name;
1354
+ return {
1355
+ annotation: name
1356
+ };
1357
+ }
1358
+ if (bodyType.kind === "scalar") return {
1359
+ annotation: bodyType.name
1360
+ };
1361
+ const schema = renderType(bodyType);
1362
+ return {
1363
+ annotation: "z.infer<typeof resultType>",
1364
+ prelude: `const resultType = ${schema};`
1365
+ };
1366
+ }
1367
+ __name(formatTypeAnnotation, "formatTypeAnnotation");
1368
+ function generateParamValidation(source, ctxExpr, varName, mode, suffix = "", modelsWithInput) {
1369
+ if (!source) return [];
1370
+ const lines = [];
1371
+ const isQuery = ctxExpr === "ctx.query";
1372
+ if (source.kind === "ref") {
1373
+ const typeName = modelsWithInput?.has(source.name) ? `${source.name}Input` : source.name;
1374
+ lines.push(` const ${varName} = await parseAndValidate(${ctxExpr}, ${typeName}.${mode}());`);
1375
+ lines.push("");
1376
+ } else if (source.kind === "params") {
1377
+ if (source.nodes.length > 0) {
1378
+ const lhs = varName === "params" ? `{ ${source.nodes.map((p) => p.name).join(", ")} }` : varName;
1379
+ lines.push(` const ${lhs} = await parseAndValidate(`);
1380
+ lines.push(` ${ctxExpr},`);
1381
+ lines.push(` ${modeToWrapper(mode)}({`);
1382
+ for (const param of source.nodes) {
1383
+ const key = isValidIdentifier2(param.name) ? param.name : `'${param.name}'`;
1384
+ if (isQuery && param.type.kind === "array") {
1385
+ const inner = renderType(param.type);
1386
+ lines.push(` ${key}: z.preprocess((v) => typeof v === 'string' ? v.split(',') : v, ${inner}),`);
1387
+ } else {
1388
+ lines.push(` ${key}: ${renderType(param.type)},`);
1389
+ }
1390
+ }
1391
+ lines.push(` })${suffix},`);
1392
+ lines.push(` );`);
1393
+ lines.push("");
1394
+ }
1395
+ } else {
1396
+ const schema = isQuery ? renderQueryType(source.node, modelsWithInput) : renderInputType(source.node, modelsWithInput);
1397
+ lines.push(` const ${varName} = await parseAndValidate(${ctxExpr}, (${schema}).${mode}());`);
1398
+ lines.push("");
1399
+ }
1400
+ return lines;
1401
+ }
1402
+ __name(generateParamValidation, "generateParamValidation");
1403
+ function generateTypeImports(types, opFile, options) {
1404
+ const lines = [];
1405
+ const { modelOutPaths, outPath } = options;
1406
+ if (modelOutPaths && outPath) {
1407
+ const byFile = /* @__PURE__ */ new Map();
1408
+ const unresolved = [];
1409
+ for (const type of types) {
1410
+ const typeOutPath = modelOutPaths.get(type);
1411
+ if (typeOutPath) {
1412
+ const group = byFile.get(typeOutPath) ?? [];
1413
+ group.push(type);
1414
+ byFile.set(typeOutPath, group);
1415
+ } else {
1416
+ unresolved.push(type);
1417
+ }
1418
+ }
1419
+ const fromDir = dirname2(outPath);
1420
+ for (const [typeOutPath, names] of byFile) {
1421
+ let rel = relative2(fromDir, typeOutPath);
1422
+ rel = rel.replace(/\.ts$/, ".js");
1423
+ if (!rel.startsWith(".")) rel = "./" + rel;
1424
+ lines.push(`import { ${names.sort().join(", ")} } from '${rel}';`);
1425
+ }
1426
+ for (const type of unresolved) {
1427
+ const moduleName = pascalToDotCase(type);
1428
+ lines.push(`import { ${type} } from './${moduleName}.js';`);
1429
+ }
1430
+ } else {
1431
+ const typeImport = deriveTypeImportPath(opFile, options.typeImportPathTemplate);
1432
+ lines.push(`import { ${types.join(", ")} } from '${typeImport}';`);
1433
+ }
1434
+ return lines;
1435
+ }
1436
+ __name(generateTypeImports, "generateTypeImports");
1437
+ function collectTypes(root, modelsWithInput, modelsWithOutput) {
1438
+ const types = /* @__PURE__ */ new Set();
1439
+ for (const route of root.routes) {
1440
+ collectParamSourceRefs(route.params, types);
1441
+ collectParamSourceInputRefs(route.params, types, modelsWithInput);
1442
+ for (const op of route.operations) {
1443
+ if (op.request) {
1444
+ for (const body of op.request.bodies) {
1445
+ collectTypeNodeRefs(body.bodyType, types);
1446
+ collectInputTypeNodeRefs(body.bodyType, types, modelsWithInput);
1447
+ }
1448
+ }
1449
+ for (const resp of op.responses) {
1450
+ if (resp.bodyType) {
1451
+ collectTypeNodeRefs(resp.bodyType, types);
1452
+ collectOutputTypeNodeRefs(resp.bodyType, types, modelsWithOutput);
1453
+ }
1454
+ if (resp.headers) {
1455
+ for (const h of resp.headers) {
1456
+ collectTypeNodeRefs(h.type, types);
1457
+ collectOutputTypeNodeRefs(h.type, types, modelsWithOutput);
1458
+ }
1459
+ }
1460
+ }
1461
+ collectParamSourceRefs(op.query, types);
1462
+ collectParamSourceInputRefs(op.query, types, modelsWithInput);
1463
+ collectParamSourceRefs(op.headers, types);
1464
+ collectParamSourceInputRefs(op.headers, types, modelsWithInput);
1465
+ }
1466
+ }
1467
+ return [
1468
+ ...types
1469
+ ].sort();
1470
+ }
1471
+ __name(collectTypes, "collectTypes");
1472
+ function collectOutputTypeNodeRefs(type, out, modelsWithOutput) {
1473
+ if (!modelsWithOutput) return;
1474
+ switch (type.kind) {
1475
+ case "ref":
1476
+ if (modelsWithOutput.has(type.name)) out.add(`${type.name}Output`);
1477
+ break;
1478
+ case "array":
1479
+ collectOutputTypeNodeRefs(type.item, out, modelsWithOutput);
1480
+ break;
1481
+ case "tuple":
1482
+ type.items.forEach((t) => collectOutputTypeNodeRefs(t, out, modelsWithOutput));
1483
+ break;
1484
+ case "record":
1485
+ collectOutputTypeNodeRefs(type.key, out, modelsWithOutput);
1486
+ collectOutputTypeNodeRefs(type.value, out, modelsWithOutput);
1487
+ break;
1488
+ case "union":
1489
+ type.members.forEach((t) => collectOutputTypeNodeRefs(t, out, modelsWithOutput));
1490
+ break;
1491
+ case "discriminatedUnion":
1492
+ type.members.forEach((t) => collectOutputTypeNodeRefs(t, out, modelsWithOutput));
1493
+ break;
1494
+ case "intersection":
1495
+ type.members.forEach((t) => collectOutputTypeNodeRefs(t, out, modelsWithOutput));
1496
+ break;
1497
+ case "lazy":
1498
+ collectOutputTypeNodeRefs(type.inner, out, modelsWithOutput);
1499
+ break;
1500
+ case "inlineObject":
1501
+ type.fields.forEach((f) => collectOutputTypeNodeRefs(f.type, out, modelsWithOutput));
1502
+ break;
1503
+ }
1504
+ }
1505
+ __name(collectOutputTypeNodeRefs, "collectOutputTypeNodeRefs");
1506
+ function collectParamSourceRefs(source, out) {
1507
+ if (!source) return;
1508
+ if (source.kind === "ref") {
1509
+ if (/^[A-Z]/.test(source.name)) out.add(source.name);
1510
+ } else if (source.kind === "params") {
1511
+ for (const param of source.nodes) {
1512
+ collectTypeNodeRefs(param.type, out);
1513
+ }
1514
+ } else {
1515
+ collectTypeNodeRefs(source.node, out);
1516
+ }
1517
+ }
1518
+ __name(collectParamSourceRefs, "collectParamSourceRefs");
1519
+ function collectParamSourceInputRefs(source, out, modelsWithInput) {
1520
+ if (!source || !modelsWithInput) return;
1521
+ if (source.kind === "ref") {
1522
+ if (modelsWithInput.has(source.name)) out.add(`${source.name}Input`);
1523
+ } else if (source.kind === "type") {
1524
+ collectInputTypeNodeRefs(source.node, out, modelsWithInput);
1525
+ }
1526
+ }
1527
+ __name(collectParamSourceInputRefs, "collectParamSourceInputRefs");
1528
+ function collectInputTypeNodeRefs(type, out, modelsWithInput) {
1529
+ if (!modelsWithInput) return;
1530
+ switch (type.kind) {
1531
+ case "ref":
1532
+ if (modelsWithInput.has(type.name)) out.add(`${type.name}Input`);
1533
+ break;
1534
+ case "array":
1535
+ collectInputTypeNodeRefs(type.item, out, modelsWithInput);
1536
+ break;
1537
+ case "tuple":
1538
+ type.items.forEach((t) => collectInputTypeNodeRefs(t, out, modelsWithInput));
1539
+ break;
1540
+ case "record":
1541
+ collectInputTypeNodeRefs(type.key, out, modelsWithInput);
1542
+ collectInputTypeNodeRefs(type.value, out, modelsWithInput);
1543
+ break;
1544
+ case "union":
1545
+ type.members.forEach((t) => collectInputTypeNodeRefs(t, out, modelsWithInput));
1546
+ break;
1547
+ case "discriminatedUnion":
1548
+ type.members.forEach((t) => collectInputTypeNodeRefs(t, out, modelsWithInput));
1549
+ break;
1550
+ case "intersection":
1551
+ type.members.forEach((t) => collectInputTypeNodeRefs(t, out, modelsWithInput));
1552
+ break;
1553
+ case "lazy":
1554
+ collectInputTypeNodeRefs(type.inner, out, modelsWithInput);
1555
+ break;
1556
+ case "inlineObject":
1557
+ type.fields.forEach((f) => collectInputTypeNodeRefs(f.type, out, modelsWithInput));
1558
+ break;
1559
+ }
1560
+ }
1561
+ __name(collectInputTypeNodeRefs, "collectInputTypeNodeRefs");
1562
+ function collectTypeNodeRefs(type, out) {
1563
+ switch (type.kind) {
1564
+ case "ref":
1565
+ if (/^[A-Z]/.test(type.name)) out.add(type.name);
1566
+ break;
1567
+ case "array":
1568
+ collectTypeNodeRefs(type.item, out);
1569
+ break;
1570
+ case "tuple":
1571
+ type.items.forEach((t) => collectTypeNodeRefs(t, out));
1572
+ break;
1573
+ case "record":
1574
+ collectTypeNodeRefs(type.key, out);
1575
+ collectTypeNodeRefs(type.value, out);
1576
+ break;
1577
+ case "union":
1578
+ type.members.forEach((t) => collectTypeNodeRefs(t, out));
1579
+ break;
1580
+ case "discriminatedUnion":
1581
+ type.members.forEach((t) => collectTypeNodeRefs(t, out));
1582
+ break;
1583
+ case "intersection":
1584
+ type.members.forEach((t) => collectTypeNodeRefs(t, out));
1585
+ break;
1586
+ case "lazy":
1587
+ collectTypeNodeRefs(type.inner, out);
1588
+ break;
1589
+ case "inlineObject":
1590
+ type.fields.forEach((f) => collectTypeNodeRefs(f.type, out));
1591
+ break;
1592
+ }
1593
+ }
1594
+ __name(collectTypeNodeRefs, "collectTypeNodeRefs");
1595
+ function paramSourceNeedsDateTime(source) {
1596
+ if (!source) return false;
1597
+ if (source.kind === "ref") return false;
1598
+ if (source.kind === "params") return source.nodes.some((p) => typeNeedsDateTime(p.type));
1599
+ return typeNeedsDateTime(source.node);
1600
+ }
1601
+ __name(paramSourceNeedsDateTime, "paramSourceNeedsDateTime");
1602
+ function opNeedsDateTime(root) {
1603
+ return root.routes.some((route) => paramSourceNeedsDateTime(route.params) || route.operations.some((op) => !!op.request?.bodies.some((b) => typeNeedsDateTime(b.bodyType)) || op.responses.some((r) => r.bodyType && typeNeedsDateTime(r.bodyType)) || paramSourceNeedsDateTime(op.query) || paramSourceNeedsDateTime(op.headers)));
1604
+ }
1605
+ __name(opNeedsDateTime, "opNeedsDateTime");
1606
+ function paramSourceNeedsScalar(source, name) {
1607
+ if (!source) return false;
1608
+ if (source.kind === "ref") return false;
1609
+ if (source.kind === "params") return source.nodes.some((p) => typeNeedsScalar(p.type, name));
1610
+ return typeNeedsScalar(source.node, name);
1611
+ }
1612
+ __name(paramSourceNeedsScalar, "paramSourceNeedsScalar");
1613
+ function opNeedsScalar(root, name) {
1614
+ return root.routes.some((route) => paramSourceNeedsScalar(route.params, name) || route.operations.some((op) => !!op.request?.bodies.some((b) => typeNeedsScalar(b.bodyType, name)) || op.responses.some((r) => r.bodyType && typeNeedsScalar(r.bodyType, name)) || paramSourceNeedsScalar(op.query, name) || paramSourceNeedsScalar(op.headers, name)));
1615
+ }
1616
+ __name(opNeedsScalar, "opNeedsScalar");
1617
+ function collectServices(root) {
1618
+ const services = /* @__PURE__ */ new Set();
1619
+ const inferredService = `${deriveBaseName(root.file)}Service`;
1620
+ for (const route of root.routes) {
1621
+ for (const op of route.operations) {
1622
+ if (op.service) {
1623
+ services.add(op.service.split(".")[0] ?? op.service);
1624
+ } else {
1625
+ services.add(inferredService);
1626
+ }
1627
+ }
1628
+ }
1629
+ return [
1630
+ ...services
1631
+ ].sort();
1632
+ }
1633
+ __name(collectServices, "collectServices");
1634
+ function hasParamSource(source) {
1635
+ if (!source) return false;
1636
+ if (source.kind === "ref") return true;
1637
+ if (source.kind === "params") return source.nodes.length > 0;
1638
+ return true;
1639
+ }
1640
+ __name(hasParamSource, "hasParamSource");
1641
+ function routeNeedsValidation(root) {
1642
+ return root.routes.some((r) => hasParamSource(r.params) || r.operations.some((op) => !!op.request || hasParamSource(op.query) || hasParamSource(op.headers)));
1643
+ }
1644
+ __name(routeNeedsValidation, "routeNeedsValidation");
1645
+ function fileNeedsSecurity(root) {
1646
+ return root.routes.some((route) => route.operations.some((op) => resolveSecurity(route, op, root) !== SECURITY_NONE));
1647
+ }
1648
+ __name(fileNeedsSecurity, "fileNeedsSecurity");
1649
+ function fileNeedsSignature(root) {
1650
+ return root.routes.some((route) => route.operations.some((op) => !!op.signature));
1651
+ }
1652
+ __name(fileNeedsSignature, "fileNeedsSignature");
1653
+ function isValidIdentifier2(name) {
1654
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
1655
+ }
1656
+ __name(isValidIdentifier2, "isValidIdentifier");
1657
+ function deriveBaseName(file) {
1658
+ const base = file.split("/").pop()?.replace(/\.(op|ck)$/, "") ?? "Resource";
1659
+ return base.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
1660
+ }
1661
+ __name(deriveBaseName, "deriveBaseName");
1662
+ function deriveRouterName(file) {
1663
+ return `${deriveBaseName(file)}Router`;
1664
+ }
1665
+ __name(deriveRouterName, "deriveRouterName");
1666
+ function deriveModulePath(serviceName, template) {
1667
+ const base = serviceName.replace(/Service$/, "");
1668
+ const kebab = base.replace(/([A-Z])/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, "");
1669
+ if (template) {
1670
+ return template.replace(/\{name\}/g, base).replace(/\{kebab\}/g, kebab);
1671
+ }
1672
+ return `#modules/${kebab}/${kebab}.service.js`;
1673
+ }
1674
+ __name(deriveModulePath, "deriveModulePath");
1675
+ function deriveTypeImportPath(file, template) {
1676
+ const base = file.split("/").pop()?.replace(/\.(op|ck)$/, "") ?? "resource";
1677
+ const module = base.split(".")[0] ?? base;
1678
+ if (template) {
1679
+ return template.replace(/\{module\}/g, module).replace(/\{base\}/g, base);
1680
+ }
1681
+ return `#modules/${module}/types/index.js`;
1682
+ }
1683
+ __name(deriveTypeImportPath, "deriveTypeImportPath");
1684
+
1685
+ // src/codegen-sdk.ts
1686
+ import { resolveModifiers as resolveModifiers2, isJsonMime, classifyContentType as classifyContentType2 } from "@contractkit/core";
1687
+ import { basename as basename2, dirname as dirname3, relative as relative3 } from "path";
1688
+ function jsonOrFormSerialize(varName, contentType) {
1689
+ if (contentType === "application/x-www-form-urlencoded") {
1690
+ return `new URLSearchParams(${varName} as unknown as Record<string, string>).toString()`;
1691
+ }
1692
+ if (contentType === "multipart/form-data") {
1693
+ return `(${varName} as FormData)`;
1694
+ }
1695
+ return `JSON.stringify(${varName}, bigIntReplacer)`;
1696
+ }
1697
+ __name(jsonOrFormSerialize, "jsonOrFormSerialize");
1698
+ function renderSerializeExpr(varName, bodies, ctVar) {
1699
+ const arms = bodies.slice(0, -1);
1700
+ const last = bodies[bodies.length - 1];
1701
+ let expr = jsonOrFormSerialize(varName, last.contentType);
1702
+ for (let i = arms.length - 1; i >= 0; i--) {
1703
+ const arm = arms[i];
1704
+ expr = `${ctVar} === '${arm.contentType}' ? ${jsonOrFormSerialize(varName, arm.contentType)} : ${expr}`;
1705
+ }
1706
+ return expr;
1707
+ }
1708
+ __name(renderSerializeExpr, "renderSerializeExpr");
1709
+ function classifyBodyStrategy(op) {
1710
+ const bodies = op.request?.bodies ?? [];
1711
+ if (bodies.length === 0) return {
1712
+ kind: "none"
1713
+ };
1714
+ if (bodies.length === 1) return {
1715
+ kind: "single",
1716
+ body: bodies[0]
1717
+ };
1718
+ if (bodies.every((b) => bodyTypesStructurallyEqual(b.bodyType, bodies[0].bodyType))) {
1719
+ return {
1720
+ kind: "multi-equal",
1721
+ bodies
1722
+ };
1723
+ }
1724
+ if (bodies.some((b) => b.contentType === "multipart/form-data")) {
1725
+ return {
1726
+ kind: "multi-formdata-detect",
1727
+ bodies
1728
+ };
1729
+ }
1730
+ return {
1731
+ kind: "multi-required-arg",
1732
+ bodies
1733
+ };
1734
+ }
1735
+ __name(classifyBodyStrategy, "classifyBodyStrategy");
1736
+ function hasPublicOperations(root, includeInternal = false) {
1737
+ for (const route of root.routes) {
1738
+ for (const op of route.operations) {
1739
+ if (includeInternal || !resolveModifiers2(route, op).includes("internal")) return true;
1740
+ }
1741
+ }
1742
+ return false;
1743
+ }
1744
+ __name(hasPublicOperations, "hasPublicOperations");
1745
+ function generateSdk(root, options = {}) {
1746
+ const lines = [];
1747
+ const includeInternal = options.includeInternal ?? false;
1748
+ const types = collectTypes2(root, options.modelsWithInput, options.modelsWithOutput, includeInternal);
1749
+ const clientClassName = deriveClientClassName(root.file);
1750
+ if (types.length > 0) {
1751
+ lines.push(...generateTypeImports2(types, root.file, options));
1752
+ }
1753
+ if (options.sdkOptionsPath && options.outPath) {
1754
+ let rel = relative3(dirname3(options.outPath), options.sdkOptionsPath);
1755
+ rel = rel.replace(/\.ts$/, ".js");
1756
+ if (!rel.startsWith(".")) rel = "./" + rel;
1757
+ const jsonImport = sdkNeedsJson(root, includeInternal) ? ", JsonValue" : "";
1758
+ lines.push(`import type { SdkFetch${jsonImport} } from '${rel}';`);
1759
+ const valueImports = [];
1760
+ if (sdkNeedsBigIntReplacer(root, includeInternal)) valueImports.push("bigIntReplacer");
1761
+ if (sdkNeedsBigIntReviver(root, includeInternal)) valueImports.push("parseJson");
1762
+ if (sdkNeedsQueryString(root, includeInternal)) valueImports.push("buildQueryString");
1763
+ if (valueImports.length > 0) {
1764
+ lines.push(`import { ${valueImports.join(", ")} } from '${rel}';`);
1765
+ }
1766
+ } else {
1767
+ lines.push("");
1768
+ lines.push("export class SdkError extends Error {");
1769
+ lines.push(" constructor(");
1770
+ lines.push(" public readonly status: number,");
1771
+ lines.push(" public readonly statusText: string,");
1772
+ lines.push(" public readonly body: unknown,");
1773
+ lines.push(" ) {");
1774
+ lines.push(" super(`${status} ${statusText}`);");
1775
+ lines.push(" this.name = 'SdkError';");
1776
+ lines.push(" }");
1777
+ lines.push("}");
1778
+ lines.push("");
1779
+ lines.push("export type SdkFetch = (url: string, init: RequestInit) => Promise<Response>;");
1780
+ lines.push("");
1781
+ lines.push("export interface SdkOptions {");
1782
+ lines.push(" baseUrl: string;");
1783
+ lines.push(" headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);");
1784
+ lines.push(" fetch?: SdkFetch;");
1785
+ lines.push(" /** Called once per request to produce a unique X-Request-ID header value */");
1786
+ lines.push(" requestIdFactory?: () => string;");
1787
+ lines.push("}");
1788
+ lines.push("");
1789
+ lines.push("export function createSdkFetch(options: SdkOptions): SdkFetch {");
1790
+ lines.push(" const getRequestId = options.requestIdFactory ?? (() => crypto.randomUUID());");
1791
+ lines.push(" return async (url: string, init: RequestInit): Promise<Response> => {");
1792
+ lines.push(" const baseHeaders = typeof options.headers === 'function'");
1793
+ lines.push(" ? await options.headers()");
1794
+ lines.push(" : options.headers ?? {};");
1795
+ lines.push(" const res = await fetch(`${options.baseUrl}${url}`, {");
1796
+ lines.push(" ...init,");
1797
+ lines.push(" headers: { ...baseHeaders, 'X-Request-ID': getRequestId(), ...init.headers as Record<string, string> },");
1798
+ lines.push(" });");
1799
+ lines.push(" if (!res.ok) {");
1800
+ lines.push(" const text = await res.text();");
1801
+ lines.push(" let body: unknown;");
1802
+ lines.push(" try { body = JSON.parse(text); } catch { body = text; }");
1803
+ lines.push(" throw new SdkError(res.status, res.statusText, body);");
1804
+ lines.push(" }");
1805
+ lines.push(" return res;");
1806
+ lines.push(" };");
1807
+ lines.push("}");
1808
+ lines.push("");
1809
+ lines.push("export function buildQueryString(query: object | undefined): string {");
1810
+ lines.push(" const searchParams = new URLSearchParams();");
1811
+ lines.push(" if (query) {");
1812
+ lines.push(" for (const [k, v] of Object.entries(query)) {");
1813
+ lines.push(" if (v === undefined || v === null) continue;");
1814
+ lines.push(" if (Array.isArray(v)) { for (const item of v) searchParams.append(k, String(item)); }");
1815
+ lines.push(" else searchParams.set(k, String(v));");
1816
+ lines.push(" }");
1817
+ lines.push(" }");
1818
+ lines.push(" const qs = searchParams.toString();");
1819
+ lines.push(" return qs ? `?${qs}` : '';");
1820
+ lines.push("}");
1821
+ lines.push("");
1822
+ lines.push("export async function parseJson<T>(res: Response): Promise<T> {");
1823
+ lines.push(" return JSON.parse(await res.text(), bigIntReviver) as T;");
1824
+ lines.push("}");
1825
+ }
1826
+ if (sdkNeedsJson(root, includeInternal) && !(options.sdkOptionsPath && options.outPath)) {
1827
+ lines.push(JSON_VALUE_TYPE_DECL);
1828
+ }
1829
+ lines.push("");
1830
+ lines.push("/**");
1831
+ const relFile = options.outPath ? relative3(dirname3(options.outPath), root.file) : root.file;
1832
+ lines.push(` * generated from [${basename2(root.file)}](file://./${relFile})`);
1833
+ lines.push(" */");
1834
+ lines.push(`export class ${clientClassName} {`);
1835
+ lines.push(" constructor(private fetch: SdkFetch) {}");
1836
+ for (const route of root.routes) {
1837
+ for (const op of route.operations) {
1838
+ const mods = resolveModifiers2(route, op);
1839
+ if (!includeInternal && mods.includes("internal")) continue;
1840
+ lines.push("");
1841
+ if (mods.includes("deprecated")) lines.push(" /** @deprecated */");
1842
+ lines.push(...generateMethod(route, op, root.file, options));
1843
+ }
1844
+ }
1845
+ lines.push("}");
1846
+ lines.push("");
1847
+ return lines.join("\n");
1848
+ }
1849
+ __name(generateSdk, "generateSdk");
1850
+ function generateMethod(route, op, file, options) {
1851
+ const lines = [];
1852
+ const methodName = deriveMethodName(op, route);
1853
+ const httpMethod = op.method.toUpperCase();
1854
+ const { modelsWithInput, modelsWithOutput } = options;
1855
+ const params = buildMethodParams(route, op, modelsWithInput);
1856
+ const paramStr = params.map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ");
1857
+ const primaryResponse = op.responses.find((r) => r.bodyType) ?? op.responses[0];
1858
+ const isVoid = !primaryResponse?.bodyType;
1859
+ const respCategory = primaryResponse?.contentType ? classifyContentType2(primaryResponse.contentType) : "json";
1860
+ const dataType = isVoid ? "void" : respCategory === "text" ? "string" : respCategory === "binary" ? "Blob" : renderOutputTsType(primaryResponse.bodyType, modelsWithOutput);
1861
+ const respHeaders = primaryResponse?.headers ?? [];
1862
+ const hasRespHeaders = respHeaders.length > 0;
1863
+ const headersShape = hasRespHeaders ? `{ ${respHeaders.map((h) => `${quoteKey2(headerNameToProperty(h.name))}${h.optional ? "?" : ""}: ${renderOutputTsType(h.type, modelsWithOutput)}`).join("; ")} }` : "";
1864
+ const returnType = hasRespHeaders ? isVoid ? `{ headers: ${headersShape} }` : `{ data: ${dataType}; headers: ${headersShape} }` : dataType;
1865
+ const desc = op.description ?? route.description;
1866
+ if (op.name || desc) {
1867
+ const tags = [];
1868
+ if (op.name) tags.push(`@name ${op.name}`);
1869
+ if (desc) tags.push(`@description ${desc}`);
1870
+ if (tags.length === 1) {
1871
+ lines.push(` /** ${tags[0]} */`);
1872
+ } else {
1873
+ lines.push(` /**`);
1874
+ for (const tag of tags) lines.push(` * ${tag}`);
1875
+ lines.push(` */`);
1876
+ }
1877
+ }
1878
+ lines.push(` async ${methodName}(${paramStr}): Promise<${returnType}> {`);
1879
+ const urlExpr = buildUrlExpression(route.path, route.params);
1880
+ const hasQuery = !!op.query;
1881
+ let fetchUrl = urlExpr;
1882
+ if (hasQuery) {
1883
+ lines.push(` const qs = buildQueryString(query);`);
1884
+ fetchUrl = urlExpr;
1885
+ }
1886
+ const strategy = classifyBodyStrategy(op);
1887
+ const hasBody = strategy.kind !== "none";
1888
+ const hasOpHeaders = !!op.headers;
1889
+ if (strategy.kind === "multi-equal") {
1890
+ const defaultCt = strategy.bodies[0].contentType;
1891
+ lines.push(` const __contentType = options?.contentType ?? '${defaultCt}';`);
1892
+ lines.push(` const __serialized = ${renderSerializeExpr("body", strategy.bodies, "__contentType")};`);
1893
+ } else if (strategy.kind === "multi-formdata-detect") {
1894
+ lines.push(` const __isFormData = body instanceof FormData;`);
1895
+ const nonMultipart = strategy.bodies.find((b) => b.contentType !== "multipart/form-data");
1896
+ lines.push(` const __contentType: string = __isFormData ? 'multipart/form-data' : '${nonMultipart.contentType}';`);
1897
+ lines.push(` const __serialized: BodyInit = __isFormData ? (body as FormData) : ${jsonOrFormSerialize("body", nonMultipart.contentType)};`);
1898
+ } else if (strategy.kind === "multi-required-arg") {
1899
+ lines.push(` const __contentType = options.contentType;`);
1900
+ lines.push(` const __serialized = ${renderSerializeExpr("body", strategy.bodies, "__contentType")};`);
1901
+ }
1902
+ const fetchArgs = [];
1903
+ if (hasQuery) {
1904
+ fetchArgs.push(`url: \`${fetchUrl}\${qs}\``);
1905
+ } else {
1906
+ fetchArgs.push(`url: \`${fetchUrl}\``);
1907
+ }
1908
+ fetchArgs.push(`method: '${httpMethod}'`);
1909
+ if (strategy.kind === "single") {
1910
+ const body = strategy.body;
1911
+ const cat = classifyContentType2(body.contentType);
1912
+ if (cat === "multipart") {
1913
+ fetchArgs.push("body: body");
1914
+ } else if (cat === "urlencoded") {
1915
+ fetchArgs.push(`headers: { 'Content-Type': '${body.contentType}' }`);
1916
+ fetchArgs.push("body: new URLSearchParams(body as unknown as Record<string, string>).toString()");
1917
+ } else if (cat === "text" || cat === "binary") {
1918
+ fetchArgs.push(`headers: { 'Content-Type': '${body.contentType}' }`);
1919
+ fetchArgs.push("body: body");
1920
+ } else {
1921
+ fetchArgs.push(`headers: { 'Content-Type': '${body.contentType}' }`);
1922
+ fetchArgs.push("body: JSON.stringify(body, bigIntReplacer)");
1923
+ }
1924
+ } else if (hasBody) {
1925
+ fetchArgs.push(`headers: { 'Content-Type': __contentType }`);
1926
+ fetchArgs.push("body: __serialized");
1927
+ }
1928
+ if (hasOpHeaders) {
1929
+ const lastHeaderIdx = fetchArgs.findIndex((a) => a.startsWith("headers:"));
1930
+ if (lastHeaderIdx !== -1) {
1931
+ const existing = fetchArgs[lastHeaderIdx];
1932
+ const inner = existing.slice("headers: ".length).replace(/^\{\s*|\s*\}$/g, "");
1933
+ fetchArgs[lastHeaderIdx] = `headers: { ${inner}, ...customHeaders }`;
1934
+ } else {
1935
+ fetchArgs.push("headers: customHeaders");
1936
+ }
1937
+ }
1938
+ const resultPrefix = isVoid && !hasRespHeaders ? "" : "const result = ";
1939
+ if (fetchArgs.length === 2 && !hasBody && !hasOpHeaders && !hasQuery) {
1940
+ lines.push(` ${resultPrefix}await this.fetch(\`${fetchUrl}\`, { method: '${httpMethod}' });`);
1941
+ } else {
1942
+ lines.push(` ${resultPrefix}await this.fetch(${fetchArgs[0].split(": ").slice(1).join(": ")}, {`);
1943
+ for (let i = 1; i < fetchArgs.length; i++) {
1944
+ lines.push(` ${fetchArgs[i]},`);
1945
+ }
1946
+ lines.push(` });`);
1947
+ }
1948
+ const readBodyExpr = respCategory === "text" ? `await result.text()` : respCategory === "binary" ? `await result.blob()` : `await parseJson<${dataType}>(result)`;
1949
+ if (hasRespHeaders) {
1950
+ const headerEntries = respHeaders.map((h) => `${quoteKey2(headerNameToProperty(h.name))}: result.headers.get('${h.name}') ?? undefined`).join(", ");
1951
+ if (isVoid) {
1952
+ lines.push(` return { headers: { ${headerEntries} } };`);
1953
+ } else {
1954
+ lines.push(` const data = ${readBodyExpr};`);
1955
+ lines.push(` return { data, headers: { ${headerEntries} } };`);
1956
+ }
1957
+ } else if (!isVoid) {
1958
+ lines.push(` return ${readBodyExpr};`);
1959
+ }
1960
+ lines.push(" }");
1961
+ return lines;
1962
+ }
1963
+ __name(generateMethod, "generateMethod");
1964
+ function buildUrlExpression(path, _) {
1965
+ return path.replace(/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g, (_match, name) => {
1966
+ return `\${encodeURIComponent(${name})}`;
1967
+ });
1968
+ }
1969
+ __name(buildUrlExpression, "buildUrlExpression");
1970
+ function buildMethodParams(route, op, modelsWithInput) {
1971
+ const params = [];
1972
+ if (route.params) {
1973
+ if (route.params.kind === "params") {
1974
+ for (const p of route.params.nodes) {
1975
+ params.push({
1976
+ name: p.name,
1977
+ type: renderInputTsType(p.type, modelsWithInput),
1978
+ optional: false
1979
+ });
1980
+ }
1981
+ } else if (route.params.kind === "ref") {
1982
+ const typeName = modelsWithInput?.has(route.params.name) ? `${route.params.name}Input` : route.params.name;
1983
+ params.push({
1984
+ name: "params",
1985
+ type: typeName,
1986
+ optional: false
1987
+ });
1988
+ } else {
1989
+ params.push({
1990
+ name: "params",
1991
+ type: renderInputTsType(route.params.node, modelsWithInput),
1992
+ optional: false
1993
+ });
1994
+ }
1995
+ }
1996
+ const strategy = classifyBodyStrategy(op);
1997
+ if (strategy.kind === "single") {
1998
+ const body = strategy.body;
1999
+ const cat = classifyContentType2(body.contentType);
2000
+ if (cat === "multipart") {
2001
+ params.push({
2002
+ name: "body",
2003
+ type: "FormData",
2004
+ optional: false
2005
+ });
2006
+ } else if (cat === "text") {
2007
+ params.push({
2008
+ name: "body",
2009
+ type: "string",
2010
+ optional: false
2011
+ });
2012
+ } else if (cat === "binary") {
2013
+ params.push({
2014
+ name: "body",
2015
+ type: "Blob | ArrayBuffer | Uint8Array | string",
2016
+ optional: false
2017
+ });
2018
+ } else {
2019
+ params.push({
2020
+ name: "body",
2021
+ type: renderInputTsType(body.bodyType, modelsWithInput),
2022
+ optional: false
2023
+ });
2024
+ }
2025
+ } else if (strategy.kind === "multi-equal") {
2026
+ const bodies = strategy.bodies;
2027
+ const bodyType = renderInputTsType(bodies[0].bodyType, modelsWithInput);
2028
+ params.push({
2029
+ name: "body",
2030
+ type: bodyType,
2031
+ optional: false
2032
+ });
2033
+ const ctUnion = bodies.map((b) => `'${b.contentType}'`).join(" | ");
2034
+ params.push({
2035
+ name: "options",
2036
+ type: `{ contentType?: ${ctUnion} }`,
2037
+ optional: true
2038
+ });
2039
+ } else if (strategy.kind === "multi-formdata-detect") {
2040
+ const types = strategy.bodies.map((b) => b.contentType === "multipart/form-data" ? "FormData" : renderInputTsType(b.bodyType, modelsWithInput)).join(" | ");
2041
+ params.push({
2042
+ name: "body",
2043
+ type: types,
2044
+ optional: false
2045
+ });
2046
+ } else if (strategy.kind === "multi-required-arg") {
2047
+ const types = strategy.bodies.map((b) => b.contentType === "multipart/form-data" ? "FormData" : renderInputTsType(b.bodyType, modelsWithInput)).join(" | ");
2048
+ params.push({
2049
+ name: "body",
2050
+ type: types,
2051
+ optional: false
2052
+ });
2053
+ const ctUnion = strategy.bodies.map((b) => `'${b.contentType}'`).join(" | ");
2054
+ params.push({
2055
+ name: "options",
2056
+ type: `{ contentType: ${ctUnion} }`,
2057
+ optional: false
2058
+ });
2059
+ }
2060
+ if (op.query) {
2061
+ if (op.query.kind === "params") {
2062
+ const fields = op.query.nodes.map((p) => `${quoteKey2(p.name)}?: ${renderInputTsType(p.type, modelsWithInput)}`).join("; ");
2063
+ params.push({
2064
+ name: "query",
2065
+ type: `{ ${fields} }`,
2066
+ optional: true
2067
+ });
2068
+ } else if (op.query.kind === "ref") {
2069
+ const typeName = modelsWithInput?.has(op.query.name) ? `${op.query.name}Input` : op.query.name;
2070
+ params.push({
2071
+ name: "query",
2072
+ type: typeName,
2073
+ optional: true
2074
+ });
2075
+ } else {
2076
+ params.push({
2077
+ name: "query",
2078
+ type: renderInputTsType(op.query.node, modelsWithInput),
2079
+ optional: true
2080
+ });
2081
+ }
2082
+ }
2083
+ if (op.headers) {
2084
+ if (op.headers.kind === "params") {
2085
+ const fields = op.headers.nodes.map((p) => `${quoteKey2(p.name)}?: ${renderInputTsType(p.type, modelsWithInput)}`).join("; ");
2086
+ params.push({
2087
+ name: "customHeaders",
2088
+ type: `{ ${fields} }`,
2089
+ optional: true
2090
+ });
2091
+ } else if (op.headers.kind === "ref") {
2092
+ const typeName = modelsWithInput?.has(op.headers.name) ? `${op.headers.name}Input` : op.headers.name;
2093
+ params.push({
2094
+ name: "customHeaders",
2095
+ type: typeName,
2096
+ optional: true
2097
+ });
2098
+ } else {
2099
+ params.push({
2100
+ name: "customHeaders",
2101
+ type: renderInputTsType(op.headers.node, modelsWithInput),
2102
+ optional: true
2103
+ });
2104
+ }
2105
+ }
2106
+ return params;
2107
+ }
2108
+ __name(buildMethodParams, "buildMethodParams");
2109
+ function deriveMethodName(op, route) {
2110
+ if (op.sdk) return op.sdk;
2111
+ if (op.name) return nameToMethodName(op.name);
2112
+ return inferMethodName2(op.method, route.path);
2113
+ }
2114
+ __name(deriveMethodName, "deriveMethodName");
2115
+ function nameToMethodName(name) {
2116
+ const parts = name.split(/[\s\-_]+/).filter(Boolean);
2117
+ return parts.map((p, i) => i === 0 ? p.charAt(0).toLowerCase() + p.slice(1) : p.charAt(0).toUpperCase() + p.slice(1)).join("");
2118
+ }
2119
+ __name(nameToMethodName, "nameToMethodName");
2120
+ function inferMethodName2(method, path) {
2121
+ const segments = path.split("/").filter((s) => s.length > 0);
2122
+ const parts = [
2123
+ method.toLowerCase()
2124
+ ];
2125
+ for (const seg of segments) {
2126
+ if (seg.startsWith("{")) {
2127
+ const paramName = seg.slice(1, -1);
2128
+ parts.push("By" + paramName.charAt(0).toUpperCase() + paramName.slice(1));
2129
+ } else {
2130
+ const segParts = seg.split(/[.-]/).filter(Boolean);
2131
+ for (const sp of segParts) {
2132
+ parts.push(sp.charAt(0).toUpperCase() + sp.slice(1));
2133
+ }
2134
+ }
2135
+ }
2136
+ return parts[0] + parts.slice(1).join("");
2137
+ }
2138
+ __name(inferMethodName2, "inferMethodName");
2139
+ function deriveBaseName2(file) {
2140
+ const base = file.split("/").pop()?.replace(/\.(op|ck)$/, "") ?? "Resource";
2141
+ return base.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
2142
+ }
2143
+ __name(deriveBaseName2, "deriveBaseName");
2144
+ function deriveClientClassName(file) {
2145
+ return `${deriveBaseName2(file)}Client`;
2146
+ }
2147
+ __name(deriveClientClassName, "deriveClientClassName");
2148
+ function deriveClientPropertyName(file) {
2149
+ const base = deriveBaseName2(file);
2150
+ return base.charAt(0).toLowerCase() + base.slice(1);
2151
+ }
2152
+ __name(deriveClientPropertyName, "deriveClientPropertyName");
2153
+ function collectTypes2(root, modelsWithInput, modelsWithOutput, includeInternal = false) {
2154
+ const types = /* @__PURE__ */ new Set();
2155
+ for (const route of root.routes) {
2156
+ const publicOps = route.operations.filter((op) => includeInternal || !resolveModifiers2(route, op).includes("internal"));
2157
+ if (publicOps.length === 0) continue;
2158
+ collectParamSourceRefs2(route.params, types);
2159
+ collectParamSourceInputRefs2(route.params, types, modelsWithInput);
2160
+ for (const op of publicOps) {
2161
+ if (op.request) {
2162
+ for (const body of op.request.bodies) {
2163
+ collectTypeNodeRefs2(body.bodyType, types);
2164
+ collectInputTypeNodeRefs2(body.bodyType, types, modelsWithInput);
2165
+ }
2166
+ }
2167
+ for (const resp of op.responses) {
2168
+ if (resp.bodyType) {
2169
+ collectTypeNodeRefs2(resp.bodyType, types);
2170
+ collectOutputTypeNodeRefs2(resp.bodyType, types, modelsWithOutput);
2171
+ }
2172
+ if (resp.headers) {
2173
+ for (const h of resp.headers) {
2174
+ collectTypeNodeRefs2(h.type, types);
2175
+ collectOutputTypeNodeRefs2(h.type, types, modelsWithOutput);
2176
+ }
2177
+ }
2178
+ }
2179
+ collectParamSourceRefs2(op.query, types);
2180
+ collectParamSourceInputRefs2(op.query, types, modelsWithInput);
2181
+ collectParamSourceRefs2(op.headers, types);
2182
+ collectParamSourceInputRefs2(op.headers, types, modelsWithInput);
2183
+ }
2184
+ }
2185
+ return [
2186
+ ...types
2187
+ ].sort();
2188
+ }
2189
+ __name(collectTypes2, "collectTypes");
2190
+ function collectOutputTypeNodeRefs2(type, out, modelsWithOutput) {
2191
+ if (!modelsWithOutput) return;
2192
+ switch (type.kind) {
2193
+ case "ref":
2194
+ if (modelsWithOutput.has(type.name)) out.add(`${type.name}Output`);
2195
+ break;
2196
+ case "array":
2197
+ collectOutputTypeNodeRefs2(type.item, out, modelsWithOutput);
2198
+ break;
2199
+ case "intersection":
2200
+ case "union":
2201
+ case "discriminatedUnion":
2202
+ type.members.forEach((m) => collectOutputTypeNodeRefs2(m, out, modelsWithOutput));
2203
+ break;
2204
+ case "inlineObject":
2205
+ type.fields.forEach((f) => collectOutputTypeNodeRefs2(f.type, out, modelsWithOutput));
2206
+ break;
2207
+ case "lazy":
2208
+ collectOutputTypeNodeRefs2(type.inner, out, modelsWithOutput);
2209
+ break;
2210
+ }
2211
+ }
2212
+ __name(collectOutputTypeNodeRefs2, "collectOutputTypeNodeRefs");
2213
+ function collectParamSourceInputRefs2(source, out, modelsWithInput) {
2214
+ if (!source || !modelsWithInput) return;
2215
+ if (source.kind === "ref") {
2216
+ if (modelsWithInput.has(source.name)) out.add(`${source.name}Input`);
2217
+ } else if (source.kind === "params") {
2218
+ for (const param of source.nodes) {
2219
+ collectInputTypeNodeRefs2(param.type, out, modelsWithInput);
2220
+ }
2221
+ } else {
2222
+ collectInputTypeNodeRefs2(source.node, out, modelsWithInput);
2223
+ }
2224
+ }
2225
+ __name(collectParamSourceInputRefs2, "collectParamSourceInputRefs");
2226
+ function collectInputTypeNodeRefs2(type, out, modelsWithInput) {
2227
+ if (!modelsWithInput) return;
2228
+ switch (type.kind) {
2229
+ case "ref":
2230
+ if (modelsWithInput.has(type.name)) out.add(`${type.name}Input`);
2231
+ break;
2232
+ case "array":
2233
+ collectInputTypeNodeRefs2(type.item, out, modelsWithInput);
2234
+ break;
2235
+ case "intersection":
2236
+ case "union":
2237
+ case "discriminatedUnion":
2238
+ type.members.forEach((m) => collectInputTypeNodeRefs2(m, out, modelsWithInput));
2239
+ break;
2240
+ case "inlineObject":
2241
+ type.fields.forEach((f) => collectInputTypeNodeRefs2(f.type, out, modelsWithInput));
2242
+ break;
2243
+ case "lazy":
2244
+ collectInputTypeNodeRefs2(type.inner, out, modelsWithInput);
2245
+ break;
2246
+ }
2247
+ }
2248
+ __name(collectInputTypeNodeRefs2, "collectInputTypeNodeRefs");
2249
+ function collectParamSourceRefs2(source, out) {
2250
+ if (!source) return;
2251
+ if (source.kind === "ref") {
2252
+ if (/^[A-Z]/.test(source.name)) out.add(source.name);
2253
+ } else if (source.kind === "params") {
2254
+ for (const param of source.nodes) {
2255
+ collectTypeNodeRefs2(param.type, out);
2256
+ }
2257
+ } else {
2258
+ collectTypeNodeRefs2(source.node, out);
2259
+ }
2260
+ }
2261
+ __name(collectParamSourceRefs2, "collectParamSourceRefs");
2262
+ function sdkNeedsQueryString(root, includeInternal = false) {
2263
+ for (const route of root.routes) {
2264
+ for (const op of route.operations) {
2265
+ if (!includeInternal && resolveModifiers2(route, op).includes("internal")) continue;
2266
+ if (op.query) return true;
2267
+ }
2268
+ }
2269
+ return false;
2270
+ }
2271
+ __name(sdkNeedsQueryString, "sdkNeedsQueryString");
2272
+ function sdkNeedsBigIntReplacer(root, includeInternal = false) {
2273
+ for (const route of root.routes) {
2274
+ for (const op of route.operations) {
2275
+ if (!includeInternal && resolveModifiers2(route, op).includes("internal")) continue;
2276
+ if (op.request && op.request.bodies.some((b) => isJsonMime(b.contentType))) return true;
2277
+ }
2278
+ }
2279
+ return false;
2280
+ }
2281
+ __name(sdkNeedsBigIntReplacer, "sdkNeedsBigIntReplacer");
2282
+ function sdkNeedsBigIntReviver(root, includeInternal = false) {
2283
+ for (const route of root.routes) {
2284
+ for (const op of route.operations) {
2285
+ if (!includeInternal && resolveModifiers2(route, op).includes("internal")) continue;
2286
+ if (op.responses.some((r) => {
2287
+ if (!r.bodyType) return false;
2288
+ return !r.contentType || classifyContentType2(r.contentType) === "json";
2289
+ })) {
2290
+ return true;
2291
+ }
2292
+ }
2293
+ }
2294
+ return false;
2295
+ }
2296
+ __name(sdkNeedsBigIntReviver, "sdkNeedsBigIntReviver");
2297
+ function sdkNeedsJson(root, includeInternal = false) {
2298
+ for (const route of root.routes) {
2299
+ for (const op of route.operations) {
2300
+ if (!includeInternal && resolveModifiers2(route, op).includes("internal")) continue;
2301
+ const check = /* @__PURE__ */ __name((src) => {
2302
+ if (!src || src.kind === "ref") return false;
2303
+ if (src.kind === "params") return src.nodes.some((p) => typeNeedsScalar(p.type, "json"));
2304
+ return typeNeedsScalar(src.node, "json");
2305
+ }, "check");
2306
+ if (!!op.request?.bodies.some((b) => typeNeedsScalar(b.bodyType, "json")) || op.responses.some((r) => r.bodyType && typeNeedsScalar(r.bodyType, "json")) || check(op.query) || check(op.headers) || check(route.params)) return true;
2307
+ }
2308
+ }
2309
+ return false;
2310
+ }
2311
+ __name(sdkNeedsJson, "sdkNeedsJson");
2312
+ function collectTypeNodeRefs2(type, out) {
2313
+ switch (type.kind) {
2314
+ case "ref":
2315
+ if (/^[A-Z]/.test(type.name)) out.add(type.name);
2316
+ break;
2317
+ case "array":
2318
+ collectTypeNodeRefs2(type.item, out);
2319
+ break;
2320
+ case "tuple":
2321
+ type.items.forEach((t) => collectTypeNodeRefs2(t, out));
2322
+ break;
2323
+ case "record":
2324
+ collectTypeNodeRefs2(type.key, out);
2325
+ collectTypeNodeRefs2(type.value, out);
2326
+ break;
2327
+ case "union":
2328
+ type.members.forEach((t) => collectTypeNodeRefs2(t, out));
2329
+ break;
2330
+ case "discriminatedUnion":
2331
+ type.members.forEach((t) => collectTypeNodeRefs2(t, out));
2332
+ break;
2333
+ case "intersection":
2334
+ type.members.forEach((t) => collectTypeNodeRefs2(t, out));
2335
+ break;
2336
+ case "lazy":
2337
+ collectTypeNodeRefs2(type.inner, out);
2338
+ break;
2339
+ case "inlineObject":
2340
+ type.fields.forEach((f) => collectTypeNodeRefs2(f.type, out));
2341
+ break;
2342
+ }
2343
+ }
2344
+ __name(collectTypeNodeRefs2, "collectTypeNodeRefs");
2345
+ function generateTypeImports2(types, opFile, options) {
2346
+ const lines = [];
2347
+ const { modelOutPaths, outPath } = options;
2348
+ if (modelOutPaths && outPath) {
2349
+ const byFile = /* @__PURE__ */ new Map();
2350
+ const unresolved = [];
2351
+ for (const type of types) {
2352
+ const typeOutPath = modelOutPaths.get(type);
2353
+ if (typeOutPath) {
2354
+ const group = byFile.get(typeOutPath) ?? [];
2355
+ group.push(type);
2356
+ byFile.set(typeOutPath, group);
2357
+ } else {
2358
+ unresolved.push(type);
2359
+ }
2360
+ }
2361
+ const fromDir = dirname3(outPath);
2362
+ for (const [typeOutPath, names] of byFile) {
2363
+ let rel = relative3(fromDir, typeOutPath);
2364
+ rel = rel.replace(/\.ts$/, ".js");
2365
+ if (!rel.startsWith(".")) rel = "./" + rel;
2366
+ lines.push(`import type { ${names.sort().join(", ")} } from '${rel}';`);
2367
+ }
2368
+ for (const type of unresolved) {
2369
+ const moduleName = pascalToDotCase(type);
2370
+ lines.push(`import type { ${type} } from './${moduleName}.js';`);
2371
+ }
2372
+ } else {
2373
+ const typeImport = deriveTypeImportPath2(opFile, options.typeImportPathTemplate);
2374
+ lines.push(`import type { ${types.join(", ")} } from '${typeImport}';`);
2375
+ }
2376
+ return lines;
2377
+ }
2378
+ __name(generateTypeImports2, "generateTypeImports");
2379
+ function deriveTypeImportPath2(file, template) {
2380
+ const base = file.split("/").pop()?.replace(/\.(op|ck)$/, "") ?? "resource";
2381
+ const module = base.split(".")[0] ?? base;
2382
+ if (template) {
2383
+ return template.replace(/\{module\}/g, module).replace(/\{base\}/g, base);
2384
+ }
2385
+ return `#modules/${module}/types/index.js`;
2386
+ }
2387
+ __name(deriveTypeImportPath2, "deriveTypeImportPath");
2388
+ function generateSdkOptions() {
2389
+ return [
2390
+ "export class SdkError extends Error {",
2391
+ " constructor(",
2392
+ " public readonly status: number,",
2393
+ " public readonly statusText: string,",
2394
+ " public readonly body: unknown,",
2395
+ " ) {",
2396
+ " super(`${status} ${statusText}`);",
2397
+ " this.name = 'SdkError';",
2398
+ " }",
2399
+ "}",
2400
+ "",
2401
+ "export type SdkFetch = (url: string, init: RequestInit) => Promise<Response>;",
2402
+ "",
2403
+ "export interface SdkOptions {",
2404
+ " baseUrl: string;",
2405
+ " headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);",
2406
+ " fetch?: SdkFetch;",
2407
+ " /** Called once per request to produce a unique X-Request-ID header value */",
2408
+ " requestIdFactory?: () => string;",
2409
+ "}",
2410
+ "",
2411
+ "export const bigIntReplacer = (_: string, value: any): any => {",
2412
+ " if (typeof value === 'bigint') {",
2413
+ " return value.toString() + 'n';",
2414
+ " }",
2415
+ " return value;",
2416
+ "};",
2417
+ "",
2418
+ "export const bigIntReviver = (_: string, value: any): any => {",
2419
+ " if (typeof value === 'string' && /^-?\\d+n$/.test(value)) {",
2420
+ " return BigInt(value.slice(0, -1));",
2421
+ " }",
2422
+ " return value;",
2423
+ "};",
2424
+ "",
2425
+ JSON_VALUE_TYPE_DECL,
2426
+ "",
2427
+ "export function createSdkFetch(options: SdkOptions): SdkFetch {",
2428
+ " const getRequestId = options.requestIdFactory ?? (() => crypto.randomUUID());",
2429
+ " return async (url: string, init: RequestInit): Promise<Response> => {",
2430
+ " const baseHeaders = typeof options.headers === 'function'",
2431
+ " ? await options.headers()",
2432
+ " : options.headers ?? {};",
2433
+ " const res = await fetch(`${options.baseUrl}${url}`, {",
2434
+ " ...init,",
2435
+ " headers: { ...baseHeaders, 'X-Request-ID': getRequestId(), ...init.headers as Record<string, string> },",
2436
+ " });",
2437
+ " if (!res.ok) {",
2438
+ " const text = await res.text();",
2439
+ " let body: unknown;",
2440
+ " try { body = JSON.parse(text); } catch { body = text; }",
2441
+ " throw new SdkError(res.status, res.statusText, body);",
2442
+ " }",
2443
+ " return res;",
2444
+ " };",
2445
+ "}",
2446
+ "",
2447
+ "export function buildQueryString(query: object | undefined): string {",
2448
+ " const searchParams = new URLSearchParams();",
2449
+ " if (query) {",
2450
+ " for (const [k, v] of Object.entries(query)) {",
2451
+ " if (v === undefined || v === null) continue;",
2452
+ " if (Array.isArray(v)) { for (const item of v) searchParams.append(k, String(item)); }",
2453
+ " else searchParams.set(k, String(v));",
2454
+ " }",
2455
+ " }",
2456
+ " const qs = searchParams.toString();",
2457
+ " return qs ? `?${qs}` : '';",
2458
+ "}",
2459
+ "",
2460
+ "export async function parseJson<T>(res: Response): Promise<T> {",
2461
+ " return JSON.parse(await res.text(), bigIntReviver) as T;",
2462
+ "}",
2463
+ ""
2464
+ ].join("\n");
2465
+ }
2466
+ __name(generateSdkOptions, "generateSdkOptions");
2467
+ function generateSdkAggregator(clients, sdkOptionsImportPath = "./sdk-options.js", sdkClassName = "Sdk") {
2468
+ const lines = [];
2469
+ lines.push(`import type { SdkOptions } from '${sdkOptionsImportPath}';`);
2470
+ lines.push(`import { createSdkFetch } from '${sdkOptionsImportPath}';`);
2471
+ for (const c of clients) {
2472
+ lines.push(`import { ${c.className} } from '${c.importPath}';`);
2473
+ }
2474
+ lines.push("");
2475
+ lines.push(`export class ${sdkClassName} {`);
2476
+ for (const c of clients) {
2477
+ lines.push(` readonly ${c.propertyName}: ${c.className};`);
2478
+ }
2479
+ lines.push("");
2480
+ lines.push(" constructor(options: SdkOptions) {");
2481
+ lines.push(" const sdkFetch = options.fetch ?? createSdkFetch(options);");
2482
+ for (const c of clients) {
2483
+ lines.push(` this.${c.propertyName} = new ${c.className}(sdkFetch);`);
2484
+ }
2485
+ lines.push(" }");
2486
+ lines.push("}");
2487
+ lines.push("");
2488
+ return lines.join("\n");
2489
+ }
2490
+ __name(generateSdkAggregator, "generateSdkAggregator");
2491
+
2492
+ // src/codegen-plain-types.ts
2493
+ import { relative as relative4, dirname as dirname4 } from "path";
2494
+ import { computeModelsWithOutput, collectExternalOutputRefs } from "@contractkit/core";
2495
+ function generatePlainTypes(root, context) {
2496
+ const externalRefs = collectExternalRefs(root);
2497
+ const lines = [];
2498
+ const externalModelsWithInput = context?.modelsWithInput ?? /* @__PURE__ */ new Set();
2499
+ const localModelsWithInput = computeModelsWithInput(root.models, externalModelsWithInput);
2500
+ const allModelsWithInput = /* @__PURE__ */ new Set([
2501
+ ...localModelsWithInput,
2502
+ ...externalModelsWithInput
2503
+ ]);
2504
+ const externalModelsWithOutput = context?.modelsWithOutput ?? /* @__PURE__ */ new Set();
2505
+ const localModelsWithOutput = computeModelsWithOutput(root.models, externalModelsWithOutput);
2506
+ const allModelsWithOutput = /* @__PURE__ */ new Set([
2507
+ ...localModelsWithOutput,
2508
+ ...externalModelsWithOutput
2509
+ ]);
2510
+ const externalInputRefs = allModelsWithInput.size > 0 ? collectExternalInputRefs(root, allModelsWithInput) : [];
2511
+ const externalOutputRefs = allModelsWithOutput.size > 0 ? collectExternalOutputRefs(root, allModelsWithOutput) : [];
2512
+ const allExternalRefs = [
2513
+ .../* @__PURE__ */ new Set([
2514
+ ...externalRefs,
2515
+ ...externalInputRefs,
2516
+ ...externalOutputRefs
2517
+ ])
2518
+ ].sort();
2519
+ for (const ref of allExternalRefs) {
2520
+ const importPath = resolveImportPath(ref, context);
2521
+ lines.push(`import type { ${ref} } from '${importPath}';`);
2522
+ }
2523
+ if (allExternalRefs.length > 0) lines.push("");
2524
+ if (rootNeedsScalar(root, "json")) {
2525
+ if (context?.jsonValueImportPath) {
2526
+ lines.push(`import type { JsonValue } from '${context.jsonValueImportPath}';`);
2527
+ } else {
2528
+ lines.push(JSON_VALUE_TYPE_DECL);
2529
+ }
2530
+ lines.push("");
2531
+ }
2532
+ for (const model of topoSortModels(root.models)) {
2533
+ lines.push(...generateModel2(model, context?.currentOutPath, allModelsWithInput, allModelsWithOutput));
2534
+ lines.push("");
2535
+ }
2536
+ return lines.join("\n");
2537
+ }
2538
+ __name(generatePlainTypes, "generatePlainTypes");
2539
+ function generateModel2(model, outPath, modelsWithInput, modelsWithOutput) {
2540
+ if (model.type) {
2541
+ return generateTypeAlias2(model, outPath, modelsWithInput, modelsWithOutput);
2542
+ }
2543
+ const needsInputSplit = model.fields.some((f) => f.visibility !== "normal") || (modelsWithInput?.has(model.name) ?? false);
2544
+ const lines = needsInputSplit ? generateVisibilityModel(model, outPath, modelsWithInput) : generateSimpleModel2(model, outPath);
2545
+ if (modelsWithOutput?.has(model.name)) {
2546
+ lines.push("");
2547
+ lines.push(...generateOutputModel(model, modelsWithOutput));
2548
+ }
2549
+ return lines;
2550
+ }
2551
+ __name(generateModel2, "generateModel");
2552
+ function generateComments2(model, outPath) {
2553
+ const lines = [];
2554
+ lines.push("/**");
2555
+ if (model.deprecated) {
2556
+ lines.push(` * @deprecated`);
2557
+ }
2558
+ if (model.description) {
2559
+ lines.push(` * ${model.description}`);
2560
+ }
2561
+ const relPath = outPath ? relative4(dirname4(outPath), model.loc.file) : model.loc.file;
2562
+ lines.push(` * generated from [${model.name}](file://./${relPath}#L${model.loc.line})`);
2563
+ lines.push(" */");
2564
+ return lines;
2565
+ }
2566
+ __name(generateComments2, "generateComments");
2567
+ function generateTypeAlias2(model, outPath, modelsWithInput, modelsWithOutput) {
2568
+ const lines = [];
2569
+ lines.push(...generateComments2(model, outPath));
2570
+ lines.push(`export type ${model.name} = ${renderTsType(model.type)};`);
2571
+ if (modelsWithInput?.has(model.name)) {
2572
+ lines.push(`export type ${model.name}Input = ${renderInputTsType(model.type, modelsWithInput)};`);
2573
+ }
2574
+ if (modelsWithOutput?.has(model.name)) {
2575
+ lines.push(`export type ${model.name}Output = ${renderOutputTsType(model.type, modelsWithOutput)};`);
2576
+ }
2577
+ return lines;
2578
+ }
2579
+ __name(generateTypeAlias2, "generateTypeAlias");
2580
+ function buildExtendsClause(bases, overrideNames, baseNameResolver) {
2581
+ if (bases.length === 0) return "";
2582
+ if (overrideNames.length === 0) return ` extends ${bases.map(baseNameResolver).join(", ")}`;
2583
+ const omitKeys = overrideNames.map((n) => `'${n}'`).join(" | ");
2584
+ const wrapped = bases.map((b) => `Omit<${baseNameResolver(b)}, ${omitKeys}>`);
2585
+ return ` extends ${wrapped.join(", ")}`;
2586
+ }
2587
+ __name(buildExtendsClause, "buildExtendsClause");
2588
+ function generateSimpleModel2(model, outPath) {
2589
+ const lines = [];
2590
+ lines.push(...generateComments2(model, outPath));
2591
+ const bases = model.bases ?? [];
2592
+ const overrideNames = model.fields.filter((f) => f.override).map((f) => f.name);
2593
+ lines.push(`export interface ${model.name}${buildExtendsClause(bases, overrideNames, (b) => b)} {`);
2594
+ for (const field of model.fields) {
2595
+ lines.push(` ${renderField2(field)}`);
2596
+ }
2597
+ lines.push("}");
2598
+ return lines;
2599
+ }
2600
+ __name(generateSimpleModel2, "generateSimpleModel");
2601
+ function generateVisibilityModel(model, outPath, modelsWithInput) {
2602
+ const lines = [];
2603
+ lines.push(...generateComments2(model, outPath));
2604
+ const bases = model.bases ?? [];
2605
+ const overrideNames = model.fields.filter((f) => f.override).map((f) => f.name);
2606
+ const readFields = model.fields.filter((f) => f.visibility !== "writeonly");
2607
+ lines.push(`export interface ${model.name}${buildExtendsClause(bases, overrideNames, (b) => b)} {`);
2608
+ for (const field of readFields) {
2609
+ lines.push(` ${renderField2(field)}`);
2610
+ }
2611
+ lines.push("}");
2612
+ lines.push("");
2613
+ const writeFields = model.fields.filter((f) => f.visibility !== "readonly");
2614
+ const inputResolver = /* @__PURE__ */ __name((b) => modelsWithInput?.has(b) ? `${b}Input` : b, "inputResolver");
2615
+ lines.push(`export interface ${model.name}Input${buildExtendsClause(bases, overrideNames, inputResolver)} {`);
2616
+ for (const field of writeFields) {
2617
+ lines.push(` ${modelsWithInput ? renderInputField2(field, modelsWithInput) : renderField2(field)}`);
2618
+ }
2619
+ lines.push("}");
2620
+ return lines;
2621
+ }
2622
+ __name(generateVisibilityModel, "generateVisibilityModel");
2623
+ function renderField2(field) {
2624
+ const opt = field.optional || field.default !== void 0 ? "?" : "";
2625
+ let typeStr = renderTsType(field.type);
2626
+ if (field.nullable) typeStr += " | null";
2627
+ const line = `${quoteKey2(field.name)}${opt}: ${typeStr};`;
2628
+ const jsdocParts = [];
2629
+ if (field.deprecated) jsdocParts.push("@deprecated");
2630
+ if (field.description) jsdocParts.push(field.description);
2631
+ if (jsdocParts.length > 0) {
2632
+ return `/** ${jsdocParts.join(" ")} */
2633
+ ${line}`;
2634
+ }
2635
+ return line;
2636
+ }
2637
+ __name(renderField2, "renderField");
2638
+ function renderInputField2(field, modelsWithInput) {
2639
+ const opt = field.optional || field.default !== void 0 ? "?" : "";
2640
+ let typeStr = renderInputTsType(field.type, modelsWithInput);
2641
+ if (field.nullable) typeStr += " | null";
2642
+ const line = `${quoteKey2(field.name)}${opt}: ${typeStr};`;
2643
+ const jsdocParts = [];
2644
+ if (field.deprecated) jsdocParts.push("@deprecated");
2645
+ if (field.description) jsdocParts.push(field.description);
2646
+ if (jsdocParts.length > 0) {
2647
+ return `/** ${jsdocParts.join(" ")} */
2648
+ ${line}`;
2649
+ }
2650
+ return line;
2651
+ }
2652
+ __name(renderInputField2, "renderInputField");
2653
+ function camelToSnake2(s) {
2654
+ return s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
2655
+ }
2656
+ __name(camelToSnake2, "camelToSnake");
2657
+ function camelToPascal2(s) {
2658
+ return s.charAt(0).toUpperCase() + s.slice(1);
2659
+ }
2660
+ __name(camelToPascal2, "camelToPascal");
2661
+ function applyOutputCase(name, c) {
2662
+ if (!c || c === "camel") return name;
2663
+ if (c === "snake") return camelToSnake2(name);
2664
+ return camelToPascal2(name);
2665
+ }
2666
+ __name(applyOutputCase, "applyOutputCase");
2667
+ function generateOutputModel(model, modelsWithOutput) {
2668
+ const lines = [];
2669
+ const outputCase = model.outputCase && model.outputCase !== "camel" ? model.outputCase : void 0;
2670
+ const readFields = model.fields.filter((f) => f.visibility !== "writeonly");
2671
+ if (!outputCase) {
2672
+ const baseExt = model.bases?.[0] && modelsWithOutput.has(model.bases?.[0]) ? ` extends ${model.bases?.[0]}Output` : model.bases?.[0] ? ` extends ${model.bases?.[0]}` : "";
2673
+ lines.push(`export interface ${model.name}Output${baseExt} {`);
2674
+ for (const field of readFields) {
2675
+ lines.push(` ${renderOutputField(field, model.outputCase, modelsWithOutput)}`);
2676
+ }
2677
+ lines.push("}");
2678
+ return lines;
2679
+ }
2680
+ lines.push(`export interface ${model.name}Output {`);
2681
+ for (const field of readFields) {
2682
+ lines.push(` ${renderOutputField(field, outputCase, modelsWithOutput)}`);
2683
+ }
2684
+ lines.push("}");
2685
+ return lines;
2686
+ }
2687
+ __name(generateOutputModel, "generateOutputModel");
2688
+ function renderOutputField(field, outputCase, modelsWithOutput) {
2689
+ const opt = field.optional || field.default !== void 0 ? "?" : "";
2690
+ const key = applyOutputCase(field.name, outputCase);
2691
+ let typeStr = renderOutputTsType(field.type, modelsWithOutput);
2692
+ if (field.nullable) typeStr += " | null";
2693
+ const line = `${quoteKey2(key)}${opt}: ${typeStr};`;
2694
+ const jsdocParts = [];
2695
+ if (field.deprecated) jsdocParts.push("@deprecated");
2696
+ if (field.description) jsdocParts.push(field.description);
2697
+ if (jsdocParts.length > 0) {
2698
+ return `/** ${jsdocParts.join(" ")} */
2699
+ ${line}`;
2700
+ }
2701
+ return line;
2702
+ }
2703
+ __name(renderOutputField, "renderOutputField");
2704
+
2705
+ // src/path-utils.ts
2706
+ import { resolve, join, relative as relative5, dirname as dirname5 } from "path";
2707
+ import { collectTypeRefs as collectTypeRefs2, collectPublicTypeNames } from "@contractkit/core";
2708
+ var TEMPLATE_VAR_RE = /\{\w+\}/;
2709
+ function resolveTemplate(template, vars) {
2710
+ return template.replace(/\{(\w+)\}/g, (_, key) => vars[key] ?? `{${key}}`);
2711
+ }
2712
+ __name(resolveTemplate, "resolveTemplate");
2713
+ function includesFilename(p) {
2714
+ const last = p.split("/").pop() ?? "";
2715
+ return last.includes(".");
2716
+ }
2717
+ __name(includesFilename, "includesFilename");
2718
+ function commonDir(files, rootDir) {
2719
+ if (files.length === 0) return resolve(rootDir);
2720
+ const parts = files.map((f) => dirname5(f).split("/"));
2721
+ const first = parts[0];
2722
+ let depth = first.length;
2723
+ for (const p of parts) {
2724
+ for (let i = 0; i < depth; i++) {
2725
+ if (p[i] !== first[i]) {
2726
+ depth = i;
2727
+ break;
2728
+ }
2729
+ }
2730
+ }
2731
+ return first.slice(0, depth).join("/") || "/";
2732
+ }
2733
+ __name(commonDir, "commonDir");
2734
+ function computeOpOutPath(filePath, baseDir, output, defaultSuffix, commonRoot, meta = {}) {
2735
+ const baseName = filePath.split("/").pop();
2736
+ const relDir = relative5(commonRoot, dirname5(filePath));
2737
+ const filename = baseName.replace(/\.ck$/, "");
2738
+ const defaultName = `${filename}${defaultSuffix}`;
2739
+ const baseOutDir = resolve(baseDir);
2740
+ if (output && TEMPLATE_VAR_RE.test(output)) {
2741
+ const resolved = resolveTemplate(output, {
2742
+ filename,
2743
+ dir: relDir,
2744
+ ext: "ck",
2745
+ ...meta
2746
+ });
2747
+ if (includesFilename(resolved)) return join(baseOutDir, resolved);
2748
+ return join(baseOutDir, resolved, defaultName);
2749
+ }
2750
+ if (output) {
2751
+ if (includesFilename(output)) return join(baseOutDir, output);
2752
+ return join(baseOutDir, output, relDir, defaultName);
2753
+ }
2754
+ return join(baseOutDir, relDir, defaultName);
2755
+ }
2756
+ __name(computeOpOutPath, "computeOpOutPath");
2757
+ function computeContractOutPath(filePath, baseDir, output, defaultSuffix, commonRoot, meta = {}) {
2758
+ return computeOpOutPath(filePath, baseDir, output, defaultSuffix, commonRoot, meta);
2759
+ }
2760
+ __name(computeContractOutPath, "computeContractOutPath");
2761
+ function computeSdkOutPath(filePath, rootDir, clientOutput, commonRoot, meta = {}) {
2762
+ if (!filePath.endsWith(".ck")) return null;
2763
+ const baseName = filePath.split("/").pop();
2764
+ const defaultOutName = baseName.replace(/\.ck$/, ".client.ts");
2765
+ const baseOutDir = resolve(rootDir);
2766
+ const relDir = relative5(commonRoot, dirname5(filePath));
2767
+ const filename = baseName.replace(/\.ck$/, "");
2768
+ if (clientOutput && TEMPLATE_VAR_RE.test(clientOutput)) {
2769
+ const resolved = resolveTemplate(clientOutput, {
2770
+ filename,
2771
+ dir: relDir,
2772
+ ext: "ck",
2773
+ ...meta
2774
+ });
2775
+ if (includesFilename(resolved)) return join(baseOutDir, resolved);
2776
+ return join(baseOutDir, resolved, defaultOutName);
2777
+ }
2778
+ if (clientOutput) {
2779
+ if (includesFilename(clientOutput)) return join(baseOutDir, clientOutput);
2780
+ return join(baseOutDir, clientOutput, relDir, defaultOutName);
2781
+ }
2782
+ return join(baseOutDir, relDir, defaultOutName);
2783
+ }
2784
+ __name(computeSdkOutPath, "computeSdkOutPath");
2785
+ function computeSdkTypeOutPath(filePath, rootDir, typeOutput, commonRoot, meta = {}) {
2786
+ if (!filePath.endsWith(".ck")) return null;
2787
+ const baseName = filePath.split("/").pop();
2788
+ const defaultOutName = baseName.replace(/\.ck$/, ".ts");
2789
+ const baseOutDir = resolve(rootDir);
2790
+ const relDir = relative5(commonRoot, dirname5(filePath));
2791
+ const filename = baseName.replace(/\.ck$/, "");
2792
+ if (TEMPLATE_VAR_RE.test(typeOutput)) {
2793
+ const resolved = resolveTemplate(typeOutput, {
2794
+ filename,
2795
+ dir: relDir,
2796
+ ext: "ck",
2797
+ ...meta
2798
+ });
2799
+ if (includesFilename(resolved)) return join(baseOutDir, resolved);
2800
+ return join(baseOutDir, resolved, defaultOutName);
2801
+ }
2802
+ if (includesFilename(typeOutput)) return join(baseOutDir, typeOutput);
2803
+ return join(baseOutDir, typeOutput, relDir, defaultOutName);
2804
+ }
2805
+ __name(computeSdkTypeOutPath, "computeSdkTypeOutPath");
2806
+ function generateBarrelFiles(contractPaths) {
2807
+ const byDir = /* @__PURE__ */ new Map();
2808
+ for (const outPath of contractPaths) {
2809
+ const dir = dirname5(outPath);
2810
+ const group = byDir.get(dir) ?? [];
2811
+ group.push(outPath);
2812
+ byDir.set(dir, group);
2813
+ }
2814
+ const results = [];
2815
+ for (const [dir, files] of byDir) {
2816
+ const exports = files.map((f) => `export * from './${f.split("/").pop().replace(/\.ts$/, ".js")}';`).sort().join("\n");
2817
+ results.push({
2818
+ outPath: join(dir, "index.ts"),
2819
+ content: `// Auto-generated barrel file
2820
+ ${exports}
2821
+ `
2822
+ });
2823
+ }
2824
+ return results;
2825
+ }
2826
+ __name(generateBarrelFiles, "generateBarrelFiles");
2827
+ function computePubliclyReachableTypes(opAsts, contractAsts, modelsWithInput, modelsWithOutput = /* @__PURE__ */ new Set()) {
2828
+ if (opAsts.length === 0) return null;
2829
+ const reachable = /* @__PURE__ */ new Set();
2830
+ for (const opAst of opAsts) {
2831
+ for (const name of collectPublicTypeNames(opAst, modelsWithInput, modelsWithOutput)) reachable.add(name);
2832
+ }
2833
+ const modelDeps = /* @__PURE__ */ new Map();
2834
+ for (const contractAst of contractAsts) {
2835
+ for (const model of contractAst.models) {
2836
+ const deps = /* @__PURE__ */ new Set();
2837
+ if (model.bases) for (const b of model.bases) deps.add(b);
2838
+ if (model.type) collectTypeRefs2(model.type, deps);
2839
+ for (const field of model.fields) collectTypeRefs2(field.type, deps);
2840
+ modelDeps.set(model.name, deps);
2841
+ }
2842
+ }
2843
+ const frontier = [
2844
+ ...reachable
2845
+ ];
2846
+ while (frontier.length > 0) {
2847
+ const name = frontier.pop();
2848
+ const baseName = name.endsWith("Input") ? name.slice(0, -5) : name.endsWith("Output") ? name.slice(0, -6) : name;
2849
+ for (const dep of modelDeps.get(baseName) ?? []) {
2850
+ if (!reachable.has(dep)) {
2851
+ reachable.add(dep);
2852
+ frontier.push(dep);
2853
+ }
2854
+ if (modelsWithInput.has(dep)) {
2855
+ const inputDep = `${dep}Input`;
2856
+ if (!reachable.has(inputDep)) {
2857
+ reachable.add(inputDep);
2858
+ frontier.push(inputDep);
2859
+ }
2860
+ }
2861
+ if (modelsWithOutput.has(dep)) {
2862
+ const outputDep = `${dep}Output`;
2863
+ if (!reachable.has(outputDep)) {
2864
+ reachable.add(outputDep);
2865
+ frontier.push(outputDep);
2866
+ }
2867
+ }
2868
+ }
2869
+ }
2870
+ return reachable;
2871
+ }
2872
+ __name(computePubliclyReachableTypes, "computePubliclyReachableTypes");
2873
+
2874
+ // src/index.ts
2875
+ function runServerGeneration(config, rootDir, inputs, emitFile) {
2876
+ const serverBase = resolve2(rootDir, config.baseDir ?? ".");
2877
+ const modelsWithInput = inputs.modelsWithInput;
2878
+ const modelsWithOutput = inputs.modelsWithOutput;
2879
+ const allFiles = [
2880
+ ...inputs.contractRoots.map((r) => r.file),
2881
+ ...inputs.opRoots.map((r) => r.file)
2882
+ ];
2883
+ const commonRoot = commonDir(allFiles, rootDir);
2884
+ let serverModelOutPaths = /* @__PURE__ */ new Map();
2885
+ if (config.output?.types) {
2886
+ serverModelOutPaths = /* @__PURE__ */ new Map();
2887
+ const typeEntries = [];
2888
+ for (const ast of inputs.contractRoots) {
2889
+ const typeOutPath = computeContractOutPath(ast.file, serverBase, config.output.types, ".ts", commonRoot, ast.meta);
2890
+ typeEntries.push({
2891
+ ast,
2892
+ typeOutPath
2893
+ });
2894
+ for (const model of ast.models) {
2895
+ serverModelOutPaths.set(model.name, typeOutPath);
2896
+ if (modelsWithInput.has(model.name)) {
2897
+ serverModelOutPaths.set(`${model.name}Input`, typeOutPath);
2898
+ }
2899
+ if (modelsWithOutput.has(model.name)) {
2900
+ serverModelOutPaths.set(`${model.name}Output`, typeOutPath);
2901
+ }
2902
+ }
2903
+ }
2904
+ for (const { ast, typeOutPath } of typeEntries) {
2905
+ const ctx = {
2906
+ modelOutPaths: serverModelOutPaths,
2907
+ currentOutPath: typeOutPath,
2908
+ modelsWithInput,
2909
+ modelsWithOutput
2910
+ };
2911
+ const content = config.zod ? generateContract(ast, ctx) : generatePlainTypes(ast, ctx);
2912
+ emitFile(typeOutPath, content);
2913
+ }
2914
+ }
2915
+ for (const ast of inputs.opRoots) {
2916
+ const outPath = computeOpOutPath(ast.file, serverBase, config.output?.routes, ".router.ts", commonRoot, ast.meta);
2917
+ const content = generateOp(ast, {
2918
+ servicePathTemplate: config.servicePathTemplate,
2919
+ outPath,
2920
+ modelOutPaths: serverModelOutPaths,
2921
+ modelsWithInput,
2922
+ modelsWithOutput,
2923
+ includeInternal: config.includeInternal
2924
+ });
2925
+ emitFile(outPath, content);
2926
+ }
2927
+ }
2928
+ __name(runServerGeneration, "runServerGeneration");
2929
+ function runSdkGeneration(config, rootDir, inputs, emitFile) {
2930
+ const sdkBase = config.baseDir ? resolve2(rootDir, config.baseDir) : rootDir;
2931
+ const sdkName = config.name;
2932
+ const sdkOutput = config.output?.sdk;
2933
+ const sdkEntryPath = sdkOutput ? join2(sdkBase, TEMPLATE_VAR_RE.test(sdkOutput) ? resolveTemplate(sdkOutput, {
2934
+ name: sdkName ?? "sdk"
2935
+ }) : sdkOutput) : join2(sdkBase, "sdk.ts");
2936
+ const sdkOptionsPath = join2(dirname6(sdkEntryPath), "sdk-options.ts");
2937
+ const modelsWithInput = inputs.modelsWithInput;
2938
+ const modelsWithOutput = inputs.modelsWithOutput;
2939
+ const allFiles = [
2940
+ ...inputs.contractRoots.map((r) => r.file),
2941
+ ...inputs.opRoots.map((r) => r.file)
2942
+ ];
2943
+ const ckCommonRoot = commonDir(allFiles, rootDir);
2944
+ let sdkModelOutPaths = /* @__PURE__ */ new Map();
2945
+ const sdkTypePaths = [];
2946
+ const sdkClientInfos = [];
2947
+ if (config.output?.types) {
2948
+ sdkModelOutPaths = /* @__PURE__ */ new Map();
2949
+ const publicTypes = computePubliclyReachableTypes(inputs.opRoots, inputs.contractRoots, modelsWithInput, modelsWithOutput);
2950
+ const sdkContractEntries = [];
2951
+ for (const ast of inputs.contractRoots) {
2952
+ const typeOutPath = computeSdkTypeOutPath(ast.file, sdkBase, config.output.types, ckCommonRoot, ast.meta);
2953
+ if (!typeOutPath) continue;
2954
+ if (publicTypes !== null && !ast.models.some((m) => publicTypes.has(m.name))) continue;
2955
+ sdkTypePaths.push(typeOutPath);
2956
+ sdkContractEntries.push({
2957
+ ast,
2958
+ typeOutPath
2959
+ });
2960
+ for (const model of ast.models) {
2961
+ sdkModelOutPaths.set(model.name, typeOutPath);
2962
+ if (modelsWithInput.has(model.name)) sdkModelOutPaths.set(`${model.name}Input`, typeOutPath);
2963
+ if (modelsWithOutput.has(model.name)) sdkModelOutPaths.set(`${model.name}Output`, typeOutPath);
2964
+ }
2965
+ }
2966
+ for (const { ast, typeOutPath } of sdkContractEntries) {
2967
+ let content;
2968
+ if (config.zod) {
2969
+ content = generateContract(ast, {
2970
+ modelOutPaths: sdkModelOutPaths,
2971
+ currentOutPath: typeOutPath,
2972
+ modelsWithInput,
2973
+ modelsWithOutput
2974
+ });
2975
+ } else {
2976
+ let rel = relative6(dirname6(typeOutPath), sdkOptionsPath).replace(/\.ts$/, ".js");
2977
+ if (!rel.startsWith(".")) rel = "./" + rel;
2978
+ content = generatePlainTypes(ast, {
2979
+ modelOutPaths: sdkModelOutPaths,
2980
+ currentOutPath: typeOutPath,
2981
+ modelsWithInput,
2982
+ modelsWithOutput,
2983
+ jsonValueImportPath: rel
2984
+ });
2985
+ }
2986
+ emitFile(typeOutPath, content);
2987
+ }
2988
+ }
2989
+ if (config.output?.clients) {
2990
+ for (const ast of inputs.opRoots) {
2991
+ const sdkOutPath = computeSdkOutPath(ast.file, sdkBase, config.output.clients, ckCommonRoot, ast.meta);
2992
+ if (!sdkOutPath || !hasPublicOperations(ast, config.includeInternal)) continue;
2993
+ sdkClientInfos.push({
2994
+ outPath: sdkOutPath,
2995
+ className: deriveClientClassName(ast.file),
2996
+ propertyName: deriveClientPropertyName(ast.file)
2997
+ });
2998
+ emitFile(sdkOutPath, generateSdk(ast, {
2999
+ typeImportPathTemplate: void 0,
3000
+ outPath: sdkOutPath,
3001
+ modelOutPaths: sdkModelOutPaths,
3002
+ sdkOptionsPath,
3003
+ modelsWithInput,
3004
+ modelsWithOutput,
3005
+ includeInternal: config.includeInternal
3006
+ }));
3007
+ }
3008
+ }
3009
+ emitFile(sdkOptionsPath, generateSdkOptions());
3010
+ if (sdkClientInfos.length > 0) {
3011
+ const sdkEntryDir = dirname6(sdkEntryPath);
3012
+ const clients = sdkClientInfos.map((c) => {
3013
+ let rel = relative6(sdkEntryDir, c.outPath).replace(/\.ts$/, ".js");
3014
+ if (!rel.startsWith(".")) rel = "./" + rel;
3015
+ return {
3016
+ className: c.className,
3017
+ propertyName: c.propertyName,
3018
+ importPath: rel
3019
+ };
3020
+ });
3021
+ const sdkOptionsRel = relative6(sdkEntryDir, sdkOptionsPath).replace(/\.ts$/, ".js");
3022
+ const sdkClassName = sdkName ? sdkName.split(/[-._\s]+/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Sdk" : "Sdk";
3023
+ emitFile(sdkEntryPath, generateSdkAggregator(clients, sdkOptionsRel.startsWith(".") ? sdkOptionsRel : "./" + sdkOptionsRel, sdkClassName));
3024
+ }
3025
+ const sdkSrcDir = dirname6(sdkEntryPath);
3026
+ const sdkTypeBarrels = generateBarrelFiles(sdkTypePaths);
3027
+ for (const barrel of sdkTypeBarrels) emitFile(barrel.outPath, barrel.content);
3028
+ const rootExports = [
3029
+ `export * from './${basename3(sdkOptionsPath).replace(/\.ts$/, ".js")}';`
3030
+ ];
3031
+ if (sdkClientInfos.length > 0) {
3032
+ rootExports.push(`export * from './${basename3(sdkEntryPath).replace(/\.ts$/, ".js")}';`);
3033
+ }
3034
+ for (const c of sdkClientInfos) {
3035
+ let rel = relative6(sdkSrcDir, c.outPath).replace(/\.ts$/, ".js");
3036
+ if (!rel.startsWith(".")) rel = "./" + rel;
3037
+ rootExports.push(`export * from '${rel}';`);
3038
+ }
3039
+ for (const barrel of sdkTypeBarrels) {
3040
+ let rel = relative6(sdkSrcDir, barrel.outPath).replace(/\.ts$/, ".js");
3041
+ if (!rel.startsWith(".")) rel = "./" + rel;
3042
+ rootExports.push(`export * from '${rel}';`);
3043
+ }
3044
+ emitFile(join2(sdkSrcDir, "index.ts"), `// Auto-generated barrel file
3045
+ ${rootExports.sort().join("\n")}
3046
+ `);
3047
+ }
3048
+ __name(runSdkGeneration, "runSdkGeneration");
3049
+ function runZodGeneration(config, rootDir, inputs, emitFile) {
3050
+ const zodBase = resolve2(rootDir, config.baseDir ?? ".");
3051
+ const allFiles = [
3052
+ ...inputs.contractRoots.map((r) => r.file),
3053
+ ...inputs.opRoots.map((r) => r.file)
3054
+ ];
3055
+ const commonRoot = commonDir(allFiles, rootDir);
3056
+ const modelsWithInput = inputs.modelsWithInput;
3057
+ const modelsWithOutput = inputs.modelsWithOutput;
3058
+ const modelOutPaths = /* @__PURE__ */ new Map();
3059
+ const entries = [];
3060
+ for (const ast of inputs.contractRoots) {
3061
+ const outPath = computeContractOutPath(ast.file, zodBase, config.output, ".schema.ts", commonRoot, ast.meta);
3062
+ entries.push({
3063
+ ast,
3064
+ outPath
3065
+ });
3066
+ for (const model of ast.models) {
3067
+ modelOutPaths.set(model.name, outPath);
3068
+ if (modelsWithInput.has(model.name)) modelOutPaths.set(`${model.name}Input`, outPath);
3069
+ if (modelsWithOutput.has(model.name)) modelOutPaths.set(`${model.name}Output`, outPath);
3070
+ }
3071
+ }
3072
+ for (const { ast, outPath } of entries) {
3073
+ const content = generateContract(ast, {
3074
+ modelOutPaths,
3075
+ currentOutPath: outPath,
3076
+ modelsWithInput,
3077
+ modelsWithOutput
3078
+ });
3079
+ emitFile(outPath, content);
3080
+ }
3081
+ }
3082
+ __name(runZodGeneration, "runZodGeneration");
3083
+ function runTypesGeneration(config, rootDir, inputs, emitFile) {
3084
+ const typesBase = resolve2(rootDir, config.baseDir ?? ".");
3085
+ const allFiles = [
3086
+ ...inputs.contractRoots.map((r) => r.file),
3087
+ ...inputs.opRoots.map((r) => r.file)
3088
+ ];
3089
+ const commonRoot = commonDir(allFiles, rootDir);
3090
+ const modelsWithInput = inputs.modelsWithInput;
3091
+ const modelsWithOutput = inputs.modelsWithOutput;
3092
+ const modelOutPaths = /* @__PURE__ */ new Map();
3093
+ const entries = [];
3094
+ for (const ast of inputs.contractRoots) {
3095
+ const outPath = computeContractOutPath(ast.file, typesBase, config.output, ".types.ts", commonRoot, ast.meta);
3096
+ entries.push({
3097
+ ast,
3098
+ outPath
3099
+ });
3100
+ for (const model of ast.models) {
3101
+ modelOutPaths.set(model.name, outPath);
3102
+ if (modelsWithInput.has(model.name)) modelOutPaths.set(`${model.name}Input`, outPath);
3103
+ if (modelsWithOutput.has(model.name)) modelOutPaths.set(`${model.name}Output`, outPath);
3104
+ }
3105
+ }
3106
+ for (const { ast, outPath } of entries) {
3107
+ const content = generatePlainTypes(ast, {
3108
+ modelOutPaths,
3109
+ currentOutPath: outPath,
3110
+ modelsWithInput,
3111
+ modelsWithOutput
3112
+ });
3113
+ emitFile(outPath, content);
3114
+ }
3115
+ }
3116
+ __name(runTypesGeneration, "runTypesGeneration");
3117
+ var plugin = {
3118
+ name: "typescript",
3119
+ cacheKey: "typescript",
3120
+ async generateTargets(inputs, ctx) {
3121
+ const config = ctx.options;
3122
+ if (config.server) {
3123
+ runServerGeneration(config.server, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3124
+ }
3125
+ if (config.sdk) {
3126
+ runSdkGeneration(config.sdk, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3127
+ }
3128
+ if (config.zod) {
3129
+ runZodGeneration(config.zod, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3130
+ }
3131
+ if (config.types) {
3132
+ runTypesGeneration(config.types, ctx.rootDir, inputs, ctx.emitFile.bind(ctx));
3133
+ }
3134
+ }
3135
+ };
3136
+ var index_default = plugin;
3137
+ function createTypescriptPlugin(config, rootDir) {
3138
+ return {
3139
+ name: "typescript",
3140
+ cacheKey: `typescript:${JSON.stringify(config)}`,
3141
+ async generateTargets(inputs, ctx) {
3142
+ if (config.server) {
3143
+ runServerGeneration(config.server, rootDir, inputs, ctx.emitFile.bind(ctx));
3144
+ }
3145
+ if (config.sdk) {
3146
+ runSdkGeneration(config.sdk, rootDir, inputs, ctx.emitFile.bind(ctx));
3147
+ }
3148
+ if (config.zod) {
3149
+ runZodGeneration(config.zod, rootDir, inputs, ctx.emitFile.bind(ctx));
3150
+ }
3151
+ if (config.types) {
3152
+ runTypesGeneration(config.types, rootDir, inputs, ctx.emitFile.bind(ctx));
3153
+ }
3154
+ }
3155
+ };
3156
+ }
3157
+ __name(createTypescriptPlugin, "createTypescriptPlugin");
3158
+ export {
3159
+ createTypescriptPlugin,
3160
+ index_default as default
3161
+ };
3162
+ //# sourceMappingURL=index.js.map