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