apiwork 0.0.0 → 0.0.3

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.
@@ -0,0 +1,1151 @@
1
+ import {
2
+ camelCase
3
+ } from "./chunk-MWFEILAW.js";
4
+
5
+ // src/utils/snake-case.ts
6
+ function snakeCase(name) {
7
+ return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/-/g, "_").toLowerCase();
8
+ }
9
+
10
+ // src/utils/kebab-case.ts
11
+ function kebabCase(name) {
12
+ return snakeCase(name).replace(/_/g, "-");
13
+ }
14
+
15
+ // src/utils/pascal-case.ts
16
+ function pascalCase(name) {
17
+ return camelCase(name).replace(
18
+ /^[a-z]/,
19
+ (character) => character.toUpperCase()
20
+ );
21
+ }
22
+
23
+ // src/codegen/options.ts
24
+ function resolveGenerateOptions(options) {
25
+ const {
26
+ fileCase = "kebab",
27
+ importExtension = "",
28
+ transformIdentifier = (identifier) => identifier
29
+ } = options;
30
+ return {
31
+ fileCase,
32
+ importExtension,
33
+ transformIdentifier
34
+ };
35
+ }
36
+ function formatFileName(name, fileCase) {
37
+ switch (fileCase) {
38
+ case "kebab":
39
+ return kebabCase(name);
40
+ case "camel":
41
+ return camelCase(name);
42
+ case "pascal":
43
+ return pascalCase(name);
44
+ case "snake":
45
+ return snakeCase(name);
46
+ }
47
+ }
48
+ function resolveDomainIdentifier(rawName, context) {
49
+ const scope = context.scopeIndex.get(rawName);
50
+ const source = scope === null || scope === void 0 ? "api" : "domain";
51
+ return context.options.transformIdentifier(pascalCase(rawName), source);
52
+ }
53
+ function resolveEndpointIdentifier(identifier, context) {
54
+ return context.options.transformIdentifier(identifier, "endpoint");
55
+ }
56
+ function resolveClientIdentifier(identifier, context) {
57
+ return context.options.transformIdentifier(identifier, "client");
58
+ }
59
+
60
+ // src/zod/mapper.ts
61
+ var TYPE_MAP = {
62
+ binary: "z.string()",
63
+ boolean: "z.boolean()",
64
+ date: "z.iso.date()",
65
+ datetime: "z.iso.datetime()",
66
+ decimal: "z.number()",
67
+ integer: "z.number().int()",
68
+ number: "z.number()",
69
+ string: "z.string()",
70
+ time: "z.iso.time()",
71
+ unknown: "z.unknown()",
72
+ uuid: "z.uuid()"
73
+ };
74
+ var FORMAT_MAP = {
75
+ date: "z.iso.date()",
76
+ datetime: "z.iso.datetime()",
77
+ double: "z.number()",
78
+ email: "z.email()",
79
+ float: "z.number()",
80
+ hostname: "z.string()",
81
+ int32: "z.number().int()",
82
+ int64: "z.number().int()",
83
+ ipv4: "z.ipv4()",
84
+ ipv6: "z.ipv6()",
85
+ password: "z.string()",
86
+ url: "z.url()",
87
+ uuid: "z.uuid()"
88
+ };
89
+ function generateEnumSchema(_enum, context) {
90
+ const name = resolveDomainIdentifier(_enum.name, context);
91
+ const values = _enum.values.sort().map((value) => `'${value}'`).join(", ");
92
+ return `export const ${name}Schema = z.enum([${values}]);`;
93
+ }
94
+ function generateObjectSchema(type, context) {
95
+ const name = resolveDomainIdentifier(type.name, context);
96
+ const properties = [...type.shape].sort((a, b) => a.name.localeCompare(b.name)).map((param) => {
97
+ return ` ${param.name}: ${mapParamToZod(param, context)},`;
98
+ }).join("\n");
99
+ if (type.recursive) {
100
+ return `export const ${name}Schema: z.ZodType<${name}> = z.lazy(() => z.object({
101
+ ${properties}
102
+ }));`;
103
+ }
104
+ if (type.extends.length > 0) {
105
+ const bases = type.extends.map(
106
+ (base) => `${resolveDomainIdentifier(base, context)}Schema`
107
+ );
108
+ if (bases.length === 1) {
109
+ if (properties.trim()) {
110
+ return `export const ${name}Schema = ${bases[0]}.merge(z.object({
111
+ ${properties}
112
+ }));`;
113
+ }
114
+ return `export const ${name}Schema = ${bases[0]};`;
115
+ }
116
+ const merged = bases.join(".merge(") + ")".repeat(bases.length - 1);
117
+ if (properties.trim()) {
118
+ return `export const ${name}Schema = ${merged}.merge(z.object({
119
+ ${properties}
120
+ }));`;
121
+ }
122
+ return `export const ${name}Schema = ${merged};`;
123
+ }
124
+ return `export const ${name}Schema = z.object({
125
+ ${properties}
126
+ });`;
127
+ }
128
+ function generateUnionSchema(type, context) {
129
+ const name = resolveDomainIdentifier(type.name, context);
130
+ let unionCode;
131
+ if (type.discriminator) {
132
+ const variants = type.variants.map((variant) => {
133
+ const base = mapParamToZod(variant, context);
134
+ const tag = "tag" in variant ? variant.tag : null;
135
+ if (tag) {
136
+ return `${base}.extend({ ${type.discriminator}: z.literal('${tag}') })`;
137
+ }
138
+ return base;
139
+ });
140
+ unionCode = `z.discriminatedUnion('${type.discriminator}', [
141
+ ${variants.join(",\n ")}
142
+ ])`;
143
+ } else {
144
+ const variants = type.variants.map(
145
+ (variant) => mapParamToZod(variant, context)
146
+ );
147
+ unionCode = `z.union([
148
+ ${variants.join(",\n ")}
149
+ ])`;
150
+ }
151
+ if (type.recursive) {
152
+ return `export const ${name}Schema: z.ZodType<${name}> = z.lazy(() => ${unionCode});`;
153
+ }
154
+ return `export const ${name}Schema = ${unionCode};`;
155
+ }
156
+ function mapParamToZod(param, context) {
157
+ let base = mapParamBase(param, context);
158
+ base = applyModifiers(base, param);
159
+ return base;
160
+ }
161
+ function mapParamBase(param, context) {
162
+ switch (param.type) {
163
+ case "string":
164
+ case "integer":
165
+ case "number":
166
+ case "decimal":
167
+ case "boolean":
168
+ case "date":
169
+ case "datetime":
170
+ case "time":
171
+ case "uuid":
172
+ case "binary":
173
+ return mapScalar(param, context);
174
+ case "literal":
175
+ return mapLiteral(param);
176
+ case "array":
177
+ return mapArray(param, context);
178
+ case "record":
179
+ return mapRecord(param, context);
180
+ case "object":
181
+ return mapObject(param, context);
182
+ case "union":
183
+ return mapUnion(param, context);
184
+ case "reference":
185
+ return `${resolveDomainIdentifier(param.reference, context)}Schema`;
186
+ case "unknown":
187
+ return "z.unknown()";
188
+ }
189
+ }
190
+ function mapScalar(param, context) {
191
+ if (param.enum) {
192
+ return `${resolveDomainIdentifier(param.enum, context)}Schema`;
193
+ }
194
+ let base;
195
+ if (param.format) {
196
+ base = FORMAT_MAP[param.format] ?? TYPE_MAP[param.type] ?? "z.unknown()";
197
+ } else {
198
+ base = TYPE_MAP[param.type] ?? "z.unknown()";
199
+ }
200
+ if ("min" in param && param.min !== null && param.min !== void 0) {
201
+ base += `.min(${param.min})`;
202
+ }
203
+ if ("max" in param && param.max !== null && param.max !== void 0) {
204
+ base += `.max(${param.max})`;
205
+ }
206
+ return base;
207
+ }
208
+ function mapLiteral(param) {
209
+ if (param.value === null) {
210
+ return "z.null()";
211
+ }
212
+ if (typeof param.value === "string") {
213
+ return `z.literal('${param.value}')`;
214
+ }
215
+ return `z.literal(${param.value})`;
216
+ }
217
+ function mapArray(param, context) {
218
+ const items = param.of ? mapParamToZod(param.of, context) : "z.unknown()";
219
+ let base = `z.array(${items})`;
220
+ if (param.min !== null && param.min !== void 0) {
221
+ base += `.min(${param.min})`;
222
+ }
223
+ if (param.max !== null && param.max !== void 0) {
224
+ base += `.max(${param.max})`;
225
+ }
226
+ return base;
227
+ }
228
+ function mapRecord(param, context) {
229
+ const value = param.of ? mapParamToZod(param.of, context) : "z.unknown()";
230
+ return `z.record(z.string(), ${value})`;
231
+ }
232
+ function mapObject(param, context) {
233
+ if (param.shape.length === 0) {
234
+ return "z.record(z.string(), z.unknown())";
235
+ }
236
+ const properties = [...param.shape].sort((a, b) => a.name.localeCompare(b.name)).map((field) => {
237
+ const zodType = param.partial ? mapParamToZodWithoutOptional(field, context) : mapParamToZod(field, context);
238
+ return `${field.name}: ${zodType}`;
239
+ }).join(", ");
240
+ const base = `z.object({ ${properties} })`;
241
+ return param.partial ? `${base}.partial()` : base;
242
+ }
243
+ function mapUnion(param, context) {
244
+ if (param.discriminator) {
245
+ const variants2 = param.variants.map(
246
+ (variant) => mapParamToZod(variant, context)
247
+ );
248
+ return `z.discriminatedUnion('${param.discriminator}', [${variants2.join(", ")}])`;
249
+ }
250
+ const variants = param.variants.map(
251
+ (variant) => mapParamToZod(variant, context)
252
+ );
253
+ return `z.union([${variants.join(", ")}])`;
254
+ }
255
+ function applyModifiers(base, param) {
256
+ if (param.nullable && hasDefault(param)) {
257
+ return `${base}.nullable().default(${serializeDefault(param)})`;
258
+ }
259
+ if (hasDefault(param)) {
260
+ return `${base}.default(${serializeDefault(param)})`;
261
+ }
262
+ if (param.nullable && param.optional) {
263
+ return `${base}.nullable().optional()`;
264
+ }
265
+ if (param.optional) {
266
+ return `${base}.optional()`;
267
+ }
268
+ if (param.nullable) {
269
+ return `${base}.nullable()`;
270
+ }
271
+ return base;
272
+ }
273
+ function hasDefault(param) {
274
+ if (!("default" in param)) {
275
+ return false;
276
+ }
277
+ if (param.default === void 0) {
278
+ return false;
279
+ }
280
+ return true;
281
+ }
282
+ function serializeDefault(param) {
283
+ const value = "default" in param ? param.default : void 0;
284
+ if (value === null) {
285
+ return "null";
286
+ }
287
+ if (value === "") {
288
+ return "''";
289
+ }
290
+ if (typeof value === "string") {
291
+ return `'${value}'`;
292
+ }
293
+ if (typeof value === "boolean" || typeof value === "number") {
294
+ return String(value);
295
+ }
296
+ return JSON.stringify(value);
297
+ }
298
+ function mapParamToZodWithoutOptional(param, context) {
299
+ const base = mapParamBase(param, context);
300
+ if (param.nullable && hasDefault(param)) {
301
+ return `${base}.nullable().default(${serializeDefault(param)})`;
302
+ }
303
+ if (param.nullable) {
304
+ return `${base}.nullable()`;
305
+ }
306
+ if (hasDefault(param)) {
307
+ return `${base}.default(${serializeDefault(param)})`;
308
+ }
309
+ return base;
310
+ }
311
+
312
+ // src/codegen/references.ts
313
+ function collectTypeReferences(types, enums) {
314
+ const references = /* @__PURE__ */ new Set();
315
+ const localNames = /* @__PURE__ */ new Set();
316
+ for (const _enum of enums) {
317
+ localNames.add(_enum.name);
318
+ }
319
+ for (const type of types) {
320
+ localNames.add(type.name);
321
+ }
322
+ for (const type of types) {
323
+ if (type.type === "object") {
324
+ for (const param of type.shape) {
325
+ collectParamReferences(param, references);
326
+ }
327
+ } else {
328
+ for (const variant of type.variants) {
329
+ collectParamReferences(variant, references);
330
+ }
331
+ }
332
+ }
333
+ for (const name of localNames) {
334
+ references.delete(name);
335
+ }
336
+ return references;
337
+ }
338
+ function collectParamReferences(param, references) {
339
+ switch (param.type) {
340
+ case "reference":
341
+ references.add(param.reference);
342
+ break;
343
+ case "array":
344
+ if (param.of) {
345
+ collectParamReferences(param.of, references);
346
+ }
347
+ break;
348
+ case "record":
349
+ if (param.of) {
350
+ collectParamReferences(param.of, references);
351
+ }
352
+ break;
353
+ case "object":
354
+ for (const field of param.shape) {
355
+ collectParamReferences(field, references);
356
+ }
357
+ break;
358
+ case "union":
359
+ for (const variant of param.variants) {
360
+ collectParamReferences(variant, references);
361
+ }
362
+ break;
363
+ case "string":
364
+ case "integer":
365
+ case "number":
366
+ case "decimal":
367
+ case "boolean":
368
+ case "date":
369
+ case "datetime":
370
+ case "time":
371
+ case "uuid":
372
+ if (param.enum) {
373
+ references.add(param.enum);
374
+ }
375
+ break;
376
+ }
377
+ }
378
+
379
+ // src/codegen/endpoint.ts
380
+ function buildEndpoints(schema, scopeIndex, options, resolvedOptions) {
381
+ const files = /* @__PURE__ */ new Map();
382
+ const actionFiles = [];
383
+ const context = { options: resolvedOptions, scopeIndex };
384
+ traverseResources(schema.resources, [], actionFiles, options, context);
385
+ for (const actionFile of actionFiles) {
386
+ const content = buildActionFileContent(actionFile, options, context);
387
+ files.set(actionFile.filename, content);
388
+ }
389
+ if (actionFiles.length > 0) {
390
+ buildBarrelExports(actionFiles, files, resolvedOptions);
391
+ }
392
+ return files;
393
+ }
394
+ function buildActionFileContent(actionFile, options, context) {
395
+ const parts = [];
396
+ if (options.endpoints) {
397
+ parts.push("import type { Operation } from 'sorbus';");
398
+ parts.push("import * as z from 'zod';");
399
+ } else if (options.zod) {
400
+ parts.push("import * as z from 'zod';");
401
+ }
402
+ const contractImports = buildContractImports(
403
+ actionFile.references,
404
+ actionFile.depth,
405
+ options,
406
+ context
407
+ );
408
+ if (contractImports) {
409
+ if (parts.length > 0) {
410
+ parts.push("");
411
+ }
412
+ parts.push(contractImports);
413
+ }
414
+ if (options.reexportChildren) {
415
+ const extension = context.options.importExtension;
416
+ const resourceFileName = formatFileName(
417
+ actionFile.resourceName,
418
+ context.options.fileCase
419
+ );
420
+ for (const child of actionFile.childResources) {
421
+ const childFileName = formatFileName(child, context.options.fileCase);
422
+ const childOperationTree = resolveEndpointIdentifier(
423
+ `${pascalCase(child)}OperationTree`,
424
+ context
425
+ );
426
+ const childConstName = resolveEndpointIdentifier(
427
+ camelCase(child),
428
+ context
429
+ );
430
+ parts.push("");
431
+ parts.push(
432
+ `import type { ${childOperationTree} } from './${resourceFileName}/${childFileName}${extension}';`
433
+ );
434
+ parts.push(
435
+ `import { ${childConstName} } from './${resourceFileName}/${childFileName}${extension}';`
436
+ );
437
+ }
438
+ }
439
+ if (actionFile.schemaLines.length > 0) {
440
+ parts.push("");
441
+ parts.push(actionFile.schemaLines.join("\n\n"));
442
+ }
443
+ if (actionFile.typeLines.length > 0) {
444
+ parts.push("");
445
+ parts.push(actionFile.typeLines.join("\n\n"));
446
+ }
447
+ if (options.endpoints) {
448
+ parts.push("");
449
+ const members = [];
450
+ for (const line of actionFile.endpointLines) {
451
+ members.push(line);
452
+ }
453
+ for (const child of actionFile.childResources) {
454
+ const childConstName = resolveEndpointIdentifier(
455
+ camelCase(child),
456
+ context
457
+ );
458
+ members.push(` ${childConstName},`);
459
+ }
460
+ const resourceConstName = resolveEndpointIdentifier(
461
+ camelCase(actionFile.resourceName),
462
+ context
463
+ );
464
+ parts.push(`export const ${resourceConstName} = {`);
465
+ parts.push(members.join("\n"));
466
+ parts.push("} as const;");
467
+ parts.push("");
468
+ const interfaceMembers = [];
469
+ for (const line of actionFile.interfaceLines) {
470
+ interfaceMembers.push(line);
471
+ }
472
+ for (const child of actionFile.childResources) {
473
+ const childMemberName = camelCase(child);
474
+ const childOperationTree = resolveEndpointIdentifier(
475
+ `${pascalCase(child)}OperationTree`,
476
+ context
477
+ );
478
+ interfaceMembers.push(` ${childMemberName}: ${childOperationTree};`);
479
+ }
480
+ const operationTreeName = resolveEndpointIdentifier(
481
+ `${pascalCase(actionFile.resourceName)}OperationTree`,
482
+ context
483
+ );
484
+ parts.push(`export interface ${operationTreeName} {`);
485
+ parts.push(interfaceMembers.join("\n"));
486
+ parts.push("}");
487
+ }
488
+ return `${parts.join("\n")}
489
+ `;
490
+ }
491
+ function traverseResources(resources, parentIdentifiers, actionFiles, options, context) {
492
+ for (const resource of resources) {
493
+ const schemaLines = [];
494
+ const typeLines = [];
495
+ const endpointLines = [];
496
+ const interfaceLines = [];
497
+ const references = /* @__PURE__ */ new Set();
498
+ const childResources = [];
499
+ for (const action of resource.actions) {
500
+ generateActionContent(
501
+ action,
502
+ parentIdentifiers,
503
+ resource.identifier,
504
+ schemaLines,
505
+ typeLines,
506
+ endpointLines,
507
+ interfaceLines,
508
+ references,
509
+ options,
510
+ context
511
+ );
512
+ }
513
+ if (options.reexportChildren) {
514
+ for (const childResource of resource.resources) {
515
+ childResources.push(childResource.identifier);
516
+ }
517
+ }
518
+ if (schemaLines.length > 0 || typeLines.length > 0 || childResources.length > 0) {
519
+ const directoryPath = parentIdentifiers.map(
520
+ (identifier) => formatFileName(identifier, context.options.fileCase)
521
+ ).join("/");
522
+ const fileName = formatFileName(
523
+ resource.identifier,
524
+ context.options.fileCase
525
+ );
526
+ const filename = directoryPath ? `endpoints/${directoryPath}/${fileName}.ts` : `endpoints/${fileName}.ts`;
527
+ const depth = filename.split("/").length - 1;
528
+ actionFiles.push({
529
+ childResources,
530
+ depth,
531
+ endpointLines,
532
+ filename,
533
+ interfaceLines,
534
+ references,
535
+ resourceName: resource.identifier,
536
+ schemaLines,
537
+ typeLines
538
+ });
539
+ }
540
+ if (resource.resources.length > 0) {
541
+ traverseResources(
542
+ resource.resources,
543
+ [...parentIdentifiers, resource.identifier],
544
+ actionFiles,
545
+ options,
546
+ context
547
+ );
548
+ }
549
+ }
550
+ }
551
+ function analyzeAction(action, parentIdentifiers, resourceIdentifier, context) {
552
+ const segments = action.name.split(".");
553
+ const actionName = segments[segments.length - 1];
554
+ const responseBody = action.response.body && !action.response.noContent ? action.response.body : null;
555
+ const typeName = resolveEndpointIdentifier(
556
+ pascalCase(
557
+ [...parentIdentifiers, resourceIdentifier, actionName].join("_")
558
+ ),
559
+ context
560
+ );
561
+ return {
562
+ actionName,
563
+ errorCodes: action.raises.map((code) => resolveErrorStatus(code)).filter((status) => status !== null).sort((a, b) => a - b),
564
+ method: action.method.toUpperCase(),
565
+ path: action.path,
566
+ pathParams: extractPathParams(action.path),
567
+ requestBody: action.request.body,
568
+ requestQuery: action.request.query,
569
+ responseBody,
570
+ typeName
571
+ };
572
+ }
573
+ function generateActionContent(action, parentIdentifiers, resourceIdentifier, schemaLines, typeLines, endpointLines, interfaceLines, references, options, context) {
574
+ const parts = analyzeAction(
575
+ action,
576
+ parentIdentifiers,
577
+ resourceIdentifier,
578
+ context
579
+ );
580
+ generateActionTypes(parts, typeLines, references, context);
581
+ if (options.zod) {
582
+ generateActionSchemas(parts, schemaLines, references, context);
583
+ }
584
+ if (options.endpoints) {
585
+ endpointLines.push(generateRuntimeEndpoint(parts));
586
+ interfaceLines.push(generateOperationMember(parts));
587
+ }
588
+ }
589
+ function generateActionTypes(parts, lines, references, context) {
590
+ const { typeName } = parts;
591
+ lines.push(`export type ${typeName}Method = '${parts.method}';`);
592
+ lines.push(`export type ${typeName}Path = '${parts.path}';`);
593
+ if (parts.pathParams.length > 0) {
594
+ const fields = parts.pathParams.map((param) => `${camelCase(param)}: string`).join("; ");
595
+ lines.push(`export interface ${typeName}PathParams { ${fields} }`);
596
+ }
597
+ if (parts.requestQuery.length > 0) {
598
+ lines.push(
599
+ generateRequestQueryType(
600
+ typeName,
601
+ parts.requestQuery,
602
+ references,
603
+ context
604
+ )
605
+ );
606
+ }
607
+ if (parts.requestBody.length > 0) {
608
+ lines.push(
609
+ generateRequestBodyType(typeName, parts.requestBody, references, context)
610
+ );
611
+ }
612
+ if (parts.responseBody) {
613
+ lines.push(
614
+ generateResponseBodyType(
615
+ typeName,
616
+ parts.responseBody,
617
+ references,
618
+ context
619
+ )
620
+ );
621
+ }
622
+ if (parts.requestQuery.length > 0 || parts.requestBody.length > 0) {
623
+ const requestFields = [];
624
+ if (parts.requestBody.length > 0) {
625
+ requestFields.push(`body: ${typeName}RequestBody`);
626
+ }
627
+ if (parts.requestQuery.length > 0) {
628
+ requestFields.push(`query: ${typeName}RequestQuery`);
629
+ }
630
+ lines.push(
631
+ `export interface ${typeName}Request { ${requestFields.join("; ")} }`
632
+ );
633
+ }
634
+ if (parts.responseBody) {
635
+ lines.push(
636
+ `export interface ${typeName}Response { body: ${typeName}ResponseBody }`
637
+ );
638
+ }
639
+ if (parts.errorCodes.length > 0) {
640
+ lines.push(
641
+ `export type ${typeName}Errors = ${parts.errorCodes.join(" | ")};`
642
+ );
643
+ }
644
+ lines.push(generateDefinitionType(parts));
645
+ }
646
+ function generateDefinitionType(parts) {
647
+ const { typeName } = parts;
648
+ const fields = [];
649
+ fields.push(`method: ${typeName}Method`);
650
+ fields.push(`path: ${typeName}Path`);
651
+ if (parts.pathParams.length > 0) {
652
+ fields.push(`pathParams: ${typeName}PathParams`);
653
+ }
654
+ if (parts.requestQuery.length > 0 || parts.requestBody.length > 0) {
655
+ fields.push(`request: ${typeName}Request`);
656
+ }
657
+ if (parts.responseBody) {
658
+ fields.push(`response: ${typeName}Response`);
659
+ }
660
+ if (parts.errorCodes.length > 0) {
661
+ fields.push(`errors: ${typeName}Errors`);
662
+ }
663
+ return `export interface ${typeName} { ${fields.join("; ")} }`;
664
+ }
665
+ function generateActionSchemas(parts, lines, references, context) {
666
+ const { typeName } = parts;
667
+ if (parts.pathParams.length > 0) {
668
+ const fields = parts.pathParams.map((param) => `${camelCase(param)}: z.string()`).join(", ");
669
+ lines.push(
670
+ `export const ${typeName}PathParamsSchema = z.object({ ${fields} });`
671
+ );
672
+ }
673
+ if (parts.requestQuery.length > 0) {
674
+ lines.push(
675
+ generateRequestQuerySchema(
676
+ typeName,
677
+ parts.requestQuery,
678
+ references,
679
+ context
680
+ )
681
+ );
682
+ }
683
+ if (parts.requestBody.length > 0) {
684
+ lines.push(
685
+ generateRequestBodySchema(
686
+ typeName,
687
+ parts.requestBody,
688
+ references,
689
+ context
690
+ )
691
+ );
692
+ }
693
+ if (parts.responseBody) {
694
+ lines.push(
695
+ generateResponseBodySchema(
696
+ typeName,
697
+ parts.responseBody,
698
+ references,
699
+ context
700
+ )
701
+ );
702
+ }
703
+ }
704
+ function generateRuntimeEndpoint(parts) {
705
+ const { actionName, typeName } = parts;
706
+ const lines = [];
707
+ lines.push(` ${camelCase(actionName)}: {`);
708
+ lines.push(` method: '${parts.method}',`);
709
+ lines.push(` path: '${parts.path}',`);
710
+ if (parts.pathParams.length > 0) {
711
+ lines.push(` pathParams: ${typeName}PathParamsSchema,`);
712
+ }
713
+ if (parts.requestQuery.length > 0 || parts.requestBody.length > 0) {
714
+ lines.push(" request: {");
715
+ if (parts.requestQuery.length > 0) {
716
+ lines.push(` query: ${typeName}RequestQuerySchema,`);
717
+ }
718
+ if (parts.requestBody.length > 0) {
719
+ lines.push(` body: ${typeName}RequestBodySchema,`);
720
+ }
721
+ lines.push(" },");
722
+ }
723
+ if (parts.responseBody) {
724
+ lines.push(" response: {");
725
+ lines.push(` body: ${typeName}ResponseBodySchema,`);
726
+ lines.push(" },");
727
+ }
728
+ if (parts.errorCodes.length > 0) {
729
+ lines.push(` errors: [${parts.errorCodes.join(", ")}],`);
730
+ }
731
+ lines.push(" },");
732
+ return lines.join("\n");
733
+ }
734
+ function generateOperationMember(parts) {
735
+ return ` ${camelCase(parts.actionName)}: Operation<${parts.typeName}>;`;
736
+ }
737
+ function extractPathParams(path) {
738
+ const matches = path.match(/:(\w+)/g);
739
+ if (!matches) {
740
+ return [];
741
+ }
742
+ return matches.map((match) => match.slice(1));
743
+ }
744
+ var ERROR_STATUS_MAP = {
745
+ bad_request: 400,
746
+ conflict: 409,
747
+ forbidden: 403,
748
+ internal_server_error: 500,
749
+ not_found: 404,
750
+ unauthorized: 401,
751
+ unprocessable_entity: 422
752
+ };
753
+ function resolveErrorStatus(code) {
754
+ return ERROR_STATUS_MAP[code] ?? null;
755
+ }
756
+ function generateRequestQuerySchema(typeName, params, references, context) {
757
+ const fields = params.sort((a, b) => a.name.localeCompare(b.name)).map((param) => {
758
+ collectParamReferences(param, references);
759
+ return ` ${param.name}: ${mapParamToZod(param, context)},`;
760
+ }).join("\n");
761
+ return `export const ${typeName}RequestQuerySchema = z.object({
762
+ ${fields}
763
+ });`;
764
+ }
765
+ function generateRequestBodySchema(typeName, params, references, context) {
766
+ const fields = params.sort((a, b) => a.name.localeCompare(b.name)).map((param) => {
767
+ collectParamReferences(param, references);
768
+ return ` ${param.name}: ${mapParamToZod(param, context)},`;
769
+ }).join("\n");
770
+ return `export const ${typeName}RequestBodySchema = z.object({
771
+ ${fields}
772
+ });`;
773
+ }
774
+ function generateResponseBodySchema(typeName, body, references, context) {
775
+ collectParamReferences(body, references);
776
+ return `export const ${typeName}ResponseBodySchema = ${mapParamToZod(body, context)};`;
777
+ }
778
+ function generateRequestQueryType(typeName, params, references, context) {
779
+ const fields = params.sort((a, b) => a.name.localeCompare(b.name)).map((param) => {
780
+ collectParamReferences(param, references);
781
+ const optional = isOptionalField(param) ? "?" : "";
782
+ return ` ${param.name}${optional}: ${mapParamToType(param, context)};`;
783
+ }).join("\n");
784
+ return `export interface ${typeName}RequestQuery {
785
+ ${fields}
786
+ }`;
787
+ }
788
+ function generateRequestBodyType(typeName, params, references, context) {
789
+ const fields = params.sort((a, b) => a.name.localeCompare(b.name)).map((param) => {
790
+ collectParamReferences(param, references);
791
+ const optional = isOptionalField(param) ? "?" : "";
792
+ return ` ${param.name}${optional}: ${mapParamToType(param, context)};`;
793
+ }).join("\n");
794
+ return `export interface ${typeName}RequestBody {
795
+ ${fields}
796
+ }`;
797
+ }
798
+ function generateResponseBodyType(typeName, body, references, context) {
799
+ collectParamReferences(body, references);
800
+ return `export type ${typeName}ResponseBody = ${mapParamToType(body, context)};`;
801
+ }
802
+ function buildContractImports(references, depth, options, context) {
803
+ const importsByFile = /* @__PURE__ */ new Map();
804
+ const prefix = "../".repeat(depth);
805
+ const extension = context.options.importExtension;
806
+ for (const reference of references) {
807
+ const scope = context.scopeIndex.get(reference);
808
+ if (scope === void 0) {
809
+ continue;
810
+ }
811
+ const filename = scope === null ? `${prefix}api${extension}` : `${prefix}domains/${formatFileName(scope, context.options.fileCase)}${extension}`;
812
+ let entry = importsByFile.get(filename);
813
+ if (!entry) {
814
+ entry = { schemas: [], types: [] };
815
+ importsByFile.set(filename, entry);
816
+ }
817
+ const name = resolveDomainIdentifier(reference, context);
818
+ if (options.zod) {
819
+ entry.schemas.push(`${name}Schema`);
820
+ }
821
+ entry.types.push(name);
822
+ }
823
+ const lines = [];
824
+ for (const [filename, entry] of [...importsByFile.entries()].sort(
825
+ ([a], [b]) => a.localeCompare(b)
826
+ )) {
827
+ if (options.zod) {
828
+ lines.push(
829
+ `import type { ${entry.types.sort().join(", ")} } from '${filename}';`
830
+ );
831
+ lines.push(
832
+ `import { ${entry.schemas.sort().join(", ")} } from '${filename}';`
833
+ );
834
+ } else {
835
+ lines.push(
836
+ `import type { ${entry.types.sort().join(", ")} } from '${filename}';`
837
+ );
838
+ }
839
+ }
840
+ return lines.join("\n");
841
+ }
842
+ function buildBarrelExports(actionFiles, files, resolvedOptions) {
843
+ const directories = /* @__PURE__ */ new Map();
844
+ for (const actionFile of actionFiles) {
845
+ const parts = actionFile.filename.split("/");
846
+ const filename = parts.pop();
847
+ if (!filename) {
848
+ throw new Error(`Invalid action file path: ${actionFile.filename}`);
849
+ }
850
+ const file = filename.replace(".ts", "");
851
+ const directory = parts.join("/");
852
+ let entries = directories.get(directory);
853
+ if (!entries) {
854
+ entries = /* @__PURE__ */ new Set();
855
+ directories.set(directory, entries);
856
+ }
857
+ entries.add(file);
858
+ }
859
+ for (const directory of [...directories.keys()]) {
860
+ const parentParts = directory.split("/");
861
+ if (parentParts.length > 1) {
862
+ const parentDirectory = parentParts.slice(0, -1).join("/");
863
+ const childDirectory = parentParts[parentParts.length - 1];
864
+ let parentEntries = directories.get(parentDirectory);
865
+ if (!parentEntries) {
866
+ parentEntries = /* @__PURE__ */ new Set();
867
+ directories.set(parentDirectory, parentEntries);
868
+ }
869
+ parentEntries.add(childDirectory);
870
+ }
871
+ }
872
+ for (const [directory, entries] of directories) {
873
+ const lines = [...entries].sort().map(
874
+ (entry) => `export * from './${entry}${resolvedOptions.importExtension}';`
875
+ );
876
+ files.set(`${directory}/index.ts`, `${lines.join("\n")}
877
+ `);
878
+ }
879
+ }
880
+
881
+ // src/codegen/fs.ts
882
+ import { mkdir, writeFile } from "fs/promises";
883
+ import { dirname, join } from "path";
884
+ async function writeFiles(outdir, files) {
885
+ for (const [filename, content] of files) {
886
+ const filepath = join(outdir, filename);
887
+ await mkdir(dirname(filepath), { recursive: true });
888
+ await writeFile(filepath, content);
889
+ }
890
+ }
891
+
892
+ // src/typescript/mapper.ts
893
+ function generateEnum(_enum, context) {
894
+ const name = resolveDomainIdentifier(_enum.name, context);
895
+ const values = _enum.values.map((value) => `'${value}'`).join(" | ");
896
+ return `export type ${name} = ${values};`;
897
+ }
898
+ function generateInterface(type, context) {
899
+ const name = resolveDomainIdentifier(type.name, context);
900
+ const fields = [...type.shape].sort((a, b) => a.name.localeCompare(b.name)).map((param) => {
901
+ const optional = isOptionalField(param) ? "?" : "";
902
+ const typescriptType = mapParamToType(param, context);
903
+ return ` ${param.name}${optional}: ${typescriptType};`;
904
+ }).join("\n");
905
+ if (type.extends.length > 0) {
906
+ const bases = type.extends.map((base) => resolveDomainIdentifier(base, context)).join(", ");
907
+ return `export interface ${name} extends ${bases} {
908
+ ${fields}
909
+ }`;
910
+ }
911
+ return `export interface ${name} {
912
+ ${fields}
913
+ }`;
914
+ }
915
+ function generateUnionType(type, context) {
916
+ const name = resolveDomainIdentifier(type.name, context);
917
+ if (type.discriminator) {
918
+ const variants2 = type.variants.map((variant) => {
919
+ const base = mapParamToType(variant, context);
920
+ const tag = "tag" in variant ? variant.tag : null;
921
+ if (tag) {
922
+ return `${base} & { ${type.discriminator}: '${tag}' }`;
923
+ }
924
+ return base;
925
+ });
926
+ return `export type ${name} =
927
+ | ${variants2.join("\n | ")};`;
928
+ }
929
+ const variants = type.variants.map(
930
+ (variant) => mapParamToType(variant, context)
931
+ );
932
+ return `export type ${name} = ${variants.join(" | ")};`;
933
+ }
934
+ function mapParamToType(param, context) {
935
+ const base = mapBaseType(param, context);
936
+ return param.nullable ? `${base} | null` : base;
937
+ }
938
+ function mapBaseType(param, context) {
939
+ switch (param.type) {
940
+ case "string":
941
+ return param.enum ? resolveDomainIdentifier(param.enum, context) : "string";
942
+ case "integer":
943
+ return param.enum ? resolveDomainIdentifier(param.enum, context) : "number";
944
+ case "number":
945
+ case "decimal":
946
+ return "number";
947
+ case "boolean":
948
+ return "boolean";
949
+ case "date":
950
+ case "datetime":
951
+ case "time":
952
+ return "string";
953
+ case "uuid":
954
+ return "string";
955
+ case "binary":
956
+ return "string";
957
+ case "literal":
958
+ return typeof param.value === "string" ? `'${param.value}'` : String(param.value);
959
+ case "array":
960
+ return param.of ? `${mapParamToType(param.of, context)}[]` : "unknown[]";
961
+ case "record":
962
+ return param.of ? `Record<string, ${mapParamToType(param.of, context)}>` : "Record<string, unknown>";
963
+ case "object":
964
+ return mapObjectParam(param, context);
965
+ case "union":
966
+ return param.variants.map((variant) => mapParamToType(variant, context)).join(" | ");
967
+ case "reference":
968
+ return resolveDomainIdentifier(param.reference, context);
969
+ case "unknown":
970
+ return "unknown";
971
+ }
972
+ }
973
+ function mapObjectParam(param, context) {
974
+ if (param.shape.length === 0) {
975
+ return "Record<string, unknown>";
976
+ }
977
+ const fields = [...param.shape].sort((a, b) => a.name.localeCompare(b.name)).map((field) => {
978
+ const optional = param.partial || isOptionalField(field) ? "?" : "";
979
+ return `${field.name}${optional}: ${mapParamToType(field, context)}`;
980
+ }).join("; ");
981
+ return `{ ${fields} }`;
982
+ }
983
+ function isOptionalField(param) {
984
+ return param.optional === true;
985
+ }
986
+
987
+ // src/codegen/domain.ts
988
+ function buildScopeIndex(schema) {
989
+ const index = /* @__PURE__ */ new Map();
990
+ for (const _enum of schema.enums) {
991
+ index.set(_enum.name, _enum.scope);
992
+ }
993
+ for (const type of schema.types) {
994
+ index.set(type.name, type.scope);
995
+ }
996
+ return index;
997
+ }
998
+ function buildSchemas(schema, options, resolvedOptions) {
999
+ const files = /* @__PURE__ */ new Map();
1000
+ const groups = groupByScope(schema);
1001
+ const scopeIndex = buildScopeIndex(schema);
1002
+ const context = { options: resolvedOptions, scopeIndex };
1003
+ for (const [scope, group] of groups) {
1004
+ const filename = scope === null ? "api.ts" : `domains/${formatFileName(scope, resolvedOptions.fileCase)}.ts`;
1005
+ const content = buildFile(
1006
+ scope,
1007
+ group.enums,
1008
+ group.types,
1009
+ options,
1010
+ context
1011
+ );
1012
+ files.set(filename, content);
1013
+ }
1014
+ const barrelLines = [];
1015
+ for (const scope of [...groups.keys()].filter((scope2) => scope2 !== null).sort()) {
1016
+ barrelLines.push(
1017
+ `export * from './${formatFileName(scope, resolvedOptions.fileCase)}${resolvedOptions.importExtension}';`
1018
+ );
1019
+ }
1020
+ if (barrelLines.length > 0) {
1021
+ files.set("domains/index.ts", `${barrelLines.join("\n")}
1022
+ `);
1023
+ }
1024
+ return files;
1025
+ }
1026
+ function groupByScope(schema) {
1027
+ const groups = /* @__PURE__ */ new Map();
1028
+ function getGroup(scope) {
1029
+ let group = groups.get(scope);
1030
+ if (!group) {
1031
+ group = { enums: [], types: [] };
1032
+ groups.set(scope, group);
1033
+ }
1034
+ return group;
1035
+ }
1036
+ for (const _enum of schema.enums) {
1037
+ getGroup(_enum.scope).enums.push(_enum);
1038
+ }
1039
+ for (const type of schema.types) {
1040
+ getGroup(type.scope).types.push(type);
1041
+ }
1042
+ return groups;
1043
+ }
1044
+ function buildFile(scope, enums, types, options, context) {
1045
+ const references = collectTypeReferences(types, enums);
1046
+ const imports = buildImports(scope, references, options, context);
1047
+ const lines = [];
1048
+ if (options.zod) {
1049
+ lines.push("import * as z from 'zod';");
1050
+ if (imports) {
1051
+ lines.push("");
1052
+ }
1053
+ }
1054
+ if (imports) {
1055
+ lines.push(imports);
1056
+ }
1057
+ if (lines.length > 0) {
1058
+ lines.push("");
1059
+ }
1060
+ if (options.zod) {
1061
+ for (const _enum of enums) {
1062
+ lines.push(generateEnumSchema(_enum, context));
1063
+ lines.push("");
1064
+ }
1065
+ for (const type of types) {
1066
+ if (type.type === "object") {
1067
+ lines.push(generateObjectSchema(type, context));
1068
+ } else {
1069
+ lines.push(generateUnionSchema(type, context));
1070
+ }
1071
+ lines.push("");
1072
+ }
1073
+ }
1074
+ for (const _enum of enums) {
1075
+ lines.push(generateEnum(_enum, context));
1076
+ lines.push("");
1077
+ }
1078
+ for (const type of types) {
1079
+ if (type.type === "object") {
1080
+ lines.push(generateInterface(type, context));
1081
+ } else {
1082
+ lines.push(generateUnionType(type, context));
1083
+ }
1084
+ lines.push("");
1085
+ }
1086
+ return `${lines.join("\n").trim()}
1087
+ `;
1088
+ }
1089
+ function resolveImportPath(currentScope, targetScope, resolvedOptions) {
1090
+ const extension = resolvedOptions.importExtension;
1091
+ if (currentScope === null) {
1092
+ return `./domains/${formatFileName(targetScope, resolvedOptions.fileCase)}${extension}`;
1093
+ }
1094
+ if (targetScope === null) {
1095
+ return `../api${extension}`;
1096
+ }
1097
+ return `./${formatFileName(targetScope, resolvedOptions.fileCase)}${extension}`;
1098
+ }
1099
+ function buildImports(currentScope, references, options, context) {
1100
+ const importsByFile = /* @__PURE__ */ new Map();
1101
+ for (const reference of references) {
1102
+ const targetScope = context.scopeIndex.get(reference);
1103
+ if (targetScope === void 0 || targetScope === currentScope) {
1104
+ continue;
1105
+ }
1106
+ const filename = resolveImportPath(
1107
+ currentScope,
1108
+ targetScope,
1109
+ context.options
1110
+ );
1111
+ let entry = importsByFile.get(filename);
1112
+ if (!entry) {
1113
+ entry = { schemas: [], types: [] };
1114
+ importsByFile.set(filename, entry);
1115
+ }
1116
+ const name = resolveDomainIdentifier(reference, context);
1117
+ if (options.zod) {
1118
+ entry.schemas.push(`${name}Schema`);
1119
+ }
1120
+ entry.types.push(name);
1121
+ }
1122
+ const lines = [];
1123
+ for (const [filename, entry] of [...importsByFile.entries()].sort(
1124
+ ([a], [b]) => a.localeCompare(b)
1125
+ )) {
1126
+ if (options.zod) {
1127
+ const allImports = [
1128
+ ...entry.schemas.sort(),
1129
+ ...entry.types.sort().map((name) => `type ${name}`)
1130
+ ];
1131
+ lines.push(`import { ${allImports.join(", ")} } from '${filename}';`);
1132
+ } else {
1133
+ lines.push(
1134
+ `import type { ${entry.types.sort().join(", ")} } from '${filename}';`
1135
+ );
1136
+ }
1137
+ }
1138
+ return lines.join("\n");
1139
+ }
1140
+
1141
+ export {
1142
+ pascalCase,
1143
+ resolveGenerateOptions,
1144
+ resolveDomainIdentifier,
1145
+ resolveEndpointIdentifier,
1146
+ resolveClientIdentifier,
1147
+ buildScopeIndex,
1148
+ buildSchemas,
1149
+ buildEndpoints,
1150
+ writeFiles
1151
+ };