zenko 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,702 @@
1
+ // src/utils/topological-sort.ts
2
+ function topologicalSort(schemas) {
3
+ const visited = /* @__PURE__ */ new Set();
4
+ const visiting = /* @__PURE__ */ new Set();
5
+ const result = [];
6
+ const visit = (name) => {
7
+ if (visited.has(name)) return;
8
+ if (visiting.has(name)) {
9
+ return;
10
+ }
11
+ visiting.add(name);
12
+ const schema = schemas[name];
13
+ const dependencies = extractDependencies(schema);
14
+ for (const dep of dependencies) {
15
+ if (schemas[dep]) {
16
+ visit(dep);
17
+ }
18
+ }
19
+ visiting.delete(name);
20
+ visited.add(name);
21
+ result.push(name);
22
+ };
23
+ for (const name of Object.keys(schemas)) {
24
+ visit(name);
25
+ }
26
+ return result;
27
+ }
28
+ function extractDependencies(schema) {
29
+ const dependencies = [];
30
+ const traverse = (obj) => {
31
+ if (typeof obj !== "object" || obj === null) return;
32
+ if (obj.$ref && typeof obj.$ref === "string") {
33
+ const refName = extractRefName(obj.$ref);
34
+ dependencies.push(refName);
35
+ return;
36
+ }
37
+ if (Array.isArray(obj)) {
38
+ obj.forEach(traverse);
39
+ } else {
40
+ Object.values(obj).forEach(traverse);
41
+ }
42
+ };
43
+ traverse(schema);
44
+ return [...new Set(dependencies)];
45
+ }
46
+ function extractRefName(ref) {
47
+ return ref.split("/").pop() || "Unknown";
48
+ }
49
+
50
+ // src/utils/property-name.ts
51
+ function isValidJSIdentifier(name) {
52
+ if (!name) return false;
53
+ const firstChar = name.at(0);
54
+ if (firstChar === void 0) return false;
55
+ if (!/[a-zA-Z_$]/.test(firstChar)) return false;
56
+ if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) return false;
57
+ const reservedWords = /* @__PURE__ */ new Set([
58
+ "abstract",
59
+ "arguments",
60
+ "await",
61
+ "boolean",
62
+ "break",
63
+ "byte",
64
+ "case",
65
+ "catch",
66
+ "char",
67
+ "class",
68
+ "const",
69
+ "continue",
70
+ "debugger",
71
+ "default",
72
+ "delete",
73
+ "do",
74
+ "double",
75
+ "else",
76
+ "enum",
77
+ "eval",
78
+ "export",
79
+ "extends",
80
+ "false",
81
+ "final",
82
+ "finally",
83
+ "float",
84
+ "for",
85
+ "function",
86
+ "goto",
87
+ "if",
88
+ "implements",
89
+ "import",
90
+ "in",
91
+ "instanceof",
92
+ "int",
93
+ "interface",
94
+ "let",
95
+ "long",
96
+ "native",
97
+ "new",
98
+ "null",
99
+ "package",
100
+ "private",
101
+ "protected",
102
+ "public",
103
+ "return",
104
+ "short",
105
+ "static",
106
+ "super",
107
+ "switch",
108
+ "synchronized",
109
+ "this",
110
+ "throw",
111
+ "throws",
112
+ "transient",
113
+ "true",
114
+ "try",
115
+ "typeof",
116
+ "var",
117
+ "void",
118
+ "volatile",
119
+ "while",
120
+ "with",
121
+ "yield",
122
+ "async"
123
+ ]);
124
+ return !reservedWords.has(name);
125
+ }
126
+ function formatPropertyName(name) {
127
+ return isValidJSIdentifier(name) ? name : `"${name}"`;
128
+ }
129
+
130
+ // src/utils/http-status.ts
131
+ var statusNameMap = {
132
+ "400": "badRequest",
133
+ "401": "unauthorized",
134
+ "402": "paymentRequired",
135
+ "403": "forbidden",
136
+ "404": "notFound",
137
+ "405": "methodNotAllowed",
138
+ "406": "notAcceptable",
139
+ "407": "proxyAuthenticationRequired",
140
+ "408": "requestTimeout",
141
+ "409": "conflict",
142
+ "410": "gone",
143
+ "411": "lengthRequired",
144
+ "412": "preconditionFailed",
145
+ "413": "payloadTooLarge",
146
+ "414": "uriTooLong",
147
+ "415": "unsupportedMediaType",
148
+ "416": "rangeNotSatisfiable",
149
+ "417": "expectationFailed",
150
+ "418": "imATeapot",
151
+ "421": "misdirectedRequest",
152
+ "422": "unprocessableEntity",
153
+ "423": "locked",
154
+ "424": "failedDependency",
155
+ "425": "tooEarly",
156
+ "426": "upgradeRequired",
157
+ "428": "preconditionRequired",
158
+ "429": "tooManyRequests",
159
+ "431": "requestHeaderFieldsTooLarge",
160
+ "451": "unavailableForLegalReasons",
161
+ "500": "internalServerError",
162
+ "501": "notImplemented",
163
+ "502": "badGateway",
164
+ "503": "serviceUnavailable",
165
+ "504": "gatewayTimeout",
166
+ "505": "httpVersionNotSupported",
167
+ "506": "variantAlsoNegotiates",
168
+ "507": "insufficientStorage",
169
+ "508": "loopDetected",
170
+ "510": "notExtended",
171
+ "511": "networkAuthenticationRequired"
172
+ };
173
+ function mapStatusToIdentifier(status) {
174
+ if (status === "default") return "defaultError";
175
+ const trimmed = status.trim();
176
+ const mapped = statusNameMap[trimmed];
177
+ if (mapped) return mapped;
178
+ if (/^\d{3}$/.test(trimmed)) {
179
+ return `status${trimmed}`;
180
+ }
181
+ const sanitized = trimmed.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
182
+ if (!sanitized) return "unknownError";
183
+ const parts = sanitized.split(/\s+/);
184
+ const [first, ...rest] = parts;
185
+ const candidate = first + rest.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
186
+ if (!candidate) return "unknownError";
187
+ return /^[a-zA-Z_$]/.test(candidate) ? candidate : `status${candidate.charAt(0).toUpperCase()}${candidate.slice(1)}`;
188
+ }
189
+ function getStatusCategory(status) {
190
+ if (status === "default") return "default";
191
+ const code = Number(status);
192
+ if (!Number.isInteger(code)) return "unknown";
193
+ if (code >= 400 && code <= 499) return "client";
194
+ if (code >= 500 && code <= 599) return "server";
195
+ return "unknown";
196
+ }
197
+ function isErrorStatus(status) {
198
+ if (status === "default") return true;
199
+ const code = Number(status);
200
+ if (!Number.isInteger(code)) return false;
201
+ return code >= 400;
202
+ }
203
+
204
+ // src/zenko.ts
205
+ function generate(spec, options = {}) {
206
+ const output = [];
207
+ const generatedTypes = /* @__PURE__ */ new Set();
208
+ const { strictDates = false, strictNumeric = false } = options;
209
+ const schemaOptions = {
210
+ strictDates,
211
+ strictNumeric
212
+ };
213
+ output.push('import { z } from "zod";');
214
+ output.push("");
215
+ if (spec.components?.schemas) {
216
+ output.push("// Generated Zod Schemas");
217
+ output.push("");
218
+ const sortedSchemas = topologicalSort(spec.components.schemas);
219
+ for (const name of sortedSchemas) {
220
+ const schema = spec.components.schemas[name];
221
+ output.push(
222
+ generateZodSchema(name, schema, generatedTypes, schemaOptions)
223
+ );
224
+ output.push("");
225
+ output.push(`export type ${name} = z.infer<typeof ${name}>;`);
226
+ output.push("");
227
+ }
228
+ }
229
+ const operations = parseOperations(spec);
230
+ output.push("// Path Functions");
231
+ output.push("export const paths = {");
232
+ for (const op of operations) {
233
+ if (op.pathParams.length === 0) {
234
+ output.push(` ${op.operationId}: () => "${op.path}",`);
235
+ } else {
236
+ const paramNames = op.pathParams.map((p) => p.name).join(", ");
237
+ const paramTypes = op.pathParams.map((p) => `${p.name}: string`).join(", ");
238
+ const pathWithParams = op.path.replace(/{([^}]+)}/g, "${$1}");
239
+ output.push(
240
+ ` ${op.operationId}: ({ ${paramNames} }: { ${paramTypes} }) => \`${pathWithParams}\`,`
241
+ );
242
+ }
243
+ }
244
+ output.push("} as const;");
245
+ output.push("");
246
+ output.push("// Header Functions");
247
+ output.push("export const headers = {");
248
+ for (const op of operations) {
249
+ if (!op.requestHeaders || op.requestHeaders.length === 0) {
250
+ output.push(` ${op.operationId}: () => ({}),`);
251
+ continue;
252
+ }
253
+ const typeEntries = op.requestHeaders.map(
254
+ (header) => `${formatPropertyName(header.name)}${header.required ? "" : "?"}: ${mapHeaderType(
255
+ header
256
+ )}`
257
+ ).join(", ");
258
+ const requiredHeaders = op.requestHeaders.filter(
259
+ (header) => header.required
260
+ );
261
+ const optionalHeaders = op.requestHeaders.filter(
262
+ (header) => !header.required
263
+ );
264
+ const hasRequired = requiredHeaders.length > 0;
265
+ const signature = hasRequired ? `(params: { ${typeEntries} })` : `(params: { ${typeEntries} } = {})`;
266
+ if (optionalHeaders.length === 0) {
267
+ output.push(` ${op.operationId}: ${signature} => ({`);
268
+ for (const header of requiredHeaders) {
269
+ const propertyKey = formatPropertyName(header.name);
270
+ const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
271
+ output.push(` ${propertyKey}: ${accessor},`);
272
+ }
273
+ output.push(" }),");
274
+ continue;
275
+ }
276
+ if (!hasRequired && optionalHeaders.length === 1 && optionalHeaders[0]) {
277
+ const header = optionalHeaders[0];
278
+ const propertyKey = formatPropertyName(header.name);
279
+ const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
280
+ output.push(` ${op.operationId}: ${signature} =>`);
281
+ output.push(
282
+ ` ${accessor} !== undefined ? { ${propertyKey}: ${accessor} } : {},`
283
+ );
284
+ continue;
285
+ }
286
+ const valueTypes = Array.from(
287
+ new Set(optionalHeaders.map((header) => mapHeaderType(header)))
288
+ ).join(" | ");
289
+ output.push(` ${op.operationId}: ${signature} => {`);
290
+ if (hasRequired) {
291
+ output.push(" const headers = {");
292
+ for (const header of requiredHeaders) {
293
+ const propertyKey = formatPropertyName(header.name);
294
+ const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
295
+ output.push(` ${propertyKey}: ${accessor},`);
296
+ }
297
+ output.push(" }");
298
+ } else {
299
+ output.push(` const headers: Record<string, ${valueTypes}> = {}`);
300
+ }
301
+ for (const header of optionalHeaders) {
302
+ const propertyKey = formatPropertyName(header.name);
303
+ const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
304
+ const assignment = isValidJSIdentifier(header.name) ? `headers.${header.name}` : `headers[${propertyKey}]`;
305
+ output.push(` if (${accessor} !== undefined) {`);
306
+ output.push(` ${assignment} = ${accessor}`);
307
+ output.push(" }");
308
+ }
309
+ output.push(" return headers");
310
+ output.push(" },");
311
+ }
312
+ output.push("} as const;");
313
+ output.push("");
314
+ output.push("// Operation Objects");
315
+ for (const op of operations) {
316
+ output.push(`export const ${op.operationId} = {`);
317
+ output.push(` path: paths.${op.operationId},`);
318
+ appendOperationField(output, "request", op.requestType);
319
+ appendOperationField(output, "response", op.responseType);
320
+ if (op.requestHeaders && op.requestHeaders.length > 0) {
321
+ output.push(` headers: headers.${op.operationId},`);
322
+ }
323
+ if (op.errors && hasAnyErrors(op.errors)) {
324
+ output.push(" errors: {");
325
+ appendErrorGroup(output, "clientErrors", op.errors.clientErrors);
326
+ appendErrorGroup(output, "serverErrors", op.errors.serverErrors);
327
+ appendErrorGroup(output, "defaultErrors", op.errors.defaultErrors);
328
+ appendErrorGroup(output, "otherErrors", op.errors.otherErrors);
329
+ output.push(" },");
330
+ }
331
+ output.push("} as const;");
332
+ output.push("");
333
+ }
334
+ return output.join("\n");
335
+ }
336
+ function appendOperationField(buffer, key, value) {
337
+ if (!value) return;
338
+ buffer.push(` ${key}: ${value},`);
339
+ }
340
+ function appendErrorGroup(buffer, label, errors) {
341
+ if (!errors || Object.keys(errors).length === 0) return;
342
+ buffer.push(` ${label}: {`);
343
+ for (const [name, typeName] of Object.entries(errors)) {
344
+ buffer.push(` ${formatPropertyName(name)}: ${typeName},`);
345
+ }
346
+ buffer.push(" },");
347
+ }
348
+ function hasAnyErrors(group) {
349
+ return [
350
+ group.clientErrors,
351
+ group.serverErrors,
352
+ group.defaultErrors,
353
+ group.otherErrors
354
+ ].some((bucket) => bucket && Object.keys(bucket).length > 0);
355
+ }
356
+ function parseOperations(spec) {
357
+ const operations = [];
358
+ for (const [path, pathItem] of Object.entries(spec.paths)) {
359
+ for (const [method, operation] of Object.entries(pathItem)) {
360
+ if (!operation.operationId) continue;
361
+ const pathParams = extractPathParams(path);
362
+ const requestType = getRequestType(operation);
363
+ const { successResponse, errors } = getResponseTypes(
364
+ operation,
365
+ operation.operationId
366
+ );
367
+ const resolvedParameters = collectParameters(pathItem, operation, spec);
368
+ const requestHeaders = getRequestHeaders(resolvedParameters);
369
+ operations.push({
370
+ operationId: operation.operationId,
371
+ path,
372
+ method: method.toLowerCase(),
373
+ pathParams,
374
+ requestType,
375
+ responseType: successResponse,
376
+ requestHeaders,
377
+ errors
378
+ });
379
+ }
380
+ }
381
+ return operations;
382
+ }
383
+ function collectParameters(pathItem, operation, spec) {
384
+ const parametersMap = /* @__PURE__ */ new Map();
385
+ const addParameters = (params) => {
386
+ if (!Array.isArray(params)) return;
387
+ for (const param of params) {
388
+ const resolved = resolveParameter(param, spec);
389
+ if (!resolved) continue;
390
+ const key = `${resolved.in}:${resolved.name}`;
391
+ parametersMap.set(key, resolved);
392
+ }
393
+ };
394
+ addParameters(pathItem.parameters);
395
+ addParameters(operation.parameters);
396
+ return Array.from(parametersMap.values());
397
+ }
398
+ function resolveParameter(parameter, spec) {
399
+ if (!parameter) return void 0;
400
+ if (parameter.$ref) {
401
+ const refName = extractRefName(parameter.$ref);
402
+ const resolved = spec.components?.parameters?.[refName];
403
+ if (!resolved) return void 0;
404
+ const { $ref, ...overrides } = parameter;
405
+ return {
406
+ ...resolved,
407
+ ...overrides
408
+ };
409
+ }
410
+ return parameter;
411
+ }
412
+ function extractPathParams(path) {
413
+ const params = [];
414
+ const matches = path.match(/{([^}]+)}/g);
415
+ if (matches) {
416
+ for (const match of matches) {
417
+ const paramName = match.slice(1, -1);
418
+ params.push({
419
+ name: paramName,
420
+ type: "string"
421
+ // OpenAPI path params are always strings
422
+ });
423
+ }
424
+ }
425
+ return params;
426
+ }
427
+ function getRequestType(operation) {
428
+ const requestBody = operation.requestBody?.content?.["application/json"]?.schema;
429
+ if (!requestBody) return void 0;
430
+ if (requestBody.$ref) {
431
+ return extractRefName(requestBody.$ref);
432
+ }
433
+ const typeName = `${capitalize(operation.operationId)}Request`;
434
+ return typeName;
435
+ }
436
+ function getResponseTypes(operation, operationId) {
437
+ const responses = operation.responses ?? {};
438
+ const successCodes = /* @__PURE__ */ new Map();
439
+ const errorEntries = [];
440
+ for (const [statusCode, response] of Object.entries(responses)) {
441
+ const resolvedSchema = response?.content?.["application/json"]?.schema;
442
+ if (!resolvedSchema) continue;
443
+ if (isErrorStatus(statusCode)) {
444
+ errorEntries.push({ code: statusCode, schema: resolvedSchema });
445
+ continue;
446
+ }
447
+ if (/^2\d\d$/.test(statusCode) || statusCode === "default") {
448
+ successCodes.set(statusCode, resolvedSchema);
449
+ }
450
+ }
451
+ const successResponse = selectSuccessResponse(successCodes, operationId);
452
+ const errors = buildErrorGroups(errorEntries, operationId);
453
+ return { successResponse, errors };
454
+ }
455
+ function selectSuccessResponse(responses, operationId) {
456
+ if (responses.size === 0) return void 0;
457
+ const preferredOrder = ["200", "201", "204"];
458
+ for (const code of preferredOrder) {
459
+ const schema = responses.get(code);
460
+ if (schema) {
461
+ return resolveResponseType(
462
+ schema,
463
+ `${capitalize(operationId)}Response${code}`
464
+ );
465
+ }
466
+ }
467
+ const [firstCode, firstSchema] = responses.entries().next().value ?? [];
468
+ if (!firstSchema) return void 0;
469
+ return resolveResponseType(
470
+ firstSchema,
471
+ `${capitalize(operationId)}Response${firstCode ?? "Default"}`
472
+ );
473
+ }
474
+ function buildErrorGroups(errors = [], operationId) {
475
+ if (!errors.length) return void 0;
476
+ const group = {};
477
+ for (const { code, schema } of errors) {
478
+ const category = getStatusCategory(code);
479
+ const identifier = mapStatusToIdentifier(code);
480
+ const typeName = resolveResponseType(
481
+ schema,
482
+ `${capitalize(operationId)}${capitalize(identifier)}`
483
+ );
484
+ switch (category) {
485
+ case "client":
486
+ group.clientErrors ??= {};
487
+ group.clientErrors[identifier] = typeName;
488
+ break;
489
+ case "server":
490
+ group.serverErrors ??= {};
491
+ group.serverErrors[identifier] = typeName;
492
+ break;
493
+ case "default":
494
+ group.defaultErrors ??= {};
495
+ group.defaultErrors[identifier] = typeName;
496
+ break;
497
+ default:
498
+ group.otherErrors ??= {};
499
+ group.otherErrors[identifier] = typeName;
500
+ break;
501
+ }
502
+ }
503
+ return group;
504
+ }
505
+ function resolveResponseType(schema, fallbackName) {
506
+ if (schema.$ref) {
507
+ return extractRefName(schema.$ref);
508
+ }
509
+ return fallbackName;
510
+ }
511
+ function getRequestHeaders(parameters) {
512
+ const headers = [];
513
+ for (const param of parameters ?? []) {
514
+ if (param.in === "header") {
515
+ headers.push({
516
+ name: param.name,
517
+ description: param.description,
518
+ schema: param.schema,
519
+ required: param.required
520
+ });
521
+ }
522
+ }
523
+ return headers;
524
+ }
525
+ function mapHeaderType(header) {
526
+ const schemaType = header.schema?.type;
527
+ switch (schemaType) {
528
+ case "integer":
529
+ case "number":
530
+ return "number";
531
+ case "boolean":
532
+ return "boolean";
533
+ default:
534
+ return "string";
535
+ }
536
+ }
537
+ function generateZodSchema(name, schema, generatedTypes, options) {
538
+ if (generatedTypes.has(name)) return "";
539
+ generatedTypes.add(name);
540
+ if (schema.enum) {
541
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
542
+ return `export const ${name} = z.enum([${enumValues}]);`;
543
+ }
544
+ if (schema.type === "object" || schema.properties) {
545
+ const properties = [];
546
+ for (const [propName, propSchema] of Object.entries(
547
+ schema.properties || {}
548
+ )) {
549
+ const isRequired = schema.required?.includes(propName) ?? false;
550
+ const zodType = getZodTypeFromSchema(propSchema, options);
551
+ const finalType = isRequired ? zodType : `${zodType}.optional()`;
552
+ properties.push(` ${formatPropertyName(propName)}: ${finalType},`);
553
+ }
554
+ return `export const ${name} = z.object({
555
+ ${properties.join("\n")}
556
+ });`;
557
+ }
558
+ if (schema.type === "array") {
559
+ const itemSchema = schema.items ?? { type: "unknown" };
560
+ const itemType = getZodTypeFromSchema(itemSchema, options);
561
+ const builder = applyStrictArrayBounds(
562
+ schema,
563
+ `z.array(${itemType})`,
564
+ itemSchema,
565
+ options.strictNumeric
566
+ );
567
+ return `export const ${name} = ${builder};`;
568
+ }
569
+ return `export const ${name} = ${getZodTypeFromSchema(schema, options)};`;
570
+ }
571
+ function getZodTypeFromSchema(schema, options) {
572
+ if (schema.$ref) {
573
+ return extractRefName(schema.$ref);
574
+ }
575
+ if (schema.enum) {
576
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
577
+ return `z.enum([${enumValues}])`;
578
+ }
579
+ switch (schema.type) {
580
+ case "string":
581
+ return buildString(schema, options);
582
+ case "boolean":
583
+ return "z.boolean()";
584
+ case "array":
585
+ return `z.array(${getZodTypeFromSchema(
586
+ schema.items ?? { type: "unknown" },
587
+ options
588
+ )})`;
589
+ case "null":
590
+ return "z.null()";
591
+ case "number":
592
+ return buildNumber(schema, options);
593
+ case "integer":
594
+ return buildInteger(schema, options);
595
+ default:
596
+ return "z.unknown()";
597
+ }
598
+ }
599
+ function buildString(schema, options) {
600
+ if (options.strictDates) {
601
+ switch (schema.format) {
602
+ case "date-time":
603
+ return "z.string().datetime()";
604
+ case "date":
605
+ return "z.string().date()";
606
+ case "time":
607
+ return "z.string().time()";
608
+ case "duration":
609
+ return "z.string().duration()";
610
+ }
611
+ }
612
+ let builder = "z.string()";
613
+ if (options.strictNumeric) {
614
+ if (typeof schema.minLength === "number") {
615
+ builder += `.min(${schema.minLength})`;
616
+ }
617
+ if (typeof schema.maxLength === "number") {
618
+ builder += `.max(${schema.maxLength})`;
619
+ }
620
+ if (schema.pattern) {
621
+ builder += `.regex(new RegExp(${JSON.stringify(schema.pattern)}))`;
622
+ }
623
+ }
624
+ switch (schema.format) {
625
+ case "uuid":
626
+ return `${builder}.uuid()`;
627
+ case "email":
628
+ return `${builder}.email()`;
629
+ case "uri":
630
+ case "url":
631
+ return `${builder}.url()`;
632
+ case "ipv4":
633
+ return `${builder}.ip({ version: "v4" })`;
634
+ case "ipv6":
635
+ return `${builder}.ip({ version: "v6" })`;
636
+ default:
637
+ return builder;
638
+ }
639
+ }
640
+ function buildNumber(schema, options) {
641
+ let builder = "z.number()";
642
+ if (options.strictNumeric) {
643
+ builder = applyNumericBounds(schema, builder);
644
+ if (typeof schema.multipleOf === "number" && schema.multipleOf !== 0) {
645
+ builder += `.refine((value) => Math.abs(value / ${schema.multipleOf} - Math.round(value / ${schema.multipleOf})) < Number.EPSILON, { message: "Must be a multiple of ${schema.multipleOf}" })`;
646
+ }
647
+ }
648
+ return builder;
649
+ }
650
+ function buildInteger(schema, options) {
651
+ let builder = buildNumber(schema, options);
652
+ builder += ".int()";
653
+ return builder;
654
+ }
655
+ function applyStrictArrayBounds(schema, builder, itemSchema, enforceBounds) {
656
+ if (!enforceBounds) {
657
+ return builder;
658
+ }
659
+ if (typeof schema.minItems === "number") {
660
+ builder += `.min(${schema.minItems})`;
661
+ }
662
+ if (typeof schema.maxItems === "number") {
663
+ builder += `.max(${schema.maxItems})`;
664
+ }
665
+ if (schema.uniqueItems && isPrimitiveLike(itemSchema)) {
666
+ builder += '.refine((items) => new Set(items).size === items.length, { message: "Items must be unique" })';
667
+ }
668
+ return builder;
669
+ }
670
+ function isPrimitiveLike(schema) {
671
+ if (schema?.$ref) return false;
672
+ const primitiveTypes = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean"]);
673
+ return primitiveTypes.has(schema?.type);
674
+ }
675
+ function applyNumericBounds(schema, builder) {
676
+ if (typeof schema.minimum === "number") {
677
+ if (schema.exclusiveMinimum === true) {
678
+ builder += `.gt(${schema.minimum})`;
679
+ } else {
680
+ builder += `.min(${schema.minimum})`;
681
+ }
682
+ } else if (typeof schema.exclusiveMinimum === "number") {
683
+ builder += `.gt(${schema.exclusiveMinimum})`;
684
+ }
685
+ if (typeof schema.maximum === "number") {
686
+ if (schema.exclusiveMaximum === true) {
687
+ builder += `.lt(${schema.maximum})`;
688
+ } else {
689
+ builder += `.max(${schema.maximum})`;
690
+ }
691
+ } else if (typeof schema.exclusiveMaximum === "number") {
692
+ builder += `.lt(${schema.exclusiveMaximum})`;
693
+ }
694
+ return builder;
695
+ }
696
+ function capitalize(str) {
697
+ return str.charAt(0).toUpperCase() + str.slice(1);
698
+ }
699
+ export {
700
+ generate
701
+ };
702
+ //# sourceMappingURL=index.mjs.map