@sdk-it/cli 0.12.6 → 0.12.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,1145 +4,10 @@ import { Command as Command2, program } from "commander";
4
4
  // packages/cli/src/lib/generate.ts
5
5
  import { Command, Option } from "commander";
6
6
  import { execFile } from "node:child_process";
7
- import { readFile as readFile2 } from "node:fs/promises";
7
+ import { readFile } from "node:fs/promises";
8
8
  import { extname } from "node:path";
9
9
  import { parse } from "yaml";
10
-
11
- // packages/typescript/dist/index.js
12
- import { join as join2 } from "node:path";
13
- import { npmRunPathEnv } from "npm-run-path";
14
- import ts, { TypeFlags, symbolName } from "typescript";
15
- import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
16
- import { dirname, isAbsolute, join } from "node:path";
17
- import debug from "debug";
18
- import ts2 from "typescript";
19
- import { get as get2, merge } from "lodash-es";
20
- import { camelcase as camelcase2, pascalcase as pascalcase2, spinalcase as spinalcase2 } from "stringcase";
21
- import { get } from "lodash-es";
22
- import { camelcase, pascalcase, spinalcase } from "stringcase";
23
- import { debounceTime, from } from "rxjs";
24
- var deriveSymbol = Symbol.for("serialize");
25
- var $types = Symbol.for("types");
26
- async function exist(file) {
27
- return stat(file).then(() => true).catch(() => false);
28
- }
29
- async function writeFiles(dir, contents) {
30
- return Promise.all(
31
- Object.entries(contents).map(async ([file, content]) => {
32
- const filePath = isAbsolute(file) ? file : join(dir, file);
33
- await mkdir(dirname(filePath), { recursive: true });
34
- if (typeof content === "string") {
35
- await writeFile(filePath, content, "utf-8");
36
- } else {
37
- if (content.ignoreIfExists) {
38
- if (!await exist(filePath)) {
39
- await writeFile(filePath, content.content, "utf-8");
40
- }
41
- }
42
- }
43
- })
44
- );
45
- }
46
- async function getFolderExports(folder, extensions = ["ts"]) {
47
- const files = await readdir(folder, { withFileTypes: true });
48
- const exports = [];
49
- for (const file of files) {
50
- if (file.isDirectory()) {
51
- exports.push(`export * from './${file.name}/index.ts';`);
52
- } else if (file.name !== "index.ts" && extensions.includes(getExt(file.name))) {
53
- exports.push(`export * from './${file.name}';`);
54
- }
55
- }
56
- return exports.join("\n");
57
- }
58
- var getExt = (fileName) => {
59
- if (!fileName) {
60
- return "";
61
- }
62
- const lastDot = fileName.lastIndexOf(".");
63
- if (lastDot === -1) {
64
- return "";
65
- }
66
- const ext = fileName.slice(lastDot + 1).split("/").filter(Boolean).join("");
67
- if (ext === fileName) {
68
- return "";
69
- }
70
- return ext || "txt";
71
- };
72
- var methods = [
73
- "get",
74
- "post",
75
- "put",
76
- "patch",
77
- "delete",
78
- "trace",
79
- "head"
80
- ];
81
- var logger = debug("january:client");
82
- function removeDuplicates(data, accessor) {
83
- return [...new Map(data.map((x) => [accessor(x), x])).values()];
84
- }
85
- function toLitObject(obj, accessor = (value) => value) {
86
- return `{${Object.keys(obj).map((key) => `${key}: ${accessor(obj[key])}`).join(", ")}}`;
87
- }
88
- var client_default = (spec) => {
89
- const optionsEntries = Object.entries(spec.options).map(
90
- ([key, value]) => [`'${key}'`, value]
91
- );
92
- const defaultHeaders = `{${optionsEntries.filter(([, value]) => value.in === "header").map(
93
- ([key, value]) => `${key}: this.options[${value.optionName ? `'${value.optionName}'` : key}]`
94
- ).join(",\n")}}`;
95
- const defaultInputs = `{${optionsEntries.filter(([, value]) => value.in === "input").map(
96
- ([key, value]) => `${key}: this.options[${value.optionName ? `'${value.optionName}'` : key}]`
97
- ).join(",\n")}}`;
98
- const specOptions = {
99
- ...Object.fromEntries(
100
- optionsEntries.map(([key, value]) => [value.optionName ?? key, value])
101
- ),
102
- fetch: {
103
- schema: "fetchType"
104
- },
105
- baseUrl: {
106
- schema: spec.servers.length ? `z.enum(servers).default(servers[0])` : "z.string()"
107
- }
108
- };
109
- return `
110
- import { fetchType, sendRequest } from './http/send-request.ts';
111
- import z from 'zod';
112
- import type { Endpoints } from './endpoints.ts';
113
- import schemas from './schemas.ts';
114
- import {
115
- createBaseUrlInterceptor,
116
- createDefaultHeadersInterceptor,
117
- } from './http/interceptors.ts';
118
-
119
- ${spec.servers.length ? `export const servers = ${JSON.stringify(spec.servers, null, 2)} as const` : ""}
120
- const optionsSchema = z.object(${toLitObject(specOptions, (x) => x.schema)});
121
- ${spec.servers.length ? `export type Servers = typeof servers[number];` : ""}
122
-
123
- type ${spec.name}Options = z.infer<typeof optionsSchema>;
124
-
125
- export class ${spec.name} {
126
-
127
- constructor(public options: ${spec.name}Options) {}
128
-
129
- async request<E extends keyof Endpoints>(
130
- endpoint: E,
131
- input: Endpoints[E]['input'],
132
- ): Promise<readonly [Endpoints[E]['output'], Endpoints[E]['error'] | null]> {
133
- const route = schemas[endpoint];
134
- return sendRequest(Object.assign(this.#defaultInputs, input), route, {
135
- fetch: this.options.fetch,
136
- interceptors: [
137
- createDefaultHeadersInterceptor(() => this.defaultHeaders),
138
- createBaseUrlInterceptor(() => this.options.baseUrl),
139
- ],
140
- });
141
- }
142
-
143
- get defaultHeaders() {
144
- return ${defaultHeaders}
145
- }
146
-
147
- get #defaultInputs() {
148
- return ${defaultInputs}
149
- }
150
-
151
- setOptions(options: Partial<${spec.name}Options>) {
152
- const validated = optionsSchema.partial().parse(options);
153
-
154
- for (const key of Object.keys(validated) as (keyof ${spec.name}Options)[]) {
155
- if (validated[key] !== undefined) {
156
- (this.options[key] as typeof validated[typeof key]) = validated[key]!;
157
- }
158
- }
159
- }
160
- }`;
161
- };
162
- var SchemaEndpoint = class {
163
- #imports = [
164
- `import z from 'zod';`,
165
- 'import type { Endpoints } from "./endpoints.ts";',
166
- `import { toRequest, json, urlencoded, nobody, formdata, createUrl } from './http/request.ts';`,
167
- `import type { ParseError } from './http/parser.ts';`
168
- ];
169
- #endpoints = [];
170
- addEndpoint(endpoint, operation) {
171
- this.#endpoints.push(` "${endpoint}": ${operation},`);
172
- }
173
- addImport(value) {
174
- this.#imports.push(value);
175
- }
176
- complete() {
177
- return `${this.#imports.join("\n")}
178
- export default {
179
- ${this.#endpoints.join("\n")}
180
- }`;
181
- }
182
- };
183
- var Emitter = class {
184
- imports = [
185
- `import z from 'zod';`,
186
- `import type { ParseError } from './http/parser.ts';`
187
- ];
188
- endpoints = [];
189
- addEndpoint(endpoint, operation) {
190
- this.endpoints.push(` "${endpoint}": ${operation};`);
191
- }
192
- addImport(value) {
193
- this.imports.push(value);
194
- }
195
- complete() {
196
- return `${this.imports.join("\n")}
197
- export interface Endpoints {
198
- ${this.endpoints.join("\n")}
199
- }`;
200
- }
201
- };
202
- var StreamEmitter = class extends Emitter {
203
- complete() {
204
- return `${this.imports.join("\n")}
205
- export interface StreamEndpoints {
206
- ${this.endpoints.join("\n")}
207
- }`;
208
- }
209
- };
210
- function generateClientSdk(spec) {
211
- const emitter = new Emitter();
212
- const streamEmitter = new StreamEmitter();
213
- const schemas = {};
214
- const schemaEndpoint = new SchemaEndpoint();
215
- const errors = [];
216
- for (const [name, operations] of Object.entries(spec.operations)) {
217
- const featureSchemaFileName = camelcase(name);
218
- schemas[featureSchemaFileName] = [`import z from 'zod';`];
219
- emitter.addImport(
220
- `import * as ${featureSchemaFileName} from './inputs/${featureSchemaFileName}.ts';`
221
- );
222
- streamEmitter.addImport(
223
- `import * as ${featureSchemaFileName} from './inputs/${featureSchemaFileName}.ts';`
224
- );
225
- schemaEndpoint.addImport(
226
- `import * as ${featureSchemaFileName} from './inputs/${featureSchemaFileName}.ts';`
227
- );
228
- for (const operation of operations) {
229
- const schemaName = camelcase(`${operation.name} schema`);
230
- const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] : toLitObject(operation.schemas)};`;
231
- schemas[featureSchemaFileName].push(schema);
232
- const schemaRef = `${featureSchemaFileName}.${schemaName}`;
233
- const output = operation.formatOutput();
234
- const inputHeaders = [];
235
- const inputQuery = [];
236
- const inputBody = [];
237
- const inputParams = [];
238
- for (const [name2, prop] of Object.entries(operation.inputs)) {
239
- if (prop.in === "headers" || prop.in === "header") {
240
- inputHeaders.push(`"${name2}"`);
241
- } else if (prop.in === "query") {
242
- inputQuery.push(`"${name2}"`);
243
- } else if (prop.in === "body") {
244
- inputBody.push(`"${name2}"`);
245
- } else if (prop.in === "path") {
246
- inputParams.push(`"${name2}"`);
247
- } else if (prop.in === "internal") {
248
- continue;
249
- } else {
250
- throw new Error(
251
- `Unknown source ${prop.in} in ${name2} ${JSON.stringify(
252
- prop
253
- )} in ${operation.name}`
254
- );
255
- }
256
- }
257
- if (operation.type === "sse") {
258
- const input = `z.infer<typeof ${schemaRef}>`;
259
- const endpoint = `${operation.trigger.method.toUpperCase()} ${operation.trigger.path}`;
260
- streamEmitter.addImport(
261
- `import type {${pascalcase(operation.name)}} from './outputs/${spinalcase(operation.name)}.ts';`
262
- );
263
- streamEmitter.addEndpoint(
264
- endpoint,
265
- `{input: ${input}, output: ${output.use}}`
266
- );
267
- schemaEndpoint.addEndpoint(
268
- endpoint,
269
- `{
270
- schema: ${schemaRef},
271
- toRequest(input: StreamEndpoints['${endpoint}']['input']) {
272
- const endpoint = '${endpoint}';
273
- return toRequest(endpoint, json(input, {
274
- inputHeaders: [${inputHeaders}],
275
- inputQuery: [${inputQuery}],
276
- inputBody: [${inputBody}],
277
- inputParams: [${inputParams}],
278
- }));
279
- },
280
- }`
281
- );
282
- } else {
283
- emitter.addImport(
284
- `import type {${output.import}} from './outputs/${spinalcase(operation.name)}.ts';`
285
- );
286
- errors.push(...operation.errors ?? []);
287
- const addTypeParser = Object.keys(operation.schemas).length > 1;
288
- for (const type in operation.schemas ?? {}) {
289
- let typePrefix = "";
290
- if (addTypeParser && type !== "json") {
291
- typePrefix = `${type} `;
292
- }
293
- const input = `typeof ${schemaRef}${addTypeParser ? `.${type}` : ""}`;
294
- const endpoint = `${typePrefix}${operation.trigger.method.toUpperCase()} ${operation.trigger.path}`;
295
- emitter.addEndpoint(
296
- endpoint,
297
- `{input: z.infer<${input}>; output: ${output.use}; error: ${(operation.errors ?? ["ServerError"]).concat(`ParseError<${input}>`).join("|")}}`
298
- );
299
- schemaEndpoint.addEndpoint(
300
- endpoint,
301
- `{
302
- schema: ${schemaRef}${addTypeParser ? `.${type}` : ""},
303
- toRequest(input: Endpoints['${endpoint}']['input']) {
304
- const endpoint = '${endpoint}';
305
- return toRequest(endpoint, ${operation.contentType || "nobody"}(input, {
306
- inputHeaders: [${inputHeaders}],
307
- inputQuery: [${inputQuery}],
308
- inputBody: [${inputBody}],
309
- inputParams: [${inputParams}],
310
- }));
311
- },
312
- }`
313
- );
314
- }
315
- }
316
- }
317
- }
318
- emitter.addImport(
319
- `import type { ${removeDuplicates(errors, (it) => it).join(", ")} } from './http/response.ts';`
320
- );
321
- return {
322
- ...Object.fromEntries(
323
- Object.entries(schemas).map(([key, value]) => [
324
- `inputs/${key}.ts`,
325
- [
326
- // schemasImports.length
327
- // ? `import {${removeDuplicates(schemasImports, (it) => it)}} from '../zod';`
328
- // : '',
329
- spec.commonZod ? 'import * as commonZod from "../zod.ts";' : "",
330
- ...value
331
- ].map((it) => it.trim()).filter(Boolean).join("\n") + "\n"
332
- // add a newline at the end
333
- ])
334
- ),
335
- "client.ts": client_default(spec),
336
- "schemas.ts": schemaEndpoint.complete(),
337
- "endpoints.ts": emitter.complete()
338
- };
339
- }
340
- function isRef(obj) {
341
- return "$ref" in obj;
342
- }
343
- function cleanRef(ref) {
344
- return ref.replace(/^#\//, "");
345
- }
346
- function parseRef(ref) {
347
- const parts = ref.split("/");
348
- const [model] = parts.splice(-1);
349
- return { model, path: parts.join("/") };
350
- }
351
- function followRef(spec, ref) {
352
- const pathParts = cleanRef(ref).split("/");
353
- const entry = get(spec, pathParts);
354
- if (entry && "$ref" in entry) {
355
- return followRef(spec, entry.$ref);
356
- }
357
- return entry;
358
- }
359
- function securityToOptions(security2, securitySchemas, staticIn) {
360
- securitySchemas ??= {};
361
- const options = {};
362
- for (const it of security2) {
363
- const [name] = Object.keys(it);
364
- const schema = securitySchemas[name];
365
- if (isRef(schema)) {
366
- throw new Error(`Ref security schemas are not supported`);
367
- }
368
- if (schema.type === "http") {
369
- options["authorization"] = {
370
- in: staticIn ?? "header",
371
- schema: "z.string().optional().transform((val) => (val ? `Bearer ${val}` : undefined))",
372
- optionName: "token"
373
- };
374
- continue;
375
- }
376
- if (schema.type === "apiKey") {
377
- if (!schema.in) {
378
- throw new Error(`apiKey security schema must have an "in" field`);
379
- }
380
- if (!schema.name) {
381
- throw new Error(`apiKey security schema must have a "name" field`);
382
- }
383
- options[schema.name] = {
384
- in: staticIn ?? schema.in,
385
- schema: "z.string().optional()"
386
- };
387
- continue;
388
- }
389
- }
390
- return options;
391
- }
392
- function mergeImports(imports) {
393
- const merged = {};
394
- for (const i of imports) {
395
- merged[i.moduleSpecifier] = merged[i.moduleSpecifier] ?? {
396
- moduleSpecifier: i.moduleSpecifier,
397
- defaultImport: i.defaultImport,
398
- namespaceImport: i.namespaceImport,
399
- namedImports: []
400
- };
401
- if (i.namedImports) {
402
- merged[i.moduleSpecifier].namedImports.push(...i.namedImports);
403
- }
404
- }
405
- return Object.values(merged);
406
- }
407
- function importsToString(...imports) {
408
- return imports.map((it) => {
409
- if (it.defaultImport) {
410
- return `import ${it.defaultImport} from '${it.moduleSpecifier}'`;
411
- }
412
- if (it.namespaceImport) {
413
- return `import * as ${it.namespaceImport} from '${it.moduleSpecifier}'`;
414
- }
415
- if (it.namedImports) {
416
- return `import {${removeDuplicates(it.namedImports, (it2) => it2.name).map((n) => `${n.isTypeOnly ? "type" : ""} ${n.name}`).join(", ")}} from '${it.moduleSpecifier}'`;
417
- }
418
- throw new Error(`Invalid import ${JSON.stringify(it)}`);
419
- });
420
- }
421
- var TypeScriptDeserialzer = class {
422
- circularRefTracker = /* @__PURE__ */ new Set();
423
- #spec;
424
- #onRef;
425
- constructor(spec, onRef) {
426
- this.#spec = spec;
427
- this.#onRef = onRef;
428
- }
429
- /**
430
- * Handle objects (properties)
431
- */
432
- object(schema, required = false) {
433
- const properties = schema.properties || {};
434
- const propEntries = Object.entries(properties).map(([key, propSchema]) => {
435
- const isRequired = (schema.required ?? []).includes(key);
436
- const tsType = this.handle(propSchema, isRequired);
437
- return `${key}: ${tsType}`;
438
- });
439
- if (schema.additionalProperties) {
440
- if (typeof schema.additionalProperties === "object") {
441
- const indexType = this.handle(schema.additionalProperties, true);
442
- propEntries.push(`[key: string]: ${indexType}`);
443
- } else if (schema.additionalProperties === true) {
444
- propEntries.push("[key: string]: any");
445
- }
446
- }
447
- return `{ ${propEntries.join("; ")} }`;
448
- }
449
- /**
450
- * Handle arrays (items could be a single schema or a tuple)
451
- */
452
- array(schema, required = false) {
453
- const { items } = schema;
454
- if (!items) {
455
- return "any[]";
456
- }
457
- if (Array.isArray(items)) {
458
- const tupleItems = items.map((sub) => this.handle(sub, true));
459
- return `[${tupleItems.join(", ")}]`;
460
- }
461
- const itemsType = this.handle(items, true);
462
- return `${itemsType}[]`;
463
- }
464
- /**
465
- * Convert a basic type (string | number | boolean | object | array, etc.) to TypeScript
466
- */
467
- normal(type, schema, required = false) {
468
- switch (type) {
469
- case "string":
470
- return this.string(schema, required);
471
- case "number":
472
- case "integer":
473
- return this.number(schema, required);
474
- case "boolean":
475
- return appendOptional("boolean", required);
476
- case "object":
477
- return this.object(schema, required);
478
- case "array":
479
- return this.array(schema, required);
480
- case "null":
481
- return "null";
482
- default:
483
- return appendOptional("any", required);
484
- }
485
- }
486
- ref($ref, required) {
487
- const schemaName = cleanRef($ref).split("/").pop();
488
- if (this.circularRefTracker.has(schemaName)) {
489
- return schemaName;
490
- }
491
- this.circularRefTracker.add(schemaName);
492
- this.#onRef(schemaName, this.handle(followRef(this.#spec, $ref), true));
493
- this.circularRefTracker.delete(schemaName);
494
- return appendOptional(schemaName, required);
495
- }
496
- allOf(schemas) {
497
- const allOfTypes = schemas.map((sub) => this.handle(sub, true));
498
- return allOfTypes.length > 1 ? `${allOfTypes.join(" & ")}` : allOfTypes[0];
499
- }
500
- anyOf(schemas, required) {
501
- const anyOfTypes = schemas.map((sub) => this.handle(sub, true));
502
- return appendOptional(
503
- anyOfTypes.length > 1 ? `${anyOfTypes.join(" | ")}` : anyOfTypes[0],
504
- required
505
- );
506
- }
507
- oneOf(schemas, required) {
508
- const oneOfTypes = schemas.map((sub) => {
509
- if (isRef(sub)) {
510
- const { model } = parseRef(sub.$ref);
511
- if (this.circularRefTracker.has(model)) {
512
- return model;
513
- }
514
- }
515
- return this.handle(sub, false);
516
- });
517
- return appendOptional(
518
- oneOfTypes.length > 1 ? `${oneOfTypes.join(" | ")}` : oneOfTypes[0],
519
- required
520
- );
521
- }
522
- enum(values, required) {
523
- const enumValues = values.map((val) => typeof val === "string" ? `'${val}'` : `${val}`).join(" | ");
524
- return appendOptional(enumValues, required);
525
- }
526
- /**
527
- * Handle string type with formats
528
- */
529
- string(schema, required) {
530
- let type;
531
- switch (schema.format) {
532
- case "date-time":
533
- case "datetime":
534
- case "date":
535
- type = "Date";
536
- break;
537
- case "binary":
538
- case "byte":
539
- type = "Blob";
540
- break;
541
- case "int64":
542
- type = "bigint";
543
- break;
544
- default:
545
- type = "string";
546
- }
547
- return appendOptional(type, required);
548
- }
549
- /**
550
- * Handle number/integer types with formats
551
- */
552
- number(schema, required) {
553
- const type = schema.format === "int64" ? "bigint" : "number";
554
- return appendOptional(type, required);
555
- }
556
- handle(schema, required) {
557
- if (isRef(schema)) {
558
- return this.ref(schema.$ref, required);
559
- }
560
- if (schema.allOf && Array.isArray(schema.allOf)) {
561
- return this.allOf(schema.allOf);
562
- }
563
- if (schema.anyOf && Array.isArray(schema.anyOf)) {
564
- return this.anyOf(schema.anyOf, required);
565
- }
566
- if (schema.oneOf && Array.isArray(schema.oneOf)) {
567
- return this.oneOf(schema.oneOf, required);
568
- }
569
- if (schema.enum && Array.isArray(schema.enum)) {
570
- return this.enum(schema.enum, required);
571
- }
572
- const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
573
- if (!types.length) {
574
- return appendOptional("any", required);
575
- }
576
- if (types.length > 1) {
577
- const realTypes = types.filter((t) => t !== "null");
578
- if (realTypes.length === 1 && types.includes("null")) {
579
- const tsType = this.normal(realTypes[0], schema, false);
580
- return appendOptional(`${tsType} | null`, required);
581
- }
582
- const typeResults = types.map((t) => this.normal(t, schema, false));
583
- return appendOptional(typeResults.join(" | "), required);
584
- }
585
- return this.normal(types[0], schema, required);
586
- }
587
- /**
588
- * Generate an interface declaration
589
- */
590
- generateInterface(name, schema) {
591
- const content = this.handle(schema, true);
592
- return `interface ${name} ${content}`;
593
- }
594
- };
595
- function appendOptional(type, isRequired) {
596
- return isRequired ? type : `${type} | undefined`;
597
- }
598
- var ZodDeserialzer = class {
599
- circularRefTracker = /* @__PURE__ */ new Set();
600
- #spec;
601
- #onRef;
602
- constructor(spec, onRef) {
603
- this.#spec = spec;
604
- this.#onRef = onRef;
605
- }
606
- /**
607
- * Handle objects (properties, additionalProperties).
608
- */
609
- object(schema, required = false) {
610
- const properties = schema.properties || {};
611
- const propEntries = Object.entries(properties).map(([key, propSchema]) => {
612
- const isRequired = (schema.required ?? []).includes(key);
613
- const zodPart = this.handle(propSchema, isRequired);
614
- return `'${key}': ${zodPart}`;
615
- });
616
- let additionalProps = "";
617
- if (schema.additionalProperties) {
618
- if (typeof schema.additionalProperties === "object") {
619
- const addPropZod = this.handle(schema.additionalProperties, true);
620
- additionalProps = `.catchall(${addPropZod})`;
621
- } else if (schema.additionalProperties === true) {
622
- additionalProps = `.catchall(z.unknown())`;
623
- }
624
- }
625
- const objectSchema = `z.object({${propEntries.join(", ")}})${additionalProps}`;
626
- return `${objectSchema}${appendOptional2(required)}`;
627
- }
628
- /**
629
- * Handle arrays (items could be a single schema or a tuple (array of schemas)).
630
- * In JSON Schema 2020-12, `items` can be an array → tuple style.
631
- */
632
- array(schema, required = false) {
633
- const { items } = schema;
634
- if (!items) {
635
- return `z.array(z.unknown())${appendOptional2(required)}`;
636
- }
637
- if (Array.isArray(items)) {
638
- const tupleItems = items.map((sub) => this.handle(sub, true));
639
- const base = `z.tuple([${tupleItems.join(", ")}])`;
640
- return `${base}${appendOptional2(required)}`;
641
- }
642
- const itemsSchema = this.handle(items, true);
643
- return `z.array(${itemsSchema})${appendOptional2(required)}`;
644
- }
645
- // oneOf() {}
646
- // enum() {}
647
- /**
648
- * Convert a basic type (string | number | boolean | object | array, etc.) to Zod.
649
- * We'll also handle .optional() if needed.
650
- */
651
- normal(type, schema, required = false) {
652
- switch (type) {
653
- case "string":
654
- return this.string(schema, required);
655
- case "number":
656
- case "integer":
657
- return this.number(schema, required);
658
- case "boolean":
659
- return `z.boolean()${appendDefault(schema.default)}${appendOptional2(required)}`;
660
- case "object":
661
- return this.object(schema, required);
662
- case "array":
663
- return this.array(schema, required);
664
- case "null":
665
- return `z.null()${appendOptional2(required)}`;
666
- default:
667
- return `z.unknown()${appendOptional2(required)}`;
668
- }
669
- }
670
- ref($ref, required) {
671
- const schemaName = cleanRef($ref).split("/").pop();
672
- if (this.circularRefTracker.has(schemaName)) {
673
- return schemaName;
674
- }
675
- this.circularRefTracker.add(schemaName);
676
- this.#onRef?.(
677
- schemaName,
678
- this.handle(followRef(this.#spec, $ref), required)
679
- );
680
- this.circularRefTracker.delete(schemaName);
681
- return schemaName;
682
- }
683
- allOf(schemas) {
684
- const allOfSchemas = schemas.map((sub) => this.handle(sub, true));
685
- return allOfSchemas.length ? `z.intersection(${allOfSchemas.join(", ")})` : allOfSchemas[0];
686
- }
687
- anyOf(schemas, required) {
688
- const anyOfSchemas = schemas.map((sub) => this.handle(sub, false));
689
- return anyOfSchemas.length > 1 ? `z.union([${anyOfSchemas.join(", ")}])${appendOptional2(required)}` : (
690
- // Handle an invalid anyOf with one schema
691
- anyOfSchemas[0]
692
- );
693
- }
694
- oneOf(schemas, required) {
695
- const oneOfSchemas = schemas.map((sub) => {
696
- if ("$ref" in sub) {
697
- const { model } = parseRef(sub.$ref);
698
- if (this.circularRefTracker.has(model)) {
699
- return model;
700
- }
701
- }
702
- return this.handle(sub, false);
703
- });
704
- return oneOfSchemas.length > 1 ? `z.union([${oneOfSchemas.join(", ")}])${appendOptional2(required)}` : (
705
- // Handle an invalid oneOf with one schema
706
- oneOfSchemas[0]
707
- );
708
- }
709
- enum(values, required) {
710
- const enumVals = values.map((val) => JSON.stringify(val)).join(", ");
711
- return `z.enum([${enumVals}])${appendOptional2(required)}`;
712
- }
713
- /**
714
- * Handle a `string` schema with possible format keywords (JSON Schema).
715
- */
716
- string(schema, required) {
717
- let base = "z.string()";
718
- switch (schema.format) {
719
- case "date-time":
720
- case "datetime":
721
- base = "z.coerce.date()";
722
- break;
723
- case "date":
724
- base = "z.coerce.date() /* or z.string() if you want raw date strings */";
725
- break;
726
- case "time":
727
- base = "z.string() /* optionally add .regex(...) for HH:MM:SS format */";
728
- break;
729
- case "email":
730
- base = "z.string().email()";
731
- break;
732
- case "uuid":
733
- base = "z.string().uuid()";
734
- break;
735
- case "url":
736
- case "uri":
737
- base = "z.string().url()";
738
- break;
739
- case "ipv4":
740
- base = 'z.string().ip({version: "v4"})';
741
- break;
742
- case "ipv6":
743
- base = 'z.string().ip({version: "v6"})';
744
- break;
745
- case "phone":
746
- base = "z.string() /* or add .regex(...) for phone formats */";
747
- break;
748
- case "byte":
749
- case "binary":
750
- base = "z.instanceof(Blob) /* consider base64 check if needed */";
751
- break;
752
- case "int64":
753
- base = "z.string() /* or z.bigint() if your app can handle it */";
754
- break;
755
- default:
756
- break;
757
- }
758
- return `${base}${appendDefault(schema.default)}${appendOptional2(required)}`;
759
- }
760
- /**
761
- * Handle number/integer constraints from OpenAPI/JSON Schema.
762
- * In 3.1, exclusiveMinimum/Maximum hold the actual numeric threshold,
763
- * rather than a boolean toggling `minimum`/`maximum`.
764
- */
765
- number(schema, required) {
766
- let defaultValue = schema.default !== void 0 ? `.default(${schema.default})` : ``;
767
- let base = "z.number()";
768
- if (schema.format === "int64") {
769
- base = "z.bigint()";
770
- if (schema.default !== void 0) {
771
- defaultValue = `.default(BigInt(${schema.default}))`;
772
- }
773
- }
774
- if (schema.format === "int32") {
775
- base += ".int()";
776
- }
777
- if (typeof schema.exclusiveMinimum === "number") {
778
- base += `.gt(${schema.exclusiveMinimum})`;
779
- }
780
- if (typeof schema.exclusiveMaximum === "number") {
781
- base += `.lt(${schema.exclusiveMaximum})`;
782
- }
783
- if (typeof schema.minimum === "number") {
784
- base += schema.format === "int64" ? `.min(BigInt(${schema.minimum}))` : `.min(${schema.minimum})`;
785
- }
786
- if (typeof schema.maximum === "number") {
787
- base += schema.format === "int64" ? `.max(BigInt(${schema.maximum}))` : `.max(${schema.maximum})`;
788
- }
789
- if (typeof schema.multipleOf === "number") {
790
- base += `.refine((val) => Number.isInteger(val / ${schema.multipleOf}), "Must be a multiple of ${schema.multipleOf}")`;
791
- }
792
- return `${base}${defaultValue}${appendOptional2(required)}`;
793
- }
794
- handle(schema, required) {
795
- if (isRef(schema)) {
796
- return this.ref(schema.$ref, required);
797
- }
798
- if (schema.allOf && Array.isArray(schema.allOf)) {
799
- return this.allOf(schema.allOf ?? []);
800
- }
801
- if (schema.anyOf && Array.isArray(schema.anyOf)) {
802
- return this.anyOf(schema.anyOf ?? [], required);
803
- }
804
- if (schema.oneOf && Array.isArray(schema.oneOf)) {
805
- return this.oneOf(schema.oneOf ?? [], required);
806
- }
807
- if (schema.enum && Array.isArray(schema.enum)) {
808
- return this.enum(schema.enum, required);
809
- }
810
- const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
811
- if (!types.length) {
812
- return `z.unknown()${appendOptional2(required)}`;
813
- }
814
- if (types.length > 1) {
815
- const realTypes = types.filter((t) => t !== "null");
816
- if (realTypes.length === 1 && types.includes("null")) {
817
- const typeZod = this.normal(realTypes[0], schema, false);
818
- return `${typeZod}.nullable()${appendOptional2(required)}`;
819
- }
820
- const subSchemas = types.map((t) => this.normal(t, schema, false));
821
- return `z.union([${subSchemas.join(", ")}])${appendOptional2(required)}`;
822
- }
823
- return this.normal(types[0], schema, required);
824
- }
825
- };
826
- function appendOptional2(isRequired) {
827
- return isRequired ? "" : ".optional()";
828
- }
829
- function appendDefault(defaultValue) {
830
- return defaultValue !== void 0 ? `.default(${JSON.stringify(defaultValue)})` : "";
831
- }
832
- var responses = {
833
- "400": "BadRequest",
834
- "401": "Unauthorized",
835
- "402": "PaymentRequired",
836
- "403": "Forbidden",
837
- "404": "NotFound",
838
- "405": "MethodNotAllowed",
839
- "406": "NotAcceptable",
840
- "409": "Conflict",
841
- "413": "PayloadTooLarge",
842
- "410": "Gone",
843
- "422": "UnprocessableEntity",
844
- "429": "TooManyRequests",
845
- "500": "InternalServerError",
846
- "501": "NotImplemented",
847
- "502": "BadGateway",
848
- "503": "ServiceUnavailable",
849
- "504": "GatewayTimeout"
850
- };
851
- var defaults = {
852
- target: "javascript",
853
- style: "github",
854
- operationId: (operation, path, method) => {
855
- if (operation.operationId) {
856
- return spinalcase2(operation.operationId);
857
- }
858
- return camelcase2(`${method} ${path.replace(/[\\/\\{\\}]/g, " ").trim()}`);
859
- }
860
- };
861
- function generateCode(config) {
862
- const commonSchemas = {};
863
- const zodDeserialzer = new ZodDeserialzer(config.spec);
864
- const groups = {};
865
- const outputs = {};
866
- for (const [path, methods2] of Object.entries(config.spec.paths ?? {})) {
867
- for (const [method, operation] of Object.entries(methods2)) {
868
- const formatOperationId = config.operationId ?? defaults.operationId;
869
- const operationName = formatOperationId(operation, path, method);
870
- console.log(`Processing ${method} ${path}`);
871
- const groupName = (operation.tags ?? ["unknown"])[0];
872
- groups[groupName] ??= [];
873
- const inputs = {};
874
- const additionalProperties = [];
875
- for (const param of operation.parameters ?? []) {
876
- if (isRef(param)) {
877
- throw new Error(`Found reference in parameter ${param.$ref}`);
878
- }
879
- if (!param.schema) {
880
- throw new Error(`Schema not found for parameter ${param.name}`);
881
- }
882
- inputs[param.name] = {
883
- in: param.in,
884
- schema: ""
885
- };
886
- additionalProperties.push(param);
887
- }
888
- const security2 = operation.security ?? [];
889
- const securitySchemas = config.spec.components?.securitySchemes ?? {};
890
- const securityOptions = securityToOptions(security2, securitySchemas);
891
- Object.assign(inputs, securityOptions);
892
- additionalProperties.push(
893
- ...Object.entries(securityOptions).map(
894
- ([name, value]) => ({
895
- name,
896
- required: false,
897
- schema: {
898
- type: "string"
899
- },
900
- in: value.in
901
- })
902
- )
903
- );
904
- const types = {};
905
- const shortContenTypeMap = {
906
- "application/json": "json",
907
- "application/x-www-form-urlencoded": "urlencoded",
908
- "multipart/form-data": "formdata",
909
- "application/xml": "xml",
910
- "text/plain": "text"
911
- };
912
- let contentType;
913
- if (operation.requestBody && Object.keys(operation.requestBody).length) {
914
- const content = isRef(operation.requestBody) ? get2(followRef(config.spec, operation.requestBody.$ref), ["content"]) : operation.requestBody.content;
915
- for (const type in content) {
916
- const ctSchema = isRef(content[type].schema) ? followRef(config.spec, content[type].schema.$ref) : content[type].schema;
917
- if (!ctSchema) {
918
- console.warn(`Schema not found for ${type}`);
919
- continue;
920
- }
921
- const schema = merge({}, ctSchema, {
922
- required: additionalProperties.filter((p) => p.required).map((p) => p.name),
923
- properties: additionalProperties.reduce(
924
- (acc, p) => ({
925
- ...acc,
926
- [p.name]: p.schema
927
- }),
928
- {}
929
- )
930
- });
931
- for (const [name] of Object.entries(ctSchema.properties ?? {})) {
932
- inputs[name] = {
933
- in: "body",
934
- schema: ""
935
- };
936
- }
937
- types[shortContenTypeMap[type]] = zodDeserialzer.handle(schema, true);
938
- }
939
- if (content["application/json"]) {
940
- contentType = "json";
941
- } else if (content["application/x-www-form-urlencoded"]) {
942
- contentType = "urlencoded";
943
- } else if (content["multipart/form-data"]) {
944
- contentType = "formdata";
945
- } else {
946
- contentType = "json";
947
- }
948
- } else {
949
- const properties = additionalProperties.reduce(
950
- (acc, p) => ({
951
- ...acc,
952
- [p.name]: p.schema
953
- }),
954
- {}
955
- );
956
- types[shortContenTypeMap["application/json"]] = zodDeserialzer.handle(
957
- {
958
- type: "object",
959
- required: additionalProperties.filter((p) => p.required).map((p) => p.name),
960
- properties
961
- },
962
- true
963
- );
964
- }
965
- const errors = [];
966
- operation.responses ??= {};
967
- let foundResponse = false;
968
- const output = [`import z from 'zod';`];
969
- for (const status in operation.responses) {
970
- const response = operation.responses[status];
971
- const statusCode = +status;
972
- if (statusCode >= 400) {
973
- errors.push(responses[status] ?? "ProblematicResponse");
974
- }
975
- if (statusCode >= 200 && statusCode < 300) {
976
- foundResponse = true;
977
- const responseContent = get2(response, ["content"]);
978
- const isJson = responseContent && responseContent["application/json"];
979
- const imports = [];
980
- const typeScriptDeserialzer = new TypeScriptDeserialzer(
981
- config.spec,
982
- (schemaName, zod) => {
983
- commonSchemas[schemaName] = zod;
984
- imports.push({
985
- defaultImport: void 0,
986
- isTypeOnly: true,
987
- moduleSpecifier: `../models/${schemaName}.ts`,
988
- namedImports: [{ isTypeOnly: true, name: schemaName }],
989
- namespaceImport: void 0
990
- });
991
- }
992
- );
993
- const responseSchema = isJson ? typeScriptDeserialzer.handle(
994
- responseContent["application/json"].schema,
995
- true
996
- ) : "ReadableStream";
997
- for (const it of mergeImports(imports)) {
998
- const singleImport = it.defaultImport ?? it.namespaceImport;
999
- if (singleImport && responseSchema.includes(singleImport)) {
1000
- output.push(importsToString(it).join("\n"));
1001
- } else if (it.namedImports.length) {
1002
- for (const namedImport of it.namedImports) {
1003
- if (responseSchema.includes(namedImport.name)) {
1004
- output.push(importsToString(it).join("\n"));
1005
- }
1006
- }
1007
- }
1008
- }
1009
- output.push(
1010
- `export type ${pascalcase2(operationName + " output")} = ${responseSchema}`
1011
- );
1012
- }
1013
- }
1014
- if (!foundResponse) {
1015
- output.push(
1016
- `export type ${pascalcase2(operationName + " output")} = void`
1017
- );
1018
- }
1019
- outputs[`${spinalcase2(operationName)}.ts`] = output.join("\n");
1020
- groups[groupName].push({
1021
- name: operationName,
1022
- type: "http",
1023
- inputs,
1024
- errors: errors.length ? errors : ["ServerError"],
1025
- contentType,
1026
- schemas: types,
1027
- formatOutput: () => ({
1028
- import: pascalcase2(operationName + " output"),
1029
- use: pascalcase2(operationName + " output")
1030
- }),
1031
- trigger: {
1032
- path,
1033
- method
1034
- }
1035
- });
1036
- }
1037
- }
1038
- return { groups, commonSchemas, outputs };
1039
- }
1040
- var interceptors_default = "export interface Interceptor {\n before?: (request: Request) => Promise<Request> | Request;\n after?: (response: Response) => Promise<Response> | Response;\n}\n\nexport const createDefaultHeadersInterceptor = (\n getHeaders: () => Record<string, string | undefined>,\n) => {\n return {\n before(request: Request) {\n const headers = getHeaders();\n\n for (const [key, value] of Object.entries(headers)) {\n // Only set the header if it doesn't already exist and has a value\n if (value !== undefined && !request.headers.has(key)) {\n request.headers.set(key, value);\n }\n }\n\n return request;\n },\n };\n};\n\nexport const createBaseUrlInterceptor = (getBaseUrl: () => string) => {\n return {\n before(request: Request) {\n const baseUrl = getBaseUrl();\n if (request.url.startsWith('local://')) {\n return new Request(request.url.replace('local://', baseUrl), request);\n }\n return request;\n },\n };\n};\n\nexport const logInterceptor = {\n before(request: Request) {\n console.log('Request', request);\n return request;\n },\n after(response: Response) {\n console.log('Response', response);\n return response;\n },\n};\n";
1041
- var parse_response_default = "import { parse } from 'fast-content-type-parse';\n\nexport async function handleError(response: Response) {\n try {\n if (response.status >= 400 && response.status < 500) {\n const body = (await response.json()) as Record<string, any>;\n return {\n status: response.status,\n body: body,\n };\n }\n return new Error(\n `An error occurred while fetching the data. Status: ${response.status}`,\n );\n } catch (error) {\n return error as any;\n }\n}\n\nasync function handleChunkedResponse(response: Response, contentType: string) {\n const { type } = parse(contentType);\n\n switch (type) {\n case 'application/json': {\n let buffer = '';\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return JSON.parse(buffer);\n }\n case 'text/html':\n case 'text/plain': {\n let buffer = '';\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return buffer;\n }\n default:\n return response.body;\n }\n}\n\nexport async function parseResponse(response: Response) {\n const contentType = response.headers.get('Content-Type');\n if (!contentType) {\n throw new Error('Content-Type header is missing');\n }\n\n if (response.status === 204) {\n return null;\n }\n const isChunked = response.headers.get('Transfer-Encoding') === 'chunked';\n if (isChunked) {\n return response.body!;\n // return handleChunkedResponse(response, contentType);\n }\n\n const { type } = parse(contentType);\n switch (type) {\n case 'application/json':\n return response.json();\n case 'text/plain':\n return response.text();\n case 'text/html':\n return response.text();\n case 'text/xml':\n case 'application/xml':\n return response.text();\n case 'application/x-www-form-urlencoded': {\n const text = await response.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n case 'multipart/form-data':\n return response.formData();\n default:\n throw new Error(`Unsupported content type: ${contentType}`);\n }\n}\n";
1042
- var parser_default = "import { z } from 'zod';\n\nexport type ParseError<T extends z.ZodType<any, any, any>> = {\n kind: 'parse';\n} & z.inferFlattenedErrors<T>;\n\nexport function parse<T extends z.ZodType>(\n schema: T,\n input: unknown,\n) {\n const result = schema.safeParse(input);\n if (!result.success) {\n const errors = result.error.flatten((issue) => issue);\n return [null, errors];\n }\n return [result.data as z.infer<T>, null];\n}\n";
1043
- var request_default = "export type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\nexport type ContentType = 'xml' | 'json' | 'urlencoded' | 'multipart';\nexport type Endpoint =\n | `${ContentType} ${Method} ${string}`\n | `${Method} ${string}`;\n\nexport type BodyInit =\n | ArrayBuffer\n | Blob\n | FormData\n | URLSearchParams\n | null\n | string;\n\nexport function createUrl(path: string, query: URLSearchParams) {\n const url = new URL(path, `local://`);\n url.search = query.toString();\n return url;\n}\n\nfunction template(\n templateString: string,\n templateVariables: Record<string, any>,\n): string {\n const nargs = /{([0-9a-zA-Z_]+)}/g;\n return templateString.replace(nargs, (match, key: string, index: number) => {\n // Handle escaped double braces\n if (\n templateString[index - 1] === '{' &&\n templateString[index + match.length] === '}'\n ) {\n return key;\n }\n\n const result = key in templateVariables ? templateVariables[key] : null;\n return result === null || result === undefined ? '' : String(result);\n });\n}\n\ntype Input = Record<string, any>;\ntype Props = {\n inputHeaders: string[];\n inputQuery: string[];\n inputBody: string[];\n inputParams: string[];\n};\n\nabstract class Serializer {\n constructor(\n protected input: Input,\n protected props: Props,\n ) {}\n abstract getBody(): BodyInit | null;\n abstract getHeaders(): Record<string, string>;\n serialize(): Serialized {\n const headers = new Headers({});\n for (const header of this.props.inputHeaders) {\n headers.set(header, this.input[header]);\n }\n\n const query = new URLSearchParams();\n for (const key of this.props.inputQuery) {\n const value = this.input[key];\n if (value !== undefined) {\n query.set(key, String(value));\n }\n }\n\n const params = this.props.inputParams.reduce<Record<string, any>>(\n (acc, key) => {\n acc[key] = this.input[key];\n return acc;\n },\n {},\n );\n\n return {\n body: this.getBody(),\n query,\n params,\n headers: this.getHeaders(),\n };\n }\n}\n\ninterface Serialized {\n body: BodyInit | null;\n query: URLSearchParams;\n params: Record<string, any>;\n headers: Record<string, string>;\n}\n\nclass JsonSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body: Record<string, any> = {};\n for (const prop of this.props.inputBody) {\n body[prop] = this.input[prop];\n }\n return JSON.stringify(body);\n }\n getHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n }\n}\n\nclass UrlencodedSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body = new URLSearchParams();\n for (const prop of this.props.inputBody) {\n body.set(prop, this.input[prop]);\n }\n return body;\n }\n getHeaders(): Record<string, string> {\n return {};\n }\n}\n\nclass NoBodySerializer extends Serializer {\n getBody(): BodyInit | null {\n return null;\n }\n getHeaders(): Record<string, string> {\n return {};\n }\n}\n\nclass FormDataSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body = new FormData();\n for (const prop of this.props.inputBody) {\n body.append(prop, this.input[prop]);\n }\n return body;\n }\n getHeaders(): Record<string, string> {\n return {};\n }\n}\n\nexport function json(input: Input, props: Props) {\n return new JsonSerializer(input, props).serialize();\n}\nexport function urlencoded(input: Input, props: Props) {\n return new UrlencodedSerializer(input, props).serialize();\n}\nexport function nobody(input: Input, props: Props) {\n return new NoBodySerializer(input, props).serialize();\n}\nexport function formdata(input: Input, props: Props) {\n return new FormDataSerializer(input, props).serialize();\n}\n\nexport function toRequest<T extends Endpoint>(\n endpoint: T,\n input: Serialized,\n): Request {\n const [method, path] = endpoint.split(' ');\n const pathVariable = template(path, input.params);\n\n const url = createUrl(pathVariable, input.query);\n return new Request(url, {\n method: method,\n headers: input.headers,\n body: method === 'GET' ? undefined : input.body,\n });\n}\n";
1044
- var response_default = "export interface ApiResponse<Status extends number, Body extends unknown> {\n kind: 'response';\n status: Status;\n body: Body;\n}\n\n// 4xx Client Errors\nexport type BadRequest = ApiResponse<400, { message: string }>;\nexport type Unauthorized = ApiResponse<401, { message: string }>;\nexport type PaymentRequired = ApiResponse<402, { message: string }>;\nexport type Forbidden = ApiResponse<403, { message: string }>;\nexport type NotFound = ApiResponse<404, { message: string }>;\nexport type MethodNotAllowed = ApiResponse<405, { message: string }>;\nexport type NotAcceptable = ApiResponse<406, { message: string }>;\nexport type Conflict = ApiResponse<409, { message: string }>;\nexport type Gone = ApiResponse<410, { message: string }>;\nexport type UnprocessableEntity = ApiResponse<422, { message: string; errors?: Record<string, string[]> }>;\nexport type TooManyRequests = ApiResponse<429, { message: string; retryAfter?: string }>;\nexport type PayloadTooLarge = ApiResponse<413, { message: string; }>;\nexport type UnsupportedMediaType = ApiResponse<415, { message: string; }>;\n\n// 5xx Server Errors\nexport type InternalServerError = ApiResponse<500, { message: string }>;\nexport type NotImplemented = ApiResponse<501, { message: string }>;\nexport type BadGateway = ApiResponse<502, { message: string }>;\nexport type ServiceUnavailable = ApiResponse<503, { message: string; retryAfter?: string }>;\nexport type GatewayTimeout = ApiResponse<504, { message: string }>;\n\nexport type ClientError =\n | BadRequest\n | Unauthorized\n | PaymentRequired\n | Forbidden\n | NotFound\n | MethodNotAllowed\n | NotAcceptable\n | Conflict\n | Gone\n | UnprocessableEntity\n | TooManyRequests;\n\nexport type ServerError =\n | InternalServerError\n | NotImplemented\n | BadGateway\n | ServiceUnavailable\n | GatewayTimeout;\n\nexport type ProblematicResponse = ClientError | ServerError;\n";
1045
- var send_request_default = "import z from 'zod';\n\nimport type { Interceptor } from './interceptors.ts';\nimport { handleError, parseResponse } from './parse-response.ts';\nimport { parse } from './parser.ts';\n\nexport interface RequestSchema {\n schema: z.ZodType;\n toRequest: (input: any) => Request;\n}\n\nexport const fetchType = z\n .function()\n .args(z.instanceof(Request))\n .returns(z.promise(z.instanceof(Response)))\n .optional();\n\nexport async function sendRequest(\n input: any,\n route: RequestSchema,\n options: {\n fetch?: z.infer<typeof fetchType>;\n interceptors?: Interceptor[];\n },\n) {\n const { interceptors = [] } = options;\n const [parsedInput, parseError] = parse(route.schema, input);\n if (parseError) {\n return [null as never, { ...parseError, kind: 'parse' } as never] as const;\n }\n\n let request = route.toRequest(parsedInput as never);\n for (const interceptor of interceptors) {\n if (interceptor.before) {\n request = await interceptor.before(request);\n }\n }\n\n let response = await (options.fetch ?? fetch)(request);\n\n for (let i = interceptors.length - 1; i >= 0; i--) {\n const interceptor = interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n\n if (response.ok) {\n const data = await parseResponse(response);\n return [data as never, null] as const;\n }\n const error = await handleError(response);\n return [null as never, { ...error, kind: 'response' }] as const;\n}\n";
1046
- function security(spec) {
1047
- const security2 = spec.security || [];
1048
- const components = spec.components || {};
1049
- const securitySchemas = components.securitySchemes || {};
1050
- const paths = Object.values(spec.paths ?? {});
1051
- const options = securityToOptions(security2, securitySchemas);
1052
- for (const it of paths) {
1053
- for (const method of methods) {
1054
- const operation = it[method];
1055
- if (!operation) {
1056
- continue;
1057
- }
1058
- Object.assign(
1059
- options,
1060
- securityToOptions(operation.security || [], securitySchemas, "input")
1061
- );
1062
- }
1063
- }
1064
- return options;
1065
- }
1066
- async function generate(spec, settings) {
1067
- const { commonSchemas, groups, outputs } = generateCode({
1068
- spec,
1069
- style: "github",
1070
- target: "javascript"
1071
- });
1072
- const output = settings.mode === "full" ? join2(settings.output, "src") : settings.output;
1073
- const options = security(spec);
1074
- const clientFiles = generateClientSdk({
1075
- name: settings.name || "Client",
1076
- operations: groups,
1077
- servers: spec.servers?.map((server) => server.url) || [],
1078
- options
1079
- });
1080
- await writeFiles(output, {
1081
- "outputs/index.ts": "",
1082
- "inputs/index.ts": ""
1083
- // 'models/index.ts': '',
1084
- // 'README.md': readme,
1085
- });
1086
- await writeFiles(join2(output, "http"), {
1087
- "interceptors.ts": interceptors_default,
1088
- "parse-response.ts": parse_response_default,
1089
- "send-request.ts": send_request_default,
1090
- "response.ts": response_default,
1091
- "parser.ts": parser_default,
1092
- "request.ts": request_default
1093
- });
1094
- await writeFiles(join2(output, "outputs"), outputs);
1095
- const imports = Object.entries(commonSchemas).map(([name]) => name);
1096
- await writeFiles(output, {
1097
- ...clientFiles,
1098
- ...Object.fromEntries(
1099
- Object.entries(commonSchemas).map(([name, schema]) => [
1100
- `models/${name}.ts`,
1101
- [
1102
- `import { z } from 'zod';`,
1103
- ...exclude(imports, [name]).map(
1104
- (it) => `import type { ${it} } from './${it}.ts';`
1105
- ),
1106
- `export type ${name} = ${schema};`
1107
- ].join("\n")
1108
- ])
1109
- )
1110
- });
1111
- const folders = [
1112
- getFolderExports(output),
1113
- getFolderExports(join2(output, "outputs")),
1114
- getFolderExports(join2(output, "inputs")),
1115
- getFolderExports(join2(output, "http"))
1116
- ];
1117
- if (imports.length) {
1118
- folders.push(getFolderExports(join2(output, "models")));
1119
- }
1120
- const [index, outputIndex, inputsIndex, httpIndex, modelsIndex] = await Promise.all(folders);
1121
- await writeFiles(output, {
1122
- "index.ts": index,
1123
- "outputs/index.ts": outputIndex,
1124
- "inputs/index.ts": inputsIndex,
1125
- "http/index.ts": httpIndex,
1126
- ...imports.length ? { "models/index.ts": modelsIndex } : {}
1127
- });
1128
- if (settings.mode === "full") {
1129
- await writeFiles(settings.output, {
1130
- "package.json": {
1131
- ignoreIfExists: true,
1132
- content: `{"type":"module","main":"./src/index.ts","dependencies":{"fast-content-type-parse":"^3.0.0"}}`
1133
- }
1134
- });
1135
- }
1136
- await settings.formatCode?.({
1137
- output,
1138
- env: npmRunPathEnv()
1139
- });
1140
- }
1141
- function exclude(list, exclude2) {
1142
- return list.filter((it) => !exclude2.includes(it));
1143
- }
1144
-
1145
- // packages/cli/src/lib/generate.ts
10
+ import { generate } from "@sdk-it/typescript";
1146
11
  var specOption = new Option(
1147
12
  "-s, --spec <spec>",
1148
13
  "Path to OpenAPI specification file"
@@ -1173,7 +38,7 @@ async function loadLocal(location) {
1173
38
  return import(location);
1174
39
  case ".yaml":
1175
40
  case ".yml": {
1176
- const text = await await readFile2(location, "utf-8");
41
+ const text = await await readFile(location, "utf-8");
1177
42
  return parse(text);
1178
43
  }
1179
44
  default: