@sdk-it/typescript 0.19.1 → 0.21.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.js CHANGED
@@ -1,183 +1,1348 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined")
11
+ return require.apply(this, arguments);
12
+ throw Error('Dynamic require of "' + x + '" is not supported');
13
+ });
14
+ var __commonJS = (cb, mod) => function __require2() {
15
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+
34
+ // node_modules/pluralize/pluralize.js
35
+ var require_pluralize = __commonJS({
36
+ "node_modules/pluralize/pluralize.js"(exports, module) {
37
+ "use strict";
38
+ (function(root, pluralize3) {
39
+ if (typeof __require === "function" && typeof exports === "object" && typeof module === "object") {
40
+ module.exports = pluralize3();
41
+ } else if (typeof define === "function" && define.amd) {
42
+ define(function() {
43
+ return pluralize3();
44
+ });
45
+ } else {
46
+ root.pluralize = pluralize3();
47
+ }
48
+ })(exports, function() {
49
+ var pluralRules = [];
50
+ var singularRules = [];
51
+ var uncountables = {};
52
+ var irregularPlurals = {};
53
+ var irregularSingles = {};
54
+ function sanitizeRule(rule) {
55
+ if (typeof rule === "string") {
56
+ return new RegExp("^" + rule + "$", "i");
57
+ }
58
+ return rule;
59
+ }
60
+ function restoreCase(word, token) {
61
+ if (word === token)
62
+ return token;
63
+ if (word === word.toLowerCase())
64
+ return token.toLowerCase();
65
+ if (word === word.toUpperCase())
66
+ return token.toUpperCase();
67
+ if (word[0] === word[0].toUpperCase()) {
68
+ return token.charAt(0).toUpperCase() + token.substr(1).toLowerCase();
69
+ }
70
+ return token.toLowerCase();
71
+ }
72
+ function interpolate(str, args) {
73
+ return str.replace(/\$(\d{1,2})/g, function(match, index) {
74
+ return args[index] || "";
75
+ });
76
+ }
77
+ function replace(word, rule) {
78
+ return word.replace(rule[0], function(match, index) {
79
+ var result = interpolate(rule[1], arguments);
80
+ if (match === "") {
81
+ return restoreCase(word[index - 1], result);
82
+ }
83
+ return restoreCase(match, result);
84
+ });
85
+ }
86
+ function sanitizeWord(token, word, rules) {
87
+ if (!token.length || uncountables.hasOwnProperty(token)) {
88
+ return word;
89
+ }
90
+ var len = rules.length;
91
+ while (len--) {
92
+ var rule = rules[len];
93
+ if (rule[0].test(word))
94
+ return replace(word, rule);
95
+ }
96
+ return word;
97
+ }
98
+ function replaceWord(replaceMap, keepMap, rules) {
99
+ return function(word) {
100
+ var token = word.toLowerCase();
101
+ if (keepMap.hasOwnProperty(token)) {
102
+ return restoreCase(word, token);
103
+ }
104
+ if (replaceMap.hasOwnProperty(token)) {
105
+ return restoreCase(word, replaceMap[token]);
106
+ }
107
+ return sanitizeWord(token, word, rules);
108
+ };
109
+ }
110
+ function checkWord(replaceMap, keepMap, rules, bool) {
111
+ return function(word) {
112
+ var token = word.toLowerCase();
113
+ if (keepMap.hasOwnProperty(token))
114
+ return true;
115
+ if (replaceMap.hasOwnProperty(token))
116
+ return false;
117
+ return sanitizeWord(token, token, rules) === token;
118
+ };
119
+ }
120
+ function pluralize3(word, count, inclusive) {
121
+ var pluralized = count === 1 ? pluralize3.singular(word) : pluralize3.plural(word);
122
+ return (inclusive ? count + " " : "") + pluralized;
123
+ }
124
+ pluralize3.plural = replaceWord(
125
+ irregularSingles,
126
+ irregularPlurals,
127
+ pluralRules
128
+ );
129
+ pluralize3.isPlural = checkWord(
130
+ irregularSingles,
131
+ irregularPlurals,
132
+ pluralRules
133
+ );
134
+ pluralize3.singular = replaceWord(
135
+ irregularPlurals,
136
+ irregularSingles,
137
+ singularRules
138
+ );
139
+ pluralize3.isSingular = checkWord(
140
+ irregularPlurals,
141
+ irregularSingles,
142
+ singularRules
143
+ );
144
+ pluralize3.addPluralRule = function(rule, replacement) {
145
+ pluralRules.push([sanitizeRule(rule), replacement]);
146
+ };
147
+ pluralize3.addSingularRule = function(rule, replacement) {
148
+ singularRules.push([sanitizeRule(rule), replacement]);
149
+ };
150
+ pluralize3.addUncountableRule = function(word) {
151
+ if (typeof word === "string") {
152
+ uncountables[word.toLowerCase()] = true;
153
+ return;
154
+ }
155
+ pluralize3.addPluralRule(word, "$0");
156
+ pluralize3.addSingularRule(word, "$0");
157
+ };
158
+ pluralize3.addIrregularRule = function(single, plural) {
159
+ plural = plural.toLowerCase();
160
+ single = single.toLowerCase();
161
+ irregularSingles[single] = plural;
162
+ irregularPlurals[plural] = single;
163
+ };
164
+ [
165
+ // Pronouns.
166
+ ["I", "we"],
167
+ ["me", "us"],
168
+ ["he", "they"],
169
+ ["she", "they"],
170
+ ["them", "them"],
171
+ ["myself", "ourselves"],
172
+ ["yourself", "yourselves"],
173
+ ["itself", "themselves"],
174
+ ["herself", "themselves"],
175
+ ["himself", "themselves"],
176
+ ["themself", "themselves"],
177
+ ["is", "are"],
178
+ ["was", "were"],
179
+ ["has", "have"],
180
+ ["this", "these"],
181
+ ["that", "those"],
182
+ // Words ending in with a consonant and `o`.
183
+ ["echo", "echoes"],
184
+ ["dingo", "dingoes"],
185
+ ["volcano", "volcanoes"],
186
+ ["tornado", "tornadoes"],
187
+ ["torpedo", "torpedoes"],
188
+ // Ends with `us`.
189
+ ["genus", "genera"],
190
+ ["viscus", "viscera"],
191
+ // Ends with `ma`.
192
+ ["stigma", "stigmata"],
193
+ ["stoma", "stomata"],
194
+ ["dogma", "dogmata"],
195
+ ["lemma", "lemmata"],
196
+ ["schema", "schemata"],
197
+ ["anathema", "anathemata"],
198
+ // Other irregular rules.
199
+ ["ox", "oxen"],
200
+ ["axe", "axes"],
201
+ ["die", "dice"],
202
+ ["yes", "yeses"],
203
+ ["foot", "feet"],
204
+ ["eave", "eaves"],
205
+ ["goose", "geese"],
206
+ ["tooth", "teeth"],
207
+ ["quiz", "quizzes"],
208
+ ["human", "humans"],
209
+ ["proof", "proofs"],
210
+ ["carve", "carves"],
211
+ ["valve", "valves"],
212
+ ["looey", "looies"],
213
+ ["thief", "thieves"],
214
+ ["groove", "grooves"],
215
+ ["pickaxe", "pickaxes"],
216
+ ["passerby", "passersby"]
217
+ ].forEach(function(rule) {
218
+ return pluralize3.addIrregularRule(rule[0], rule[1]);
219
+ });
220
+ [
221
+ [/s?$/i, "s"],
222
+ [/[^\u0000-\u007F]$/i, "$0"],
223
+ [/([^aeiou]ese)$/i, "$1"],
224
+ [/(ax|test)is$/i, "$1es"],
225
+ [/(alias|[^aou]us|t[lm]as|gas|ris)$/i, "$1es"],
226
+ [/(e[mn]u)s?$/i, "$1s"],
227
+ [/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i, "$1"],
228
+ [/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i, "$1i"],
229
+ [/(alumn|alg|vertebr)(?:a|ae)$/i, "$1ae"],
230
+ [/(seraph|cherub)(?:im)?$/i, "$1im"],
231
+ [/(her|at|gr)o$/i, "$1oes"],
232
+ [/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i, "$1a"],
233
+ [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)(?:a|on)$/i, "$1a"],
234
+ [/sis$/i, "ses"],
235
+ [/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i, "$1$2ves"],
236
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
237
+ [/([^ch][ieo][ln])ey$/i, "$1ies"],
238
+ [/(x|ch|ss|sh|zz)$/i, "$1es"],
239
+ [/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i, "$1ices"],
240
+ [/\b((?:tit)?m|l)(?:ice|ouse)$/i, "$1ice"],
241
+ [/(pe)(?:rson|ople)$/i, "$1ople"],
242
+ [/(child)(?:ren)?$/i, "$1ren"],
243
+ [/eaux$/i, "$0"],
244
+ [/m[ae]n$/i, "men"],
245
+ ["thou", "you"]
246
+ ].forEach(function(rule) {
247
+ return pluralize3.addPluralRule(rule[0], rule[1]);
248
+ });
249
+ [
250
+ [/s$/i, ""],
251
+ [/(ss)$/i, "$1"],
252
+ [/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i, "$1fe"],
253
+ [/(ar|(?:wo|[ae])l|[eo][ao])ves$/i, "$1f"],
254
+ [/ies$/i, "y"],
255
+ [/\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i, "$1ie"],
256
+ [/\b(mon|smil)ies$/i, "$1ey"],
257
+ [/\b((?:tit)?m|l)ice$/i, "$1ouse"],
258
+ [/(seraph|cherub)im$/i, "$1"],
259
+ [/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i, "$1"],
260
+ [/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i, "$1sis"],
261
+ [/(movie|twelve|abuse|e[mn]u)s$/i, "$1"],
262
+ [/(test)(?:is|es)$/i, "$1is"],
263
+ [/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i, "$1us"],
264
+ [/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i, "$1um"],
265
+ [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i, "$1on"],
266
+ [/(alumn|alg|vertebr)ae$/i, "$1a"],
267
+ [/(cod|mur|sil|vert|ind)ices$/i, "$1ex"],
268
+ [/(matr|append)ices$/i, "$1ix"],
269
+ [/(pe)(rson|ople)$/i, "$1rson"],
270
+ [/(child)ren$/i, "$1"],
271
+ [/(eau)x?$/i, "$1"],
272
+ [/men$/i, "man"]
273
+ ].forEach(function(rule) {
274
+ return pluralize3.addSingularRule(rule[0], rule[1]);
275
+ });
276
+ [
277
+ // Singular words with no plurals.
278
+ "adulthood",
279
+ "advice",
280
+ "agenda",
281
+ "aid",
282
+ "aircraft",
283
+ "alcohol",
284
+ "ammo",
285
+ "analytics",
286
+ "anime",
287
+ "athletics",
288
+ "audio",
289
+ "bison",
290
+ "blood",
291
+ "bream",
292
+ "buffalo",
293
+ "butter",
294
+ "carp",
295
+ "cash",
296
+ "chassis",
297
+ "chess",
298
+ "clothing",
299
+ "cod",
300
+ "commerce",
301
+ "cooperation",
302
+ "corps",
303
+ "debris",
304
+ "diabetes",
305
+ "digestion",
306
+ "elk",
307
+ "energy",
308
+ "equipment",
309
+ "excretion",
310
+ "expertise",
311
+ "firmware",
312
+ "flounder",
313
+ "fun",
314
+ "gallows",
315
+ "garbage",
316
+ "graffiti",
317
+ "hardware",
318
+ "headquarters",
319
+ "health",
320
+ "herpes",
321
+ "highjinks",
322
+ "homework",
323
+ "housework",
324
+ "information",
325
+ "jeans",
326
+ "justice",
327
+ "kudos",
328
+ "labour",
329
+ "literature",
330
+ "machinery",
331
+ "mackerel",
332
+ "mail",
333
+ "media",
334
+ "mews",
335
+ "moose",
336
+ "music",
337
+ "mud",
338
+ "manga",
339
+ "news",
340
+ "only",
341
+ "personnel",
342
+ "pike",
343
+ "plankton",
344
+ "pliers",
345
+ "police",
346
+ "pollution",
347
+ "premises",
348
+ "rain",
349
+ "research",
350
+ "rice",
351
+ "salmon",
352
+ "scissors",
353
+ "series",
354
+ "sewage",
355
+ "shambles",
356
+ "shrimp",
357
+ "software",
358
+ "species",
359
+ "staff",
360
+ "swine",
361
+ "tennis",
362
+ "traffic",
363
+ "transportation",
364
+ "trout",
365
+ "tuna",
366
+ "wealth",
367
+ "welfare",
368
+ "whiting",
369
+ "wildebeest",
370
+ "wildlife",
371
+ "you",
372
+ /pok[eé]mon$/i,
373
+ // Regexes.
374
+ /[^aeiou]ese$/i,
375
+ // "chinese", "japanese"
376
+ /deer$/i,
377
+ // "deer", "reindeer"
378
+ /fish$/i,
379
+ // "fish", "blowfish", "angelfish"
380
+ /measles$/i,
381
+ /o[iu]s$/i,
382
+ // "carnivorous"
383
+ /pox$/i,
384
+ // "chickpox", "smallpox"
385
+ /sheep$/i
386
+ ].forEach(pluralize3.addUncountableRule);
387
+ return pluralize3;
388
+ });
389
+ }
390
+ });
391
+
1
392
  // packages/typescript/src/lib/generate.ts
2
393
  import { template as template2 } from "lodash-es";
3
- import { join as join2 } from "node:path";
394
+ import { readFile, readdir, unlink, writeFile } from "node:fs/promises";
395
+ import { join as join2, relative, sep } from "node:path";
4
396
  import { npmRunPathEnv } from "npm-run-path";
5
- import { spinalcase as spinalcase3 } from "stringcase";
6
- import { getFolderExports, methods, writeFiles } from "@sdk-it/core";
7
-
8
- // packages/typescript/src/lib/client.ts
9
- import { toLitObject } from "@sdk-it/core";
10
- var client_default = (spec, style) => {
11
- const optionsEntries = Object.entries(spec.options).map(
12
- ([key, value]) => [`'${key}'`, value]
13
- );
14
- const defaultHeaders = `{${optionsEntries.filter(([, value]) => value.in === "header").map(
15
- ([key, value]) => `${key}: this.options[${value.optionName ? `'${value.optionName}'` : key}]`
16
- ).join(",\n")}}`;
17
- const defaultInputs = `{${optionsEntries.filter(([, value]) => value.in === "input").map(
18
- ([key, value]) => `${key}: this.options[${value.optionName ? `'${value.optionName}'` : key}]`
19
- ).join(",\n")}}`;
20
- const specOptions = {
21
- ...Object.fromEntries(
22
- optionsEntries.map(([key, value]) => [value.optionName ?? key, value])
23
- ),
24
- fetch: {
25
- schema: "fetchType"
26
- },
27
- baseUrl: {
28
- schema: spec.servers.length ? `z.enum(servers).default(servers[0])` : "z.string()"
29
- }
30
- };
31
- return `
32
- import type { HeadersInit, RequestConfig } from './http/${spec.makeImport("request")}';
33
- import { fetchType, dispatch, parse } from './http/${spec.makeImport("send-request")}';
34
- import z from 'zod';
35
- import type { Endpoints } from './api/${spec.makeImport("endpoints")}';
36
- import schemas from './api/${spec.makeImport("schemas")}';
397
+ import { spinalcase as spinalcase4 } from "stringcase";
398
+ import { methods, pascalcase as pascalcase4 } from "@sdk-it/core";
37
399
  import {
38
- createBaseUrlInterceptor,
39
- createHeadersInterceptor,
40
- } from './http/${spec.makeImport("interceptors")}';
41
-
42
- import { parseInput, type ParseError } from './http/${spec.makeImport("parser")}';
43
-
44
- ${spec.servers.length ? `export const servers = ${JSON.stringify(spec.servers, null, 2)} as const` : ""}
45
- const optionsSchema = z.object(${toLitObject(specOptions, (x) => x.schema)});
46
- ${spec.servers.length ? `export type Servers = typeof servers[number];` : ""}
47
-
48
- type ${spec.name}Options = z.infer<typeof optionsSchema>;
49
-
50
- export class ${spec.name} {
51
- public options: ${spec.name}Options
52
- constructor(options: ${spec.name}Options) {
53
- this.options = optionsSchema.parse(options);
400
+ addLeadingSlash,
401
+ exist,
402
+ getFolderExports,
403
+ readFolder,
404
+ writeFiles
405
+ } from "@sdk-it/core/file-system.js";
406
+
407
+ // packages/readme/dist/index.js
408
+ var import_pluralize = __toESM(require_pluralize(), 1);
409
+ import { isEmpty as isEmpty2 } from "@sdk-it/core";
410
+ import { camelcase } from "stringcase";
411
+ import { followRef, isRef as isRef2 } from "@sdk-it/core/ref.js";
412
+ import { isRef } from "@sdk-it/core/ref.js";
413
+ import { isEmpty } from "@sdk-it/core/utils.js";
414
+ import { followRef as followRef2, isRef as isRef3 } from "@sdk-it/core";
415
+ var HAS_MORE_POSITIVE_REGEX_PATTERNS = [
416
+ "\\bhas_?more\\b",
417
+ "\\bhas_?next\\b",
418
+ // e.g., itemsHasNext, items_has_next
419
+ "\\bmore_?items\\b",
420
+ "\\bnext_?page\\b",
421
+ // e.g., userNextPageFlag
422
+ "\\badditional\\b",
423
+ // e.g., hasAdditionalData, additional_results_exist
424
+ "\\bcontinuation\\b",
425
+ // e.g., continuationAvailable, has_continuation_token
426
+ "\\bmore_?results\\b",
427
+ "\\bpage_?available\\b",
428
+ "\\bnext(?:_?(page))?\\b"
429
+ ];
430
+ var COMPILED_HAS_MORE_POSITIVE_REGEXES = HAS_MORE_POSITIVE_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
431
+ var HAS_MORE_INVERTED_REGEX_PATTERNS = [
432
+ "\\bis_?last\\b",
433
+ // e.g., pageIsLast
434
+ "\\blast_?page\\b",
435
+ // e.g., resultsAreLastPage
436
+ "\\bend_?of_?(data|results|list|items|stream)\\b",
437
+ "\\bno_?more_?(items|data|results)?\\b",
438
+ "\\ball_?(items_?)?loaded\\b",
439
+ "\\bis_?complete\\b"
440
+ ];
441
+ var COMPILED_HAS_MORE_INVERTED_REGEXES = HAS_MORE_INVERTED_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
442
+ function forEachOperation(config, callback) {
443
+ const result = [];
444
+ for (const [path, pathItem] of Object.entries(config.spec.paths ?? {})) {
445
+ const { parameters = [], ...methods2 } = pathItem;
446
+ for (const [method, operation] of Object.entries(methods2)) {
447
+ const metadata = operation["x-oaiMeta"] ?? {};
448
+ const operationTag = operation.tags?.[0];
449
+ result.push(
450
+ callback(
451
+ {
452
+ name: metadata.name,
453
+ method,
454
+ path,
455
+ groupName: operationTag,
456
+ tag: operationTag
457
+ },
458
+ operation
459
+ )
460
+ );
461
+ }
54
462
  }
55
-
56
- async request<E extends keyof Endpoints>(
57
- endpoint: E,
58
- input: Endpoints[E]['input'],
59
- options?: { signal?: AbortSignal, headers?: HeadersInit },
60
- ) ${style.errorAsValue ? `: Promise<readonly [Endpoints[E]['output'], Endpoints[E]['error'] | null]>` : `: Promise<Endpoints[E]['output']>`} {
61
- const route = schemas[endpoint];
62
- const result = await dispatch(Object.assign(this.#defaultInputs, input), route, {
63
- fetch: this.options.fetch,
64
- interceptors: [
65
- createHeadersInterceptor(() => this.defaultHeaders, options?.headers ?? {}),
66
- createBaseUrlInterceptor(() => this.options.baseUrl),
67
- ],
68
- signal: options?.signal,
69
- });
70
- return ${style.errorAsValue ? `result as [Endpoints[E]['output'], Endpoints[E]['error'] | null]` : `result as Endpoints[E]['output']`};
463
+ return result;
464
+ }
465
+ var PropEmitter = class {
466
+ #spec;
467
+ constructor(spec) {
468
+ this.#spec = spec;
71
469
  }
72
-
73
- async prepare<E extends keyof Endpoints>(
74
- endpoint: E,
75
- input: Endpoints[E]['input'],
76
- options?: { headers?: HeadersInit },
77
- ): ${style.errorAsValue ? `Promise<
78
- readonly [
79
- RequestConfig & {
80
- parse: (response: Response) => ReturnType<typeof parse>;
81
- },
82
- ParseError<(typeof schemas)[E]['schema']> | null,
83
- ]
84
- >` : `Promise<RequestConfig & {
85
- parse: (response: Response) => ReturnType<typeof parse>;
86
- }>`} {
87
- const route = schemas[endpoint];
88
-
89
- const interceptors = [
90
- createHeadersInterceptor(
91
- () => this.defaultHeaders,
92
- options?.headers ?? {},
93
- ),
94
- createBaseUrlInterceptor(() => this.options.baseUrl),
470
+ /**
471
+ * Handle objects (properties)
472
+ */
473
+ #object(schema) {
474
+ const lines = [];
475
+ const properties = schema.properties || {};
476
+ if (Object.keys(properties).length > 0) {
477
+ lines.push(`**Properties:**`);
478
+ for (const [propName, propSchema] of Object.entries(properties)) {
479
+ const isRequired = (schema.required ?? []).includes(propName);
480
+ lines.push(...this.#property(propName, propSchema, isRequired));
481
+ }
482
+ }
483
+ if (schema.additionalProperties) {
484
+ lines.push(`**Additional Properties:**`);
485
+ if (typeof schema.additionalProperties === "boolean") {
486
+ lines.push(`- Allowed: ${schema.additionalProperties}`);
487
+ } else {
488
+ lines.push(
489
+ ...this.handle(schema.additionalProperties).map((l) => ` ${l}`)
490
+ );
491
+ }
492
+ }
493
+ return lines;
494
+ }
495
+ /**
496
+ * Format a property with its type and description
497
+ */
498
+ #property(name, schema, required) {
499
+ const docs = this.handle(schema);
500
+ const rawType = docs[0].replace("**Type:** ", "").replace(" (nullable)", "|null");
501
+ const defaultVal = !isRef3(schema) && schema.default !== void 0 ? ` default: ${JSON.stringify(schema.default)}` : "";
502
+ const reqMark = required ? " required" : "";
503
+ const summary = `- \`${name}\` ${rawType}${reqMark}${defaultVal}:`;
504
+ const detailLines = docs.slice(1).filter((l) => !l.startsWith("**Default:**")).map((l) => ` ${l}`);
505
+ return [summary, ...detailLines];
506
+ }
507
+ /**
508
+ * Handle array schemas
509
+ */
510
+ #array(schema) {
511
+ const lines = [];
512
+ lines.push(`**Array items:**`);
513
+ if (schema.items) {
514
+ const itemDocs = this.handle(schema.items);
515
+ lines.push(...itemDocs.map((line) => ` ${line}`));
516
+ } else {
517
+ lines.push(` **Type:** \`unknown\``);
518
+ }
519
+ if (schema.minItems !== void 0)
520
+ lines.push(`- Minimum items: ${schema.minItems}`);
521
+ if (schema.maxItems !== void 0)
522
+ lines.push(`- Maximum items: ${schema.maxItems}`);
523
+ if (schema.uniqueItems)
524
+ lines.push(`- Items must be unique.`);
525
+ return lines;
526
+ }
527
+ #ref($ref) {
528
+ const schemaName = $ref.split("/").pop() || "object";
529
+ const resolved = followRef2(this.#spec, $ref);
530
+ const lines = [
531
+ `**Type:** [\`${schemaName}\`](#${schemaName.toLowerCase()})`
95
532
  ];
96
- const [parsedInput, parseError] = parseInput(route.schema, input);
97
- if (parseError) {
98
- ${style.errorAsValue ? "return [null as never, parseError as never] as const;" : "throw parseError;"}
533
+ if (resolved.description) {
534
+ lines.push(resolved.description);
99
535
  }
100
-
101
- let config = route.toRequest(parsedInput as never);
102
- for (const interceptor of interceptors) {
103
- if (interceptor.before) {
104
- config = await interceptor.before(config);
536
+ return lines;
537
+ }
538
+ #allOf(schemas) {
539
+ const lines = ["**All of (Intersection):**"];
540
+ schemas.forEach((subSchema, index) => {
541
+ lines.push(`- **Constraint ${index + 1}:**`);
542
+ const subLines = this.handle(subSchema);
543
+ lines.push(...subLines.map((l) => ` ${l}`));
544
+ });
545
+ return lines;
546
+ }
547
+ #anyOf(schemas) {
548
+ const lines = ["**Any of (Union):**"];
549
+ schemas.forEach((subSchema, index) => {
550
+ lines.push(`- **Option ${index + 1}:**`);
551
+ const subLines = this.handle(subSchema);
552
+ lines.push(...subLines.map((l) => ` ${l}`));
553
+ });
554
+ return lines;
555
+ }
556
+ #oneOf(schemas) {
557
+ const lines = ["**One of (Exclusive Union):**"];
558
+ schemas.forEach((subSchema, index) => {
559
+ lines.push(`- **Option ${index + 1}:**`);
560
+ const subLines = this.handle(subSchema);
561
+ lines.push(...subLines.map((l) => ` ${l}`));
562
+ });
563
+ return lines;
564
+ }
565
+ #enum(schema) {
566
+ const lines = [`**Type:** \`${schema.type || "unknown"}\` (enum)`];
567
+ if (schema.description)
568
+ lines.push(schema.description);
569
+ lines.push("**Allowed values:**");
570
+ lines.push(
571
+ ...(schema.enum || []).map((val) => `- \`${JSON.stringify(val)}\``)
572
+ );
573
+ if (schema.default !== void 0) {
574
+ lines.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
575
+ }
576
+ return lines;
577
+ }
578
+ #normal(type, schema, nullable) {
579
+ const lines = [];
580
+ const nullableSuffix = nullable ? " (nullable)" : "";
581
+ const description = schema.description ? [schema.description] : [];
582
+ switch (type) {
583
+ case "string":
584
+ lines.push(
585
+ `**Type:** \`string\`${schema.format ? ` (format: ${schema.format})` : ""}${nullableSuffix}`
586
+ );
587
+ lines.push(...description);
588
+ if (schema.minLength !== void 0)
589
+ lines.push(`- Minimum length: ${schema.minLength}`);
590
+ if (schema.maxLength !== void 0)
591
+ lines.push(`- Maximum length: ${schema.maxLength}`);
592
+ if (schema.pattern !== void 0)
593
+ lines.push(`- Pattern: \`${schema.pattern}\``);
594
+ break;
595
+ case "number":
596
+ case "integer":
597
+ lines.push(
598
+ `**Type:** \`${type}\`${schema.format ? ` (format: ${schema.format})` : ""}${nullableSuffix}`
599
+ );
600
+ lines.push(...description);
601
+ if (schema.minimum !== void 0) {
602
+ const exclusiveMin = typeof schema.exclusiveMinimum === "number";
603
+ lines.push(
604
+ `- Minimum: ${schema.minimum}${exclusiveMin ? " (exclusive)" : ""}`
605
+ );
606
+ if (exclusiveMin) {
607
+ lines.push(
608
+ `- Must be strictly greater than: ${schema.exclusiveMinimum}`
609
+ );
610
+ }
611
+ } else if (typeof schema.exclusiveMinimum === "number") {
612
+ lines.push(
613
+ `- Must be strictly greater than: ${schema.exclusiveMinimum}`
614
+ );
615
+ }
616
+ if (schema.maximum !== void 0) {
617
+ const exclusiveMax = typeof schema.exclusiveMaximum === "number";
618
+ lines.push(
619
+ `- Maximum: ${schema.maximum}${exclusiveMax ? " (exclusive)" : ""}`
620
+ );
621
+ if (exclusiveMax) {
622
+ lines.push(
623
+ `- Must be strictly less than: ${schema.exclusiveMaximum}`
624
+ );
625
+ }
626
+ } else if (typeof schema.exclusiveMaximum === "number") {
627
+ lines.push(
628
+ `- Must be strictly less than: ${schema.exclusiveMaximum}`
629
+ );
630
+ }
631
+ if (schema.multipleOf !== void 0)
632
+ lines.push(`- Must be a multiple of: ${schema.multipleOf}`);
633
+ break;
634
+ case "boolean":
635
+ lines.push(`**Type:** \`boolean\`${nullableSuffix}`);
636
+ lines.push(...description);
637
+ break;
638
+ case "object":
639
+ lines.push(`**Type:** \`object\`${nullableSuffix}`);
640
+ lines.push(...description);
641
+ lines.push(...this.#object(schema));
642
+ break;
643
+ case "array":
644
+ lines.push(`**Type:** \`array\`${nullableSuffix}`);
645
+ lines.push(...description);
646
+ lines.push(...this.#array(schema));
647
+ break;
648
+ case "null":
649
+ lines.push(`**Type:** \`null\``);
650
+ lines.push(...description);
651
+ break;
652
+ default:
653
+ lines.push(`**Type:** \`${type}\`${nullableSuffix}`);
654
+ lines.push(...description);
655
+ }
656
+ if (schema.default !== void 0) {
657
+ lines.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
658
+ }
659
+ return lines.filter((l) => l);
660
+ }
661
+ /**
662
+ * Handle schemas by resolving references and delegating to appropriate handler
663
+ */
664
+ handle(schemaOrRef) {
665
+ if (isRef3(schemaOrRef)) {
666
+ return this.#ref(schemaOrRef.$ref);
667
+ }
668
+ const schema = schemaOrRef;
669
+ if (schema.allOf && Array.isArray(schema.allOf)) {
670
+ return this.#allOf(schema.allOf);
671
+ }
672
+ if (schema.anyOf && Array.isArray(schema.anyOf)) {
673
+ return this.#anyOf(schema.anyOf);
674
+ }
675
+ if (schema.oneOf && Array.isArray(schema.oneOf)) {
676
+ return this.#oneOf(schema.oneOf);
677
+ }
678
+ if (schema.enum && Array.isArray(schema.enum)) {
679
+ return this.#enum(schema);
680
+ }
681
+ let types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
682
+ let nullable = false;
683
+ if (types.includes("null")) {
684
+ nullable = true;
685
+ types = types.filter((t) => t !== "null");
686
+ }
687
+ if (types.length === 0) {
688
+ if (schema.properties || schema.additionalProperties) {
689
+ types = ["object"];
690
+ } else if (schema.items) {
691
+ types = ["array"];
105
692
  }
106
693
  }
107
- const prepared = { ...config, parse: (response: Response) => parse(route, response) };
108
- return ${style.errorAsValue ? "[prepared, null as never] as const;" : "prepared"}
694
+ if (types.length === 0) {
695
+ const lines2 = ["**Type:** `unknown`"];
696
+ if (schema.description)
697
+ lines2.push(schema.description);
698
+ if (schema.default !== void 0)
699
+ lines2.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
700
+ return lines2;
701
+ }
702
+ if (types.length === 1) {
703
+ return this.#normal(types[0], schema, nullable);
704
+ }
705
+ const typeString = types.join(" | ");
706
+ const nullableSuffix = nullable ? " (nullable)" : "";
707
+ const lines = [`**Type:** \`${typeString}\`${nullableSuffix}`];
708
+ if (schema.description)
709
+ lines.push(schema.description);
710
+ if (schema.default !== void 0)
711
+ lines.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
712
+ return lines;
109
713
  }
110
-
111
- get defaultHeaders() {
112
- return ${defaultHeaders}
714
+ /**
715
+ * Process a request body and return markdown documentation
716
+ */
717
+ requestBody(requestBody) {
718
+ if (!requestBody)
719
+ return [];
720
+ const lines = [];
721
+ lines.push(`##### Request Body`);
722
+ if (requestBody.description) {
723
+ lines.push(requestBody.description);
724
+ }
725
+ if (requestBody.required) {
726
+ lines.push(`*This request body is required.*`);
727
+ }
728
+ if (requestBody.content) {
729
+ for (const [contentType, mediaType] of Object.entries(
730
+ requestBody.content
731
+ )) {
732
+ lines.push(`**Content Type:** \`${contentType}\``);
733
+ if (mediaType.schema) {
734
+ const schemaDocs = this.handle(mediaType.schema);
735
+ lines.push(...schemaDocs);
736
+ }
737
+ }
738
+ }
739
+ return lines;
113
740
  }
741
+ };
742
+ function toReadme(spec, generators) {
743
+ const markdown = [];
744
+ const propEmitter = new PropEmitter(spec);
745
+ forEachOperation({ spec }, (entry, operation) => {
746
+ const { method, path, name } = entry;
747
+ spec.components ??= {};
748
+ spec.components.schemas ??= {};
749
+ markdown.push(
750
+ `#### ${name || operation.operationId} | ${`_${method.toUpperCase()} ${path}_`}`
751
+ );
752
+ markdown.push(operation.summary || "");
753
+ const snippet = generators.generateSnippet(entry, operation);
754
+ markdown.push(`##### Example usage`);
755
+ markdown.push(snippet);
756
+ const requestBodyContent = propEmitter.requestBody(operation.requestBody);
757
+ if (requestBodyContent.length > 1) {
758
+ markdown.push(requestBodyContent.join("\n\n"));
759
+ }
760
+ markdown.push(`##### Responses`);
761
+ for (const status in operation.responses) {
762
+ const response = operation.responses[status];
763
+ markdown.push(`<details>`);
764
+ markdown.push(
765
+ `<summary><b>${status}</b> <i>${response.description}</i></summary>`
766
+ );
767
+ if (!isEmpty2(response.content)) {
768
+ for (const [contentType, mediaType] of Object.entries(
769
+ response.content
770
+ )) {
771
+ markdown.push(`
772
+ **Content Type:** \`${contentType}\``);
773
+ if (mediaType.schema) {
774
+ const schemaDocs = propEmitter.handle(mediaType.schema);
775
+ markdown.push(...schemaDocs.map((l) => `
776
+ ${l}`));
777
+ }
778
+ }
779
+ }
780
+ markdown.push(`</details>`);
781
+ }
782
+ });
783
+ return markdown.join("\n\n");
784
+ }
114
785
 
115
- get #defaultInputs() {
116
- return ${defaultInputs}
786
+ // packages/spec/dist/lib/operation.js
787
+ import { camelcase as camelcase2 } from "stringcase";
788
+ import { followRef as followRef3, isRef as isRef5 } from "@sdk-it/core/ref.js";
789
+
790
+ // packages/spec/dist/lib/pagination/pagination.js
791
+ import { isRef as isRef4 } from "@sdk-it/core/ref.js";
792
+ import { isEmpty as isEmpty3 } from "@sdk-it/core/utils.js";
793
+
794
+ // packages/spec/dist/lib/pagination/pagination-result.js
795
+ var import_pluralize2 = __toESM(require_pluralize(), 1);
796
+ var PRIMARY_TOP_TIER_KEYWORDS = [
797
+ "data",
798
+ "items",
799
+ "results",
800
+ "value"
801
+ ];
802
+ var PRIMARY_OTHER_KEYWORDS = [
803
+ "records",
804
+ "content",
805
+ "list",
806
+ "payload",
807
+ "entities",
808
+ "collection",
809
+ "users",
810
+ "products",
811
+ "orders",
812
+ "bookings",
813
+ "articles",
814
+ "posts",
815
+ "documents",
816
+ "events"
817
+ ];
818
+ var SECONDARY_KEYWORDS = ["entries", "rows", "elements"];
819
+ var PLURAL_DEPRIORITIZE_LIST = [
820
+ "status",
821
+ "success",
822
+ "address",
823
+ "details",
824
+ "properties",
825
+ "params",
826
+ "headers",
827
+ "cookies",
828
+ "series",
829
+ "links",
830
+ "meta",
831
+ "metadata",
832
+ "statistics",
833
+ "settings",
834
+ "options",
835
+ "permissions",
836
+ "credentials",
837
+ "diagnostics",
838
+ "warnings",
839
+ "errors",
840
+ "actions",
841
+ "attributes",
842
+ "categories",
843
+ "features",
844
+ "includes",
845
+ "tags"
846
+ ];
847
+ var HAS_MORE_PRIMARY_POSITIVE_EXACT = [
848
+ "hasmore",
849
+ "hasnext",
850
+ "hasnextpage",
851
+ "moreitems",
852
+ "moreitemsavailable",
853
+ "nextpage",
854
+ "nextpageexists",
855
+ "nextpageavailable",
856
+ "hasadditionalresults",
857
+ "moreresultsavailable",
858
+ "canloadmore",
859
+ "hasadditional",
860
+ "additionalitems",
861
+ "fetchmore"
862
+ ];
863
+ var HAS_MORE_SECONDARY_POSITIVE_EXACT = ["more", "next"];
864
+ var HAS_MORE_PRIMARY_INVERTED_EXACT = [
865
+ "islast",
866
+ "lastpage",
867
+ "endofresults",
868
+ "endoflist",
869
+ "nomoreitems",
870
+ "nomoredata",
871
+ "allitemsloaded",
872
+ "iscomplete",
873
+ "completed"
874
+ ];
875
+ var HAS_MORE_POSITIVE_REGEX_PATTERNS2 = [
876
+ "\\bhas_?more\\b",
877
+ "\\bhas_?next\\b",
878
+ // e.g., itemsHasNext, items_has_next
879
+ "\\bmore_?items\\b",
880
+ "\\bnext_?page\\b",
881
+ // e.g., userNextPageFlag
882
+ "\\badditional\\b",
883
+ // e.g., hasAdditionalData, additional_results_exist
884
+ "\\bcontinuation\\b",
885
+ // e.g., continuationAvailable, has_continuation_token
886
+ "\\bmore_?results\\b",
887
+ "\\bpage_?available\\b",
888
+ "\\bnext(?:_?(page))?\\b"
889
+ ];
890
+ var COMPILED_HAS_MORE_POSITIVE_REGEXES2 = HAS_MORE_POSITIVE_REGEX_PATTERNS2.map((p) => new RegExp(p, "i"));
891
+ var HAS_MORE_INVERTED_REGEX_PATTERNS2 = [
892
+ "\\bis_?last\\b",
893
+ // e.g., pageIsLast
894
+ "\\blast_?page\\b",
895
+ // e.g., resultsAreLastPage
896
+ "\\bend_?of_?(data|results|list|items|stream)\\b",
897
+ "\\bno_?more_?(items|data|results)?\\b",
898
+ "\\ball_?(items_?)?loaded\\b",
899
+ "\\bis_?complete\\b"
900
+ ];
901
+ var COMPILED_HAS_MORE_INVERTED_REGEXES2 = HAS_MORE_INVERTED_REGEX_PATTERNS2.map((p) => new RegExp(p, "i"));
902
+ function getItemsName(properties) {
903
+ const arrayPropertyNames = [];
904
+ for (const propName in properties) {
905
+ if (propName in properties) {
906
+ const propSchema = properties[propName];
907
+ if (propSchema && propSchema.type === "array") {
908
+ arrayPropertyNames.push(propName);
909
+ }
910
+ }
117
911
  }
118
-
119
- setOptions(options: Partial<${spec.name}Options>) {
120
- const validated = optionsSchema.partial().parse(options);
121
-
122
- for (const key of Object.keys(validated) as (keyof ${spec.name}Options)[]) {
123
- if (validated[key] !== undefined) {
124
- (this.options[key] as typeof validated[typeof key]) = validated[key]!;
912
+ if (arrayPropertyNames.length === 0) {
913
+ return null;
914
+ }
915
+ if (arrayPropertyNames.length === 1) {
916
+ return arrayPropertyNames[0];
917
+ }
918
+ let bestCandidate = null;
919
+ let candidateRank = Infinity;
920
+ const updateCandidate = (propName, rank) => {
921
+ if (rank < candidateRank) {
922
+ bestCandidate = propName;
923
+ candidateRank = rank;
924
+ }
925
+ };
926
+ for (const propName of arrayPropertyNames) {
927
+ const lowerPropName = propName.toLowerCase();
928
+ if (PRIMARY_TOP_TIER_KEYWORDS.includes(lowerPropName)) {
929
+ updateCandidate(propName, 2);
930
+ continue;
931
+ }
932
+ if (candidateRank > 3 && PRIMARY_OTHER_KEYWORDS.includes(lowerPropName)) {
933
+ updateCandidate(propName, 3);
934
+ continue;
935
+ }
936
+ if (candidateRank > 4 && SECONDARY_KEYWORDS.includes(lowerPropName)) {
937
+ updateCandidate(propName, 4);
938
+ continue;
939
+ }
940
+ if (candidateRank > 5 && import_pluralize2.default.isPlural(propName) && !PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
941
+ updateCandidate(propName, 5);
942
+ continue;
943
+ }
944
+ if (candidateRank > 6 && import_pluralize2.default.isPlural(propName) && PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
945
+ updateCandidate(propName, 6);
946
+ continue;
947
+ }
948
+ }
949
+ if (bestCandidate) {
950
+ return bestCandidate;
951
+ }
952
+ return arrayPropertyNames[0];
953
+ }
954
+ function guess(properties) {
955
+ const booleanPropertyNames = [];
956
+ for (const propName in properties) {
957
+ if (Object.prototype.hasOwnProperty.call(properties, propName)) {
958
+ const propSchema = properties[propName];
959
+ if (propSchema && propSchema.type === "boolean" || propSchema.type === "integer") {
960
+ booleanPropertyNames.push(propName);
125
961
  }
126
962
  }
127
963
  }
128
- }`;
129
- };
964
+ if (booleanPropertyNames.length === 0) {
965
+ return null;
966
+ }
967
+ if (booleanPropertyNames.length === 1) {
968
+ return booleanPropertyNames[0];
969
+ }
970
+ let bestCandidate = null;
971
+ let candidateRank = Infinity;
972
+ const updateCandidate = (propName, rank) => {
973
+ if (rank < candidateRank) {
974
+ bestCandidate = propName;
975
+ candidateRank = rank;
976
+ }
977
+ };
978
+ for (const propName of booleanPropertyNames) {
979
+ const normalizedForExactMatch = propName.toLowerCase().replace(/[-_]/g, "");
980
+ let currentPropRank = Infinity;
981
+ if (HAS_MORE_PRIMARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
982
+ currentPropRank = 1;
983
+ } else if (HAS_MORE_SECONDARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
984
+ currentPropRank = 2;
985
+ } else {
986
+ let foundPositiveRegex = false;
987
+ for (const regex of COMPILED_HAS_MORE_POSITIVE_REGEXES2) {
988
+ if (regex.test(propName)) {
989
+ currentPropRank = 3;
990
+ foundPositiveRegex = true;
991
+ break;
992
+ }
993
+ }
994
+ if (!foundPositiveRegex) {
995
+ if (HAS_MORE_PRIMARY_INVERTED_EXACT.includes(normalizedForExactMatch)) {
996
+ currentPropRank = 4;
997
+ } else {
998
+ for (const regex of COMPILED_HAS_MORE_INVERTED_REGEXES2) {
999
+ if (regex.test(propName)) {
1000
+ currentPropRank = 5;
1001
+ break;
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ }
1007
+ updateCandidate(propName, currentPropRank);
1008
+ }
1009
+ return bestCandidate;
1010
+ }
1011
+ function getHasMoreName(properties) {
1012
+ const rootGuess = guess(properties);
1013
+ if (rootGuess) {
1014
+ return rootGuess;
1015
+ }
1016
+ for (const propName in properties) {
1017
+ const propSchema = properties[propName];
1018
+ if (propSchema.type === "object" && propSchema.properties) {
1019
+ const nested = getHasMoreName(propSchema.properties);
1020
+ if (nested) {
1021
+ return propName + "." + nested;
1022
+ }
1023
+ }
1024
+ }
1025
+ return null;
1026
+ }
130
1027
 
131
- // packages/typescript/src/lib/generator.ts
132
- import { merge, template } from "lodash-es";
133
- import { join } from "node:path";
134
- import { camelcase as camelcase3, pascalcase as pascalcase2, spinalcase as spinalcase2 } from "stringcase";
135
- import { followRef as followRef4, isEmpty, isRef as isRef5 } from "@sdk-it/core";
1028
+ // packages/spec/dist/lib/pagination/pagination.js
1029
+ var OFFSET_PARAM_REGEXES = [
1030
+ /\boffset\b/i,
1031
+ /\bskip\b/i,
1032
+ /\bstart(?:ing_at|_index)?\b/i,
1033
+ // e.g., start, starting_at, start_index
1034
+ /\bfrom\b/i
1035
+ ];
1036
+ var GENERIC_LIMIT_PARAM_REGEXES = [
1037
+ /\blimit\b/i,
1038
+ /\bcount\b/i,
1039
+ /\b(?:page_?)?size\b/i,
1040
+ // e.g., size, page_size, pagesize
1041
+ /\bmax_results\b/i,
1042
+ /\bnum_results\b/i,
1043
+ /\bshow\b/i,
1044
+ // Can sometimes mean limit
1045
+ /\bper_?page\b/i,
1046
+ // e.g., per_page, perpage
1047
+ /\bper-page\b/i,
1048
+ /\btake\b/i
1049
+ ];
1050
+ var PAGE_NUMBER_REGEXES = [
1051
+ /^page$/i,
1052
+ // Exact match for "page"
1053
+ /^p$/i,
1054
+ // Exact match for "p" (common shorthand)
1055
+ /\bpage_?(?:number|num|idx|index)\b/i
1056
+ // e.g., page_number, pageNumber, page_num, page_idx
1057
+ ];
1058
+ var PAGE_SIZE_REGEXES = [
1059
+ /\bpage_?size\b/i,
1060
+ // e.g., page_size, pagesize
1061
+ /^size$/i,
1062
+ // Exact "size"
1063
+ // /\bsize\b/i, // Broader "size" - can be ambiguous, prefer more specific ones first
1064
+ /\blimit\b/i,
1065
+ // Limit is often used for page size
1066
+ /\bcount\b/i,
1067
+ // Count can also be used for page size
1068
+ /\bper_?page\b/i,
1069
+ // e.g., per_page, perpage
1070
+ /\bper-page\b/i,
1071
+ /\bnum_?(?:items|records|results)\b/i,
1072
+ // e.g., num_items, numitems
1073
+ /\bresults_?per_?page\b/i
1074
+ ];
1075
+ var CURSOR_REGEXES = [
1076
+ /\bcursor\b/i,
1077
+ /\bafter(?:_?cursor)?\b/i,
1078
+ // e.g., after, after_cursor
1079
+ /\bbefore(?:_?cursor)?\b/i,
1080
+ // e.g., before, before_cursor
1081
+ /\b(next|prev|previous)_?(?:page_?)?token\b/i,
1082
+ // e.g., next_page_token, nextPageToken, prev_token
1083
+ /\b(next|prev|previous)_?cursor\b/i,
1084
+ // e.g., next_cursor, previousCursor
1085
+ /\bcontinuation(?:_?token)?\b/i,
1086
+ // e.g., continuation, continuation_token
1087
+ /\bpage(?:_?(token|id))?\b/i,
1088
+ // e.g., after, after_cursor
1089
+ /\bstart_?(?:key|cursor|token|after)\b/i
1090
+ // e.g., start_key, startCursor, startToken, startAfter
1091
+ ];
1092
+ var CURSOR_LIMIT_REGEXES = [
1093
+ /\blimit\b/i,
1094
+ /\bcount\b/i,
1095
+ /\bsize\b/i,
1096
+ // General size
1097
+ /\bfirst\b/i,
1098
+ // Common in Relay-style cursor pagination (forward pagination)
1099
+ /\blast\b/i,
1100
+ // Common in Relay-style cursor pagination (backward pagination)
1101
+ /\bpage_?size\b/i,
1102
+ // Sometimes page_size is used with cursors
1103
+ /\bnum_?(?:items|records|results)\b/i,
1104
+ // e.g., num_items
1105
+ /\bmax_?items\b/i,
1106
+ /\btake\b/i
1107
+ ];
1108
+ function findParamAndKeyword(queryParams, regexes, excludeParamName) {
1109
+ for (const param of queryParams) {
1110
+ if (param.name === excludeParamName) {
1111
+ continue;
1112
+ }
1113
+ for (const regex of regexes) {
1114
+ const match = param.name.match(regex);
1115
+ if (match) {
1116
+ return { param, keyword: match[0] };
1117
+ }
1118
+ }
1119
+ }
1120
+ return null;
1121
+ }
1122
+ function isOffsetPagination(operation, parameters) {
1123
+ const offsetMatch = findParamAndKeyword(parameters, OFFSET_PARAM_REGEXES);
1124
+ if (!offsetMatch)
1125
+ return null;
1126
+ const limitMatch = findParamAndKeyword(
1127
+ parameters,
1128
+ GENERIC_LIMIT_PARAM_REGEXES,
1129
+ offsetMatch.param.name
1130
+ );
1131
+ if (!limitMatch)
1132
+ return null;
1133
+ return {
1134
+ type: "offset",
1135
+ offsetParamName: offsetMatch.param.name,
1136
+ offsetKeyword: offsetMatch.keyword,
1137
+ limitParamName: limitMatch.param.name,
1138
+ limitKeyword: limitMatch.keyword
1139
+ };
1140
+ }
1141
+ function isPagePagination(operation) {
1142
+ const queryParams = operation.parameters.filter((p) => p.in === "query").filter(
1143
+ (it) => it.schema && !isRef4(it.schema) && it.schema.type === "integer"
1144
+ );
1145
+ if (queryParams.length < 2)
1146
+ return null;
1147
+ const pageNoMatch = findParamAndKeyword(queryParams, PAGE_NUMBER_REGEXES);
1148
+ if (!pageNoMatch)
1149
+ return null;
1150
+ const pageSizeMatch = findParamAndKeyword(
1151
+ queryParams,
1152
+ PAGE_SIZE_REGEXES,
1153
+ pageNoMatch.param.name
1154
+ );
1155
+ if (!pageSizeMatch)
1156
+ return null;
1157
+ return {
1158
+ type: "page",
1159
+ pageNumberParamName: pageNoMatch.param.name,
1160
+ pageNumberKeyword: pageNoMatch.keyword,
1161
+ pageSizeParamName: pageSizeMatch.param.name,
1162
+ pageSizeKeyword: pageSizeMatch.keyword
1163
+ };
1164
+ }
1165
+ function isCursorPagination(operation) {
1166
+ const queryParams = operation.parameters.filter((p) => p.in === "query");
1167
+ if (queryParams.length < 2)
1168
+ return null;
1169
+ const cursorMatch = findParamAndKeyword(queryParams, CURSOR_REGEXES);
1170
+ if (!cursorMatch)
1171
+ return null;
1172
+ const limitMatch = findParamAndKeyword(
1173
+ queryParams,
1174
+ CURSOR_LIMIT_REGEXES,
1175
+ cursorMatch.param.name
1176
+ );
1177
+ if (!limitMatch)
1178
+ return null;
1179
+ return {
1180
+ type: "cursor",
1181
+ cursorParamName: cursorMatch.param.name,
1182
+ cursorKeyword: cursorMatch.keyword,
1183
+ limitParamName: limitMatch.param.name,
1184
+ limitKeyword: limitMatch.keyword
1185
+ };
1186
+ }
1187
+ function guessPagination(operation, body, response) {
1188
+ const bodyParameters = body && body.properties ? Object.keys(body.properties).map((it) => ({ name: it })) : [];
1189
+ const parameters = operation.parameters;
1190
+ if (isEmpty3(operation.parameters) && isEmpty3(bodyParameters)) {
1191
+ return { type: "none", reason: "no parameters" };
1192
+ }
1193
+ if (!response) {
1194
+ return { type: "none", reason: "no response" };
1195
+ }
1196
+ if (!response.properties) {
1197
+ return { type: "none", reason: "empty response" };
1198
+ }
1199
+ const properties = response.properties;
1200
+ const itemsKey = getItemsName(properties);
1201
+ if (!itemsKey) {
1202
+ return { type: "none", reason: "no items key" };
1203
+ }
1204
+ const hasMoreKey = getHasMoreName(excludeKey(properties, itemsKey));
1205
+ if (!hasMoreKey) {
1206
+ return { type: "none", reason: "no hasMore key" };
1207
+ }
1208
+ const pagination = isOffsetPagination(operation, [...parameters, ...bodyParameters]) || isPagePagination(operation) || isCursorPagination(operation);
1209
+ return pagination ? { ...pagination, items: itemsKey, hasMore: hasMoreKey } : { type: "none", reason: "no pagination" };
1210
+ }
1211
+ function excludeKey(obj, key) {
1212
+ const { [key]: _, ...rest } = obj;
1213
+ return rest;
1214
+ }
136
1215
 
137
1216
  // packages/spec/dist/lib/operation.js
138
- import { camelcase } from "stringcase";
1217
+ function augmentSpec(config) {
1218
+ config.spec.paths ??= {};
1219
+ const paths = {};
1220
+ for (const [path, pathItem] of Object.entries(config.spec.paths)) {
1221
+ const { parameters = [], ...methods2 } = pathItem;
1222
+ const fixedPath = path.replace(/:([^/]+)/g, "{$1}");
1223
+ for (const [method, operation] of Object.entries(methods2)) {
1224
+ const formatOperationId = config.operationId ?? defaults.operationId;
1225
+ const formatTag = config.tag ?? defaults.tag;
1226
+ const operationId = formatOperationId(operation, fixedPath, method);
1227
+ const operationTag = formatTag(operation, fixedPath);
1228
+ const requestBody = isRef5(operation.requestBody) ? followRef3(config.spec, operation.requestBody.$ref) : operation.requestBody;
1229
+ const tunedOperation = {
1230
+ ...operation,
1231
+ parameters: [...parameters, ...operation.parameters ?? []].map(
1232
+ (it) => isRef5(it) ? followRef3(config.spec, it.$ref) : it
1233
+ ),
1234
+ tags: [operationTag],
1235
+ operationId,
1236
+ responses: resolveResponses(config.spec, operation),
1237
+ requestBody
1238
+ };
1239
+ tunedOperation["x-pagination"] = toPagination(
1240
+ config.spec,
1241
+ tunedOperation
1242
+ );
1243
+ Object.assign(paths, {
1244
+ [fixedPath]: {
1245
+ ...paths[fixedPath],
1246
+ [method]: tunedOperation
1247
+ }
1248
+ });
1249
+ }
1250
+ }
1251
+ return { ...config.spec, paths };
1252
+ }
1253
+ function toPagination(spec, tunedOperation) {
1254
+ if (tunedOperation["x-pagination"]) {
1255
+ return tunedOperation["x-pagination"];
1256
+ }
1257
+ const schema = getResponseContentSchema(
1258
+ spec,
1259
+ tunedOperation.responses["200"],
1260
+ "application/json"
1261
+ );
1262
+ const pagination = guessPagination(
1263
+ tunedOperation,
1264
+ tunedOperation.requestBody ? getRequestContentSchema(
1265
+ spec,
1266
+ tunedOperation.requestBody,
1267
+ "application/json"
1268
+ ) : void 0,
1269
+ schema
1270
+ );
1271
+ if (pagination && pagination.type !== "none" && schema) {
1272
+ return pagination;
1273
+ }
1274
+ return void 0;
1275
+ }
1276
+ function getResponseContentSchema(spec, response, type) {
1277
+ if (!response) {
1278
+ return void 0;
1279
+ }
1280
+ const content = response.content;
1281
+ if (!content) {
1282
+ return void 0;
1283
+ }
1284
+ for (const contentType in content) {
1285
+ if (contentType.toLowerCase() === type.toLowerCase()) {
1286
+ return isRef5(content[contentType].schema) ? followRef3(spec, content[contentType].schema.$ref) : content[contentType].schema;
1287
+ }
1288
+ }
1289
+ return void 0;
1290
+ }
1291
+ function getRequestContentSchema(spec, requestBody, type) {
1292
+ const content = requestBody.content;
1293
+ if (!content) {
1294
+ return void 0;
1295
+ }
1296
+ for (const contentType in content) {
1297
+ if (contentType.toLowerCase() === type.toLowerCase()) {
1298
+ return isRef5(content[contentType].schema) ? followRef3(spec, content[contentType].schema.$ref) : content[contentType].schema;
1299
+ }
1300
+ }
1301
+ return void 0;
1302
+ }
139
1303
  var defaults = {
140
1304
  operationId: (operation, path, method) => {
141
1305
  if (operation.operationId) {
142
- return camelcase(operation.operationId);
1306
+ return camelcase2(operation.operationId);
143
1307
  }
144
1308
  const metadata = operation["x-oaiMeta"];
145
1309
  if (metadata && metadata.name) {
146
- return camelcase(metadata.name);
1310
+ return camelcase2(metadata.name);
147
1311
  }
148
- return camelcase(
1312
+ return camelcase2(
149
1313
  [method, ...path.replace(/[\\/\\{\\}]/g, " ").split(" ")].filter(Boolean).join(" ").trim()
150
1314
  );
151
1315
  },
152
1316
  tag: (operation, path) => {
153
- return operation.tags?.[0] || determineGenericTag(path, operation);
1317
+ return operation.tags?.[0] ? sanitizeTag(operation.tags?.[0]) : determineGenericTag(path, operation);
154
1318
  }
155
1319
  };
156
- function forEachOperation(config, callback) {
1320
+ function resolveResponses(spec, operation) {
1321
+ const responses = operation.responses ?? {};
1322
+ const resolved = {};
1323
+ for (const status in responses) {
1324
+ const response = isRef5(responses[status]) ? followRef3(spec, responses[status].$ref) : responses[status];
1325
+ resolved[status] = response;
1326
+ }
1327
+ return resolved;
1328
+ }
1329
+ function forEachOperation2(config, callback) {
157
1330
  const result = [];
158
1331
  for (const [path, pathItem] of Object.entries(config.spec.paths ?? {})) {
159
1332
  const { parameters = [], ...methods2 } = pathItem;
160
- const fixedPath = path.replace(/:([^/]+)/g, "{$1}");
161
1333
  for (const [method, operation] of Object.entries(methods2)) {
162
- const formatOperationId = config.operationId ?? defaults.operationId;
163
- const formatTag = config.tag ?? defaults.tag;
164
- const operationName = formatOperationId(operation, fixedPath, method);
165
- const operationTag = formatTag(operation, fixedPath);
166
1334
  const metadata = operation["x-oaiMeta"] ?? {};
1335
+ const operationTag = operation.tags?.[0];
167
1336
  result.push(
168
1337
  callback(
169
1338
  {
170
1339
  name: metadata.name,
171
1340
  method,
172
- path: fixedPath,
1341
+ path,
173
1342
  groupName: operationTag,
174
1343
  tag: operationTag
175
1344
  },
176
- {
177
- ...operation,
178
- parameters: [...parameters, ...operation.parameters ?? []],
179
- operationId: operationName
180
- }
1345
+ operation
181
1346
  )
182
1347
  );
183
1348
  }
@@ -185,15 +1350,11 @@ function forEachOperation(config, callback) {
185
1350
  return result;
186
1351
  }
187
1352
  var reservedKeywords = /* @__PURE__ */ new Set([
188
- "abstract",
189
- "arguments",
190
1353
  "await",
191
- "boolean",
1354
+ // Reserved in async functions
192
1355
  "break",
193
- "byte",
194
1356
  "case",
195
1357
  "catch",
196
- "char",
197
1358
  "class",
198
1359
  "const",
199
1360
  "continue",
@@ -201,85 +1362,59 @@ var reservedKeywords = /* @__PURE__ */ new Set([
201
1362
  "default",
202
1363
  "delete",
203
1364
  "do",
204
- "double",
205
1365
  "else",
206
1366
  "enum",
207
- "eval",
208
1367
  "export",
209
1368
  "extends",
210
1369
  "false",
211
- "final",
212
1370
  "finally",
213
- "float",
214
1371
  "for",
215
1372
  "function",
216
- "goto",
217
1373
  "if",
218
1374
  "implements",
1375
+ // Strict mode
219
1376
  "import",
220
1377
  "in",
221
1378
  "instanceof",
222
- "int",
223
1379
  "interface",
1380
+ // Strict mode
224
1381
  "let",
225
- "long",
226
- "native",
1382
+ // Strict mode
227
1383
  "new",
228
1384
  "null",
229
1385
  "package",
1386
+ // Strict mode
230
1387
  "private",
1388
+ // Strict mode
231
1389
  "protected",
1390
+ // Strict mode
232
1391
  "public",
1392
+ // Strict mode
233
1393
  "return",
234
- "short",
235
1394
  "static",
1395
+ // Strict mode
236
1396
  "super",
237
1397
  "switch",
238
- "synchronized",
239
1398
  "this",
240
1399
  "throw",
241
- "throws",
242
- "transient",
243
1400
  "true",
244
1401
  "try",
245
1402
  "typeof",
246
1403
  "var",
247
1404
  "void",
248
- "volatile",
249
1405
  "while",
250
1406
  "with",
251
1407
  "yield",
252
- // Potentially problematic identifiers / Common Verbs used as tags
253
- "object",
254
- "string",
255
- "number",
256
- "any",
257
- "unknown",
258
- "never",
259
- "get",
260
- "list",
261
- "create",
262
- "update",
263
- "delete",
264
- "post",
265
- "put",
266
- "patch",
267
- "do",
268
- "send",
269
- "add",
270
- "remove",
271
- "set",
272
- "find",
273
- "search",
274
- "check",
275
- "make"
276
- // Added make, check
1408
+ // Strict mode / Generator functions
1409
+ // 'arguments' is not technically a reserved word, but it's a special identifier within functions
1410
+ // and assigning to it or declaring it can cause issues or unexpected behavior.
1411
+ "arguments"
277
1412
  ]);
278
1413
  function sanitizeTag(camelCasedTag) {
279
1414
  if (/^\d/.test(camelCasedTag)) {
280
1415
  return `_${camelCasedTag}`;
281
1416
  }
282
- return reservedKeywords.has(camelCasedTag) ? `${camelCasedTag}_` : camelCasedTag;
1417
+ return reservedKeywords.has(camelcase2(camelCasedTag)) ? `${camelCasedTag}_` : camelCasedTag;
283
1418
  }
284
1419
  function determineGenericTag(pathString, operation) {
285
1420
  const operationId = operation.operationId || "";
@@ -303,7 +1438,6 @@ function determineGenericTag(pathString, operation) {
303
1438
  "search",
304
1439
  "check",
305
1440
  "make"
306
- // Added make
307
1441
  ]);
308
1442
  const segments = pathString.split("/").filter(Boolean);
309
1443
  const potentialCandidates = segments.filter(
@@ -312,7 +1446,7 @@ function determineGenericTag(pathString, operation) {
312
1446
  for (let i = potentialCandidates.length - 1; i >= 0; i--) {
313
1447
  const segment = potentialCandidates[i];
314
1448
  if (!segment.startsWith("@")) {
315
- return sanitizeTag(camelcase(segment));
1449
+ return sanitizeTag(camelcase2(segment));
316
1450
  }
317
1451
  }
318
1452
  const canFallbackToPathSegment = potentialCandidates.length > 0;
@@ -347,17 +1481,17 @@ function determineGenericTag(pathString, operation) {
347
1481
  }
348
1482
  if (nextPartStartIndex !== -1 && nextPartStartIndex < operationId.length) {
349
1483
  const remainingOriginalSubstring = operationId.substring(nextPartStartIndex);
350
- const potentialTag = camelcase(remainingOriginalSubstring);
1484
+ const potentialTag = camelcase2(remainingOriginalSubstring);
351
1485
  if (potentialTag) {
352
1486
  return sanitizeTag(potentialTag);
353
1487
  }
354
1488
  }
355
- const potentialTagJoined = camelcase(validParts.slice(1).join("_"));
1489
+ const potentialTagJoined = camelcase2(validParts.slice(1).join("_"));
356
1490
  if (potentialTagJoined) {
357
1491
  return sanitizeTag(potentialTagJoined);
358
1492
  }
359
1493
  }
360
- const potentialTagFull = camelcase(operationId);
1494
+ const potentialTagFull = camelcase2(operationId);
361
1495
  if (potentialTagFull) {
362
1496
  const isResultSingleVerb = validParts.length === 1 && isFirstPartVerb;
363
1497
  if (!(isResultSingleVerb && canFallbackToPathSegment)) {
@@ -366,7 +1500,7 @@ function determineGenericTag(pathString, operation) {
366
1500
  }
367
1501
  }
368
1502
  }
369
- const firstPartCamel = camelcase(firstPart);
1503
+ const firstPartCamel = camelcase2(firstPart);
370
1504
  if (firstPartCamel) {
371
1505
  const isFirstPartCamelVerb = commonVerbs.has(firstPartCamel);
372
1506
  if (!isFirstPartCamelVerb || validParts.length === 1 || !canFallbackToPathSegment) {
@@ -374,32 +1508,220 @@ function determineGenericTag(pathString, operation) {
374
1508
  }
375
1509
  }
376
1510
  if (isFirstPartVerb && validParts.length > 1 && validParts[1] && canFallbackToPathSegment) {
377
- const secondPartCamel = camelcase(validParts[1]);
1511
+ const secondPartCamel = camelcase2(validParts[1]);
378
1512
  if (secondPartCamel) {
379
1513
  return sanitizeTag(secondPartCamel);
380
1514
  }
381
1515
  }
382
1516
  }
383
1517
  }
384
- if (potentialCandidates.length > 0) {
385
- let firstCandidate = potentialCandidates[0];
386
- if (firstCandidate.startsWith("@")) {
387
- firstCandidate = firstCandidate.substring(1);
1518
+ if (potentialCandidates.length > 0) {
1519
+ let firstCandidate = potentialCandidates[0];
1520
+ if (firstCandidate.startsWith("@")) {
1521
+ firstCandidate = firstCandidate.substring(1);
1522
+ }
1523
+ if (firstCandidate) {
1524
+ return sanitizeTag(camelcase2(firstCandidate));
1525
+ }
1526
+ }
1527
+ console.warn(
1528
+ `Could not determine a suitable tag for path: ${pathString}, operationId: ${operationId}. Using 'unknown'.`
1529
+ );
1530
+ return "unknown";
1531
+ }
1532
+ function patchParameters(spec, objectSchema, operation) {
1533
+ const securitySchemes = spec.components?.securitySchemes ?? {};
1534
+ const securityOptions = securityToOptions(
1535
+ spec,
1536
+ operation.security ?? [],
1537
+ securitySchemes
1538
+ );
1539
+ objectSchema.properties ??= {};
1540
+ objectSchema.required ??= [];
1541
+ for (const param of operation.parameters) {
1542
+ if (param.required) {
1543
+ objectSchema.required.push(param.name);
1544
+ }
1545
+ objectSchema.properties[param.name] = isRef5(param.schema) ? followRef3(spec, param.schema.$ref) : param.schema ?? { type: "string" };
1546
+ }
1547
+ for (const param of securityOptions) {
1548
+ objectSchema.required = (objectSchema.required ?? []).filter(
1549
+ (name) => name !== param.name
1550
+ );
1551
+ objectSchema.properties[param.name] = isRef5(param.schema) ? followRef3(spec, param.schema.$ref) : param.schema ?? { type: "string" };
1552
+ }
1553
+ }
1554
+ function securityToOptions(spec, security2, securitySchemes, staticIn) {
1555
+ securitySchemes ??= {};
1556
+ const parameters = [];
1557
+ for (const it of security2) {
1558
+ const [name] = Object.keys(it);
1559
+ if (!name) {
1560
+ continue;
1561
+ }
1562
+ const schema = isRef5(securitySchemes[name]) ? followRef3(spec, securitySchemes[name].$ref) : securitySchemes[name];
1563
+ if (schema.type === "http") {
1564
+ parameters.push({
1565
+ in: staticIn ?? "header",
1566
+ name: "authorization",
1567
+ schema: { type: "string" }
1568
+ });
1569
+ continue;
1570
+ }
1571
+ if (schema.type === "apiKey") {
1572
+ if (!schema.in) {
1573
+ throw new Error(`apiKey security schema must have an "in" field`);
1574
+ }
1575
+ if (!schema.name) {
1576
+ throw new Error(`apiKey security schema must have a "name" field`);
1577
+ }
1578
+ parameters.push({
1579
+ in: staticIn ?? schema.in,
1580
+ name: schema.name,
1581
+ schema: { type: "string" }
1582
+ });
1583
+ continue;
1584
+ }
1585
+ }
1586
+ return parameters;
1587
+ }
1588
+
1589
+ // packages/typescript/src/lib/client.ts
1590
+ import { toLitObject } from "@sdk-it/core";
1591
+ var client_default = (spec, style) => {
1592
+ const optionsEntries = Object.entries(spec.options).map(
1593
+ ([key, value]) => [`'${key}'`, value]
1594
+ );
1595
+ const defaultHeaders = `{${optionsEntries.filter(([, value]) => value.in === "header").map(
1596
+ ([key, value]) => `${key}: this.options[${value.optionName ? `'${value.optionName}'` : key}]`
1597
+ ).join(",\n")}}`;
1598
+ const defaultInputs = `{${optionsEntries.filter(([, value]) => value.in === "input").map(
1599
+ ([key, value]) => `${key}: this.options[${value.optionName ? `'${value.optionName}'` : key}]`
1600
+ ).join(",\n")}}`;
1601
+ const specOptions = {
1602
+ ...Object.fromEntries(
1603
+ optionsEntries.map(([key, value]) => [value.optionName ?? key, value])
1604
+ ),
1605
+ fetch: {
1606
+ schema: "fetchType"
1607
+ },
1608
+ baseUrl: {
1609
+ schema: spec.servers.length ? `z.enum(servers).default(servers[0])` : "z.string()"
1610
+ }
1611
+ };
1612
+ return `import z from 'zod';
1613
+ import type { HeadersInit, RequestConfig } from './http/${spec.makeImport("request")}';
1614
+ import { fetchType, parse } from './http/${spec.makeImport("dispatcher")}';
1615
+ import schemas from './api/${spec.makeImport("schemas")}';
1616
+ import {
1617
+ createBaseUrlInterceptor,
1618
+ createHeadersInterceptor,
1619
+ } from './http/${spec.makeImport("interceptors")}';
1620
+
1621
+ import { parseInput, type ParseError } from './http/${spec.makeImport("parser")}';
1622
+
1623
+ ${spec.servers.length ? `export const servers = ${JSON.stringify(spec.servers, null, 2)} as const` : ""}
1624
+ const optionsSchema = z.object(${toLitObject(specOptions, (x) => x.schema)});
1625
+ ${spec.servers.length ? `export type Servers = typeof servers[number];` : ""}
1626
+
1627
+ type ${spec.name}Options = z.infer<typeof optionsSchema>;
1628
+
1629
+ export class ${spec.name} {
1630
+ public options: ${spec.name}Options
1631
+ constructor(options: ${spec.name}Options) {
1632
+ this.options = optionsSchema.parse(options);
1633
+ }
1634
+
1635
+ async request<const E extends keyof typeof schemas>(
1636
+ endpoint: E,
1637
+ input: z.infer<(typeof schemas)[E]['schema']>,
1638
+ options?: { signal?: AbortSignal, headers?: HeadersInit },
1639
+ ) ${style.errorAsValue ? `: Promise<Awaited<ReturnType<(typeof schemas)[E]['dispatch']>>| [never, ParseError<(typeof schemas)[E]['schema']>]>` : `: Promise<Awaited<ReturnType<(typeof schemas)[E]['dispatch']>>>`} {
1640
+ const route = schemas[endpoint];
1641
+ const withDefaultInputs = Object.assign({}, this.#defaultInputs, input);
1642
+ const [parsedInput, parseError] = parseInput(route.schema, withDefaultInputs);
1643
+ if (parseError) {
1644
+ ${style.errorAsValue ? "return [null as never, parseError as never] as const;" : "throw parseError;"}
1645
+ }
1646
+ const result = await route.dispatch(parsedInput as never, {
1647
+ fetch: this.options.fetch,
1648
+ interceptors: [
1649
+ createHeadersInterceptor(() => this.defaultHeaders, options?.headers ?? {}),
1650
+ createBaseUrlInterceptor(() => this.options.baseUrl),
1651
+ ],
1652
+ signal: options?.signal,
1653
+ });
1654
+ return result as Awaited<ReturnType<(typeof schemas)[E]['dispatch']>>;
1655
+ }
1656
+
1657
+ async prepare<const E extends keyof typeof schemas>(
1658
+ endpoint: E,
1659
+ input: z.infer<(typeof schemas)[E]['schema']>,
1660
+ options?: { headers?: HeadersInit },
1661
+ ): ${style.errorAsValue ? `Promise<
1662
+ readonly [
1663
+ RequestConfig & {
1664
+ parse: (response: Response) => ReturnType<typeof parse>;
1665
+ },
1666
+ ParseError<(typeof schemas)[E]['schema']> | null,
1667
+ ]
1668
+ >` : `Promise<RequestConfig & {
1669
+ parse: (response: Response) => ReturnType<typeof parse>;
1670
+ }>`} {
1671
+ const route = schemas[endpoint];
1672
+
1673
+ const interceptors = [
1674
+ createHeadersInterceptor(
1675
+ () => this.defaultHeaders,
1676
+ options?.headers ?? {},
1677
+ ),
1678
+ createBaseUrlInterceptor(() => this.options.baseUrl),
1679
+ ];
1680
+ const [parsedInput, parseError] = parseInput(route.schema, input);
1681
+ if (parseError) {
1682
+ ${style.errorAsValue ? "return [null as never, parseError as never] as const;" : "throw parseError;"}
388
1683
  }
389
- if (firstCandidate) {
390
- return sanitizeTag(camelcase(firstCandidate));
1684
+
1685
+ let config = route.toRequest(parsedInput as never);
1686
+ for (const interceptor of interceptors) {
1687
+ if (interceptor.before) {
1688
+ config = await interceptor.before(config);
1689
+ }
391
1690
  }
1691
+ const prepared = { ...config, parse: (response: Response) => parse(route.output, response) as never };
1692
+ return ${style.errorAsValue ? "[prepared, null as never] as const;" : "prepared as any"}
392
1693
  }
393
- console.warn(
394
- `Could not determine a suitable tag for path: ${pathString}, operationId: ${operationId}. Using 'unknown'.`
395
- );
396
- return "unknown";
397
- }
1694
+
1695
+ get defaultHeaders() {
1696
+ return ${defaultHeaders}
1697
+ }
1698
+
1699
+ get #defaultInputs() {
1700
+ return ${defaultInputs}
1701
+ }
1702
+
1703
+ setOptions(options: Partial<${spec.name}Options>) {
1704
+ const validated = optionsSchema.partial().parse(options);
1705
+
1706
+ for (const key of Object.keys(validated) as (keyof ${spec.name}Options)[]) {
1707
+ if (validated[key] !== undefined) {
1708
+ (this.options[key] as typeof validated[typeof key]) = validated[key]!;
1709
+ }
1710
+ }
1711
+ }
1712
+ }`;
1713
+ };
1714
+
1715
+ // packages/typescript/src/lib/generator.ts
1716
+ import { merge, template } from "lodash-es";
1717
+ import { join } from "node:path";
1718
+ import { camelcase as camelcase4, pascalcase as pascalcase2, spinalcase as spinalcase2 } from "stringcase";
1719
+ import { followRef as followRef7, isEmpty as isEmpty4, isRef as isRef10 } from "@sdk-it/core";
398
1720
 
399
1721
  // packages/typescript/src/lib/emitters/zod.ts
400
- import { cleanRef, followRef, isRef, parseRef } from "@sdk-it/core";
401
- var ZodDeserialzer = class {
402
- circularRefTracker = /* @__PURE__ */ new Set();
1722
+ import { cleanRef, followRef as followRef4, isRef as isRef6 } from "@sdk-it/core";
1723
+ var ZodEmitter = class {
1724
+ generatedRefs = /* @__PURE__ */ new Set();
403
1725
  #spec;
404
1726
  #onRef;
405
1727
  constructor(spec, onRef) {
@@ -473,15 +1795,14 @@ var ZodDeserialzer = class {
473
1795
  }
474
1796
  ref($ref, required) {
475
1797
  const schemaName = cleanRef($ref).split("/").pop();
476
- if (this.circularRefTracker.has(schemaName)) {
1798
+ if (this.generatedRefs.has(schemaName)) {
477
1799
  return schemaName;
478
1800
  }
479
- this.circularRefTracker.add(schemaName);
1801
+ this.generatedRefs.add(schemaName);
480
1802
  this.#onRef?.(
481
1803
  schemaName,
482
- this.handle(followRef(this.#spec, $ref), required)
1804
+ this.handle(followRef4(this.#spec, $ref), required)
483
1805
  );
484
- this.circularRefTracker.delete(schemaName);
485
1806
  return schemaName;
486
1807
  }
487
1808
  allOf(schemas, required) {
@@ -509,15 +1830,7 @@ var ZodDeserialzer = class {
509
1830
  return `z.union([${anyOfSchemas.join(", ")}])${appendOptional(required)}`;
510
1831
  }
511
1832
  oneOf(schemas, required) {
512
- const oneOfSchemas = schemas.map((sub) => {
513
- if (isRef(sub)) {
514
- const { model } = parseRef(sub.$ref);
515
- if (this.circularRefTracker.has(model)) {
516
- return `${model}${appendOptional(required)}`;
517
- }
518
- }
519
- return this.handle(sub, true);
520
- });
1833
+ const oneOfSchemas = schemas.map((sub) => this.handle(sub, true));
521
1834
  if (oneOfSchemas.length === 1) {
522
1835
  return `${oneOfSchemas[0]}${appendOptional(required)}`;
523
1836
  }
@@ -537,6 +1850,10 @@ var ZodDeserialzer = class {
537
1850
  */
538
1851
  string(schema) {
539
1852
  let base = "z.string()";
1853
+ if (schema.contentEncoding === "binary") {
1854
+ base = "z.instanceof(Blob)";
1855
+ return base;
1856
+ }
540
1857
  switch (schema.format) {
541
1858
  case "date-time":
542
1859
  case "datetime":
@@ -614,7 +1931,7 @@ var ZodDeserialzer = class {
614
1931
  return { base, defaultValue };
615
1932
  }
616
1933
  handle(schema, required) {
617
- if (isRef(schema)) {
1934
+ if (isRef6(schema)) {
618
1935
  return `${this.ref(schema.$ref, true)}${appendOptional(required)}`;
619
1936
  }
620
1937
  if (schema.allOf && Array.isArray(schema.allOf)) {
@@ -660,13 +1977,13 @@ function appendDefault(defaultValue) {
660
1977
 
661
1978
  // packages/typescript/src/lib/sdk.ts
662
1979
  import { get } from "lodash-es";
663
- import { camelcase as camelcase2, pascalcase, spinalcase } from "stringcase";
664
- import { followRef as followRef3, isRef as isRef4, toLitObject as toLitObject2 } from "@sdk-it/core";
1980
+ import { camelcase as camelcase3, pascalcase, spinalcase } from "stringcase";
1981
+ import { isRef as isRef9, toLitObject as toLitObject2 } from "@sdk-it/core";
665
1982
 
666
1983
  // packages/typescript/src/lib/emitters/interface.ts
667
- import { cleanRef as cleanRef2, followRef as followRef2, isRef as isRef2, parseRef as parseRef2 } from "@sdk-it/core";
668
- var TypeScriptDeserialzer = class {
669
- circularRefTracker = /* @__PURE__ */ new Set();
1984
+ import { cleanRef as cleanRef2, followRef as followRef5, isRef as isRef7 } from "@sdk-it/core";
1985
+ var TypeScriptEmitter = class {
1986
+ generatedRefs = /* @__PURE__ */ new Set();
670
1987
  #spec;
671
1988
  #onRef;
672
1989
  constructor(spec, onRef) {
@@ -677,7 +1994,7 @@ var TypeScriptDeserialzer = class {
677
1994
  return `'${value}'`;
678
1995
  };
679
1996
  #isInternal = (schema) => {
680
- return isRef2(schema) ? false : !!schema["x-internal"];
1997
+ return isRef7(schema) ? false : !!schema["x-internal"];
681
1998
  };
682
1999
  /**
683
2000
  * Handle objects (properties)
@@ -697,7 +2014,7 @@ var TypeScriptDeserialzer = class {
697
2014
  propEntries.push("[key: string]: any");
698
2015
  }
699
2016
  }
700
- return `{ ${propEntries.join("; ")} }`;
2017
+ return `${propEntries.length ? `{ ${propEntries.join("; ")} }` : "unknown"}`;
701
2018
  }
702
2019
  /**
703
2020
  * Handle arrays (items could be a single schema or a tuple)
@@ -739,15 +2056,14 @@ var TypeScriptDeserialzer = class {
739
2056
  }
740
2057
  ref($ref, required) {
741
2058
  const schemaName = cleanRef2($ref).split("/").pop();
742
- if (this.circularRefTracker.has(schemaName)) {
2059
+ if (this.generatedRefs.has(schemaName)) {
743
2060
  return schemaName;
744
2061
  }
745
- this.circularRefTracker.add(schemaName);
2062
+ this.generatedRefs.add(schemaName);
746
2063
  this.#onRef?.(
747
2064
  schemaName,
748
- this.handle(followRef2(this.#spec, $ref), required)
2065
+ this.handle(followRef5(this.#spec, $ref), required)
749
2066
  );
750
- this.circularRefTracker.delete(schemaName);
751
2067
  return appendOptional2(schemaName, required);
752
2068
  }
753
2069
  allOf(schemas) {
@@ -763,12 +2079,6 @@ var TypeScriptDeserialzer = class {
763
2079
  }
764
2080
  oneOf(schemas, required) {
765
2081
  const oneOfTypes = schemas.map((sub) => {
766
- if (isRef2(sub)) {
767
- const { model } = parseRef2(sub.$ref);
768
- if (this.circularRefTracker.has(model)) {
769
- return model;
770
- }
771
- }
772
2082
  return this.handle(sub, false);
773
2083
  });
774
2084
  return appendOptional2(
@@ -785,6 +2095,9 @@ var TypeScriptDeserialzer = class {
785
2095
  */
786
2096
  string(schema, required) {
787
2097
  let type;
2098
+ if (schema.contentEncoding === "binary") {
2099
+ return appendOptional2("Blob", required);
2100
+ }
788
2101
  switch (schema.format) {
789
2102
  case "date-time":
790
2103
  case "datetime":
@@ -811,7 +2124,7 @@ var TypeScriptDeserialzer = class {
811
2124
  return appendOptional2(type, required);
812
2125
  }
813
2126
  handle(schema, required) {
814
- if (isRef2(schema)) {
2127
+ if (isRef7(schema)) {
815
2128
  return this.ref(schema.$ref, required);
816
2129
  }
817
2130
  if (schema.allOf && Array.isArray(schema.allOf)) {
@@ -856,8 +2169,8 @@ function appendOptional2(type, isRequired) {
856
2169
  }
857
2170
 
858
2171
  // packages/typescript/src/lib/utils.ts
859
- import { isRef as isRef3, removeDuplicates } from "@sdk-it/core";
860
- function securityToOptions(security2, securitySchemes, staticIn) {
2172
+ import { followRef as followRef6, isRef as isRef8, removeDuplicates } from "@sdk-it/core";
2173
+ function securityToOptions2(spec, security2, securitySchemes, staticIn) {
861
2174
  securitySchemes ??= {};
862
2175
  const options = {};
863
2176
  for (const it of security2) {
@@ -865,10 +2178,7 @@ function securityToOptions(security2, securitySchemes, staticIn) {
865
2178
  if (!name) {
866
2179
  continue;
867
2180
  }
868
- const schema = securitySchemes[name];
869
- if (isRef3(schema)) {
870
- throw new Error(`Ref security schemas are not supported`);
871
- }
2181
+ const schema = isRef8(securitySchemes[name]) ? followRef6(spec, securitySchemes[name].$ref) : securitySchemes[name];
872
2182
  if (schema.type === "http") {
873
2183
  options["authorization"] = {
874
2184
  in: staticIn ?? "header",
@@ -954,7 +2264,7 @@ function generateInputs(operationsSet, commonZod, makeImport) {
954
2264
  const output = [];
955
2265
  const imports = /* @__PURE__ */ new Set(['import { z } from "zod";']);
956
2266
  for (const operation of operations) {
957
- const schemaName = camelcase2(`${operation.name} schema`);
2267
+ const schemaName = camelcase3(`${operation.name} schema`);
958
2268
  const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] : toLitObject2(operation.schemas)};`;
959
2269
  const inputContent = schema;
960
2270
  for (const schema2 of commonImports) {
@@ -991,8 +2301,8 @@ function generateInputs(operationsSet, commonZod, makeImport) {
991
2301
  };
992
2302
  }
993
2303
  function toEndpoint(groupName, spec, specOperation, operation, utils) {
994
- const schemaName = camelcase2(`${operation.name} schema`);
995
- const schemaRef = `${camelcase2(groupName)}.${schemaName}`;
2304
+ const schemaName = camelcase3(`${operation.name} schema`);
2305
+ const schemaRef = `${camelcase3(groupName)}.${schemaName}`;
996
2306
  const inputHeaders = [];
997
2307
  const inputQuery = [];
998
2308
  const inputBody = [];
@@ -1024,13 +2334,25 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
1024
2334
  const statusCode = +status;
1025
2335
  return statusCode >= 200 && statusCode < 300;
1026
2336
  }).length > 1;
1027
- for (const status in specOperation.responses) {
1028
- const response = isRef4(specOperation.responses[status]) ? followRef3(spec, specOperation.responses[status].$ref) : specOperation.responses[status];
2337
+ const responseWithAtLeast200 = statusesCount ? specOperation.responses : Object.assign(
2338
+ {
2339
+ "200": {
2340
+ description: "OK",
2341
+ content: {
2342
+ "application/json": {
2343
+ schema: { type: "object" }
2344
+ }
2345
+ }
2346
+ }
2347
+ },
2348
+ specOperation.responses
2349
+ );
2350
+ for (const status in responseWithAtLeast200) {
1029
2351
  const handled = handleResponse(
1030
2352
  spec,
1031
2353
  operation.name,
1032
2354
  status,
1033
- response,
2355
+ responseWithAtLeast200[status],
1034
2356
  utils,
1035
2357
  true
1036
2358
  // statusesCount,
@@ -1050,19 +2372,104 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
1050
2372
  schema: ${schemaRef}${addTypeParser ? `.${type}` : ""},
1051
2373
  output:[${outputs.join(",")}],
1052
2374
  toRequest(input: z.infer<typeof ${schemaRef}${addTypeParser ? `.${type}` : ""}>) {
1053
- const endpoint = '${endpoint}';
1054
- return toRequest(endpoint, ${operation.outgoingContentType || "nobody"}(input, {
1055
- inputHeaders: [${inputHeaders}],
1056
- inputQuery: [${inputQuery}],
1057
- inputBody: [${inputBody}],
1058
- inputParams: [${inputParams}],
1059
- }));
1060
- },
1061
- }`
2375
+ return toRequest('${endpoint}', ${operation.outgoingContentType || "empty"}(input, {
2376
+ inputHeaders: [${inputHeaders}],
2377
+ inputQuery: [${inputQuery}],
2378
+ inputBody: [${inputBody}],
2379
+ inputParams: [${inputParams}],
2380
+ }));},
2381
+ async dispatch(input: z.infer<typeof ${schemaRef}${addTypeParser ? `.${type}` : ""}>,options: {
2382
+ signal?: AbortSignal;
2383
+ interceptors: Interceptor[];
2384
+ fetch: z.infer<typeof fetchType>;
2385
+ })${specOperation["x-pagination"] ? paginationOperation(specOperation, utils.style) : normalOperation(utils.style)}`
1062
2386
  );
1063
2387
  }
1064
2388
  return { responses, schemas };
1065
2389
  }
2390
+ function normalOperation(style) {
2391
+ return `{
2392
+ const dispatcher = new Dispatcher(options.interceptors, options.fetch);
2393
+ const result = await dispatcher.send(this.toRequest(input), this.output);
2394
+ return ${style?.outputType === "status" ? "result" : style?.errorAsValue ? `result` : "result.data;"}
2395
+ },
2396
+ }`;
2397
+ }
2398
+ function paginationOperation(operation, style) {
2399
+ const pagination = operation["x-pagination"];
2400
+ const data = `${style?.errorAsValue ? `result[0]${style.outputType === "status" ? "" : ""}` : `${style?.outputType === "default" ? "result.data" : "result.data"}`}`;
2401
+ if (pagination.type === "offset") {
2402
+ const sameInputNames = pagination.limitParamName === "limit" && pagination.offsetParamName === "offset";
2403
+ const initialParams = sameInputNames ? "input" : `{...input, limit: input.${pagination.limitParamName}, offset: input.${pagination.offsetParamName}}`;
2404
+ const nextPageParams = sameInputNames ? "...nextPageParams" : `${pagination.offsetParamName}: nextPageParams.offset, ${pagination.limitParamName}: nextPageParams.limit`;
2405
+ const logic = `const pagination = new OffsetPagination(${initialParams}, async (nextPageParams) => {
2406
+ const dispatcher = new Dispatcher(options.interceptors, options.fetch);
2407
+ const result = await dispatcher.send(
2408
+ this.toRequest({...input, ${nextPageParams}}),
2409
+ this.output,
2410
+ );
2411
+ return {
2412
+ data: ${data}.${pagination.items},
2413
+ meta: {
2414
+ hasMore: Boolean(${data}.${pagination.hasMore}),
2415
+ },
2416
+ };
2417
+ });
2418
+ await pagination.getNextPage();
2419
+ return ${style?.outputType === "status" ? "new http.Ok(pagination);" : "pagination"}
2420
+ `;
2421
+ return style?.errorAsValue ? `{try {${logic}} catch (error) {return [null as never, error] as const;}}}` : `{${logic}}}`;
2422
+ }
2423
+ if (pagination.type === "cursor") {
2424
+ const sameInputNames = pagination.cursorParamName === "cursor";
2425
+ const initialParams = sameInputNames ? "input" : `{...input, cursor: input.${pagination.cursorParamName}}`;
2426
+ const nextPageParams = sameInputNames ? "...nextPageParams" : `${pagination.cursorParamName}: nextPageParams.cursor`;
2427
+ const logic = `
2428
+ const pagination = new CursorPagination(${initialParams}, async (nextPageParams) => {
2429
+ const dispatcher = new Dispatcher(options.interceptors, options.fetch);
2430
+ const result = await dispatcher.send(
2431
+ this.toRequest({...input, ${nextPageParams}}),
2432
+ this.output,
2433
+ );
2434
+ ${style?.errorAsValue ? `if (result[1]) {throw result[1];}` : ""}
2435
+ return {
2436
+ data: ${data}.${pagination.items},
2437
+ meta: {
2438
+ hasMore: Boolean(${data}.${pagination.hasMore}),
2439
+ },
2440
+ };
2441
+ });
2442
+ await pagination.getNextPage();
2443
+ return ${style?.outputType === "status" ? "new http.Ok(pagination);" : "pagination"}
2444
+ `;
2445
+ return style?.errorAsValue ? `{try {${logic}} catch (error) {return [null as never, error] as const;}}}` : `{${logic}}}`;
2446
+ }
2447
+ if (pagination.type === "page") {
2448
+ const sameInputNames = pagination.pageNumberParamName === "page" && pagination.pageSizeParamName === "pageSize";
2449
+ const initialParams = sameInputNames ? "input" : `{...input, page: input.${pagination.pageNumberParamName}, pageSize: input.${pagination.pageSizeParamName}}`;
2450
+ const nextPageParams = sameInputNames ? "...nextPageParams" : `${pagination.pageNumberParamName}: nextPageParams.page, ${pagination.pageSizeParamName}: nextPageParams.pageSize`;
2451
+ const logic = `
2452
+ const pagination = new Pagination(${initialParams}, async (nextPageParams) => {
2453
+ const dispatcher = new Dispatcher(options.interceptors, options.fetch);
2454
+ const result = await dispatcher.send(
2455
+ this.toRequest({...input, ${nextPageParams}}),
2456
+ this.output,
2457
+ );
2458
+ ${style?.errorAsValue ? `if (result[1]) {throw result[1];}` : ""}
2459
+ return {
2460
+ data: ${data}.${pagination.items},
2461
+ meta: {
2462
+ hasMore: Boolean(${data}.${pagination.hasMore}),
2463
+ },
2464
+ };
2465
+ });
2466
+ await pagination.getNextPage();
2467
+ return ${style?.outputType === "status" ? "new http.Ok(pagination);" : "pagination"}
2468
+ `;
2469
+ return style?.errorAsValue ? `{try {${logic}} catch (error) {return [null as never, error] as const;}}}` : `{${logic}}}`;
2470
+ }
2471
+ return normalOperation(style);
2472
+ }
1066
2473
  var statusCodeToResponseMap = {
1067
2474
  "200": "Ok",
1068
2475
  "201": "Created",
@@ -1100,7 +2507,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1100
2507
  };
1101
2508
  const responses = [];
1102
2509
  const outputs = [];
1103
- const typeScriptDeserialzer = new TypeScriptDeserialzer(
2510
+ const typeScriptDeserialzer = new TypeScriptEmitter(
1104
2511
  spec,
1105
2512
  (schemaName, zod) => {
1106
2513
  schemas[schemaName] = zod;
@@ -1135,7 +2542,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1135
2542
  let responseSchema = parser === "chunked" ? "ReadableStream" : "void";
1136
2543
  if (isJson) {
1137
2544
  const schema = responseContent["application/json"].schema;
1138
- const isObject = !isRef4(schema) && schema.type === "object";
2545
+ const isObject = !isRef9(schema) && schema.type === "object";
1139
2546
  if (isObject && schema.properties) {
1140
2547
  schema.properties["[http.KIND]"] = {
1141
2548
  "x-internal": true,
@@ -1143,7 +2550,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1143
2550
  type: "string"
1144
2551
  };
1145
2552
  schema.required ??= [];
1146
- schema.required.push("[KIND]");
2553
+ schema.required.push("[http.KIND]");
1147
2554
  }
1148
2555
  responseSchema = typeScriptDeserialzer.handle(schema, true);
1149
2556
  }
@@ -1176,13 +2583,13 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1176
2583
  }
1177
2584
 
1178
2585
  // packages/typescript/src/lib/styles/github/endpoints.txt
1179
- var endpoints_default = "type Output<T extends OutputType> = T extends {\n parser: Parser;\n type: Type<unknown>;\n}\n ? InstanceType<T['type']>\n : T extends Type<unknown>\n ? InstanceType<T>\n : never;\n\ntype Unionize<T> = T extends [infer Single extends OutputType]\n ? Output<Single>\n : T extends readonly [...infer Tuple extends OutputType[]]\n ? { [I in keyof Tuple]: Output<Tuple[I]> }[number]\n : never;\n\ntype EndpointOutput<K extends keyof typeof schemas> = Extract<\n Unionize<(typeof schemas)[K]['output']>,\n SuccessfulResponse\n>;\n\ntype EndpointError<K extends keyof typeof schemas> = Extract<\n Unionize<(typeof schemas)[K]['output']>,\n ProblematicResponse\n>;\n\nexport type Endpoints = {\n [K in keyof typeof schemas]: {\n input: z.infer<(typeof schemas)[K]['schema']>;\n output: <% if (outputType === 'default') { %>EndpointOutput<K>['data']<% } else { %>EndpointOutput<K><% } %>;\n error: EndpointError<K> | ParseError<(typeof schemas)[K]['schema']>;\n };\n};";
2586
+ var endpoints_default = "type EndpointOutput<K extends keyof typeof schemas> = Extract<\n Unionize<(typeof schemas)[K]['output']>,\n SuccessfulResponse\n>;\n\ntype EndpointError<K extends keyof typeof schemas> = Extract<\n Unionize<(typeof schemas)[K]['output']>,\n ProblematicResponse\n>;\n\nexport type Endpoints = {\n [K in keyof typeof schemas]: {\n input: z.infer<(typeof schemas)[K]['schema']>;\n output: <% if (outputType === 'default') { %>EndpointOutput<K>['data']<% } else { %>EndpointOutput<K><% } %>;\n error: EndpointError<K> | ParseError<(typeof schemas)[K]['schema']>;\n };\n};";
1180
2587
 
1181
2588
  // packages/typescript/src/lib/generator.ts
1182
2589
  function generateCode(config) {
1183
2590
  const commonZod = /* @__PURE__ */ new Map();
1184
2591
  const commonZodImports = [];
1185
- const zodDeserialzer = new ZodDeserialzer(config.spec, (model, schema) => {
2592
+ const zodDeserialzer = new ZodEmitter(config.spec, (model, schema) => {
1186
2593
  commonZod.set(model, schema);
1187
2594
  commonZodImports.push({
1188
2595
  defaultImport: void 0,
@@ -1195,41 +2602,41 @@ function generateCode(config) {
1195
2602
  const groups = {};
1196
2603
  const outputs = {};
1197
2604
  const endpoints = {};
1198
- forEachOperation(config, (entry, operation) => {
2605
+ forEachOperation2(config, (entry, operation) => {
1199
2606
  console.log(`Processing ${entry.method} ${entry.path}`);
1200
2607
  groups[entry.groupName] ??= [];
1201
2608
  endpoints[entry.groupName] ??= [];
1202
2609
  const inputs = {};
1203
- const additionalProperties = [];
1204
- for (const param of operation.parameters ?? []) {
1205
- if (isRef5(param)) {
1206
- throw new Error(`Found reference in parameter ${param.$ref}`);
1207
- }
2610
+ const additionalProperties = {};
2611
+ for (const param of operation.parameters) {
1208
2612
  if (!param.schema) {
1209
- throw new Error(`Schema not found for parameter ${param.name}`);
2613
+ param.schema = {
2614
+ type: "string"
2615
+ };
1210
2616
  }
1211
2617
  inputs[param.name] = {
1212
2618
  in: param.in,
1213
2619
  schema: ""
1214
2620
  };
1215
- additionalProperties.push(param);
2621
+ additionalProperties[param.name] = param;
1216
2622
  }
1217
- const security2 = operation.security ?? [];
1218
2623
  const securitySchemes = config.spec.components?.securitySchemes ?? {};
1219
- const securityOptions = securityToOptions(security2, securitySchemes);
1220
- Object.assign(inputs, securityOptions);
1221
- additionalProperties.push(
1222
- ...Object.entries(securityOptions).map(
1223
- ([name, value]) => ({
1224
- name,
1225
- required: false,
1226
- schema: {
1227
- type: "string"
1228
- },
1229
- in: value.in
1230
- })
1231
- )
2624
+ const securityOptions = securityToOptions2(
2625
+ config.spec,
2626
+ operation.security ?? [],
2627
+ securitySchemes
1232
2628
  );
2629
+ Object.assign(inputs, securityOptions);
2630
+ Object.entries(securityOptions).forEach(([name, value]) => {
2631
+ additionalProperties[name] = {
2632
+ name,
2633
+ required: false,
2634
+ schema: {
2635
+ type: "string"
2636
+ },
2637
+ in: value.in
2638
+ };
2639
+ });
1233
2640
  const schemas = {};
1234
2641
  const shortContenTypeMap = {
1235
2642
  "application/json": "json",
@@ -1243,10 +2650,12 @@ function generateCode(config) {
1243
2650
  "text/plain": "text"
1244
2651
  };
1245
2652
  let outgoingContentType;
1246
- if (!isEmpty(operation.requestBody)) {
1247
- const requestBody = isRef5(operation.requestBody) ? followRef4(config.spec, operation.requestBody.$ref) : operation.requestBody;
1248
- for (const type in requestBody.content) {
1249
- const ctSchema = isRef5(requestBody.content[type].schema) ? followRef4(config.spec, requestBody.content[type].schema.$ref) : requestBody.content[type].schema;
2653
+ if (!isEmpty4(operation.requestBody)) {
2654
+ for (const type in operation.requestBody.content) {
2655
+ const ctSchema = isRef10(operation.requestBody.content[type].schema) ? followRef7(
2656
+ config.spec,
2657
+ operation.requestBody.content[type].schema.$ref
2658
+ ) : operation.requestBody.content[type].schema;
1250
2659
  if (!ctSchema) {
1251
2660
  console.warn(
1252
2661
  `Schema not found for ${type} in ${entry.method} ${entry.path}`
@@ -1257,16 +2666,16 @@ function generateCode(config) {
1257
2666
  if (objectSchema.type !== "object") {
1258
2667
  objectSchema = {
1259
2668
  type: "object",
1260
- required: [requestBody.required ? "$body" : ""],
2669
+ required: [operation.requestBody.required ? "$body" : ""],
1261
2670
  properties: {
1262
2671
  $body: ctSchema
1263
2672
  }
1264
2673
  };
1265
2674
  }
1266
2675
  const schema = merge({}, objectSchema, {
1267
- required: additionalProperties.filter((p) => p.required).map((p) => p.name),
1268
- properties: additionalProperties.reduce(
1269
- (acc, p) => ({
2676
+ required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
2677
+ properties: Object.entries(additionalProperties).reduce(
2678
+ (acc, [, p]) => ({
1270
2679
  ...acc,
1271
2680
  [p.name]: p.schema
1272
2681
  }),
@@ -1276,18 +2685,18 @@ function generateCode(config) {
1276
2685
  Object.assign(inputs, bodyInputs(config, objectSchema));
1277
2686
  schemas[shortContenTypeMap[type]] = zodDeserialzer.handle(schema, true);
1278
2687
  }
1279
- if (requestBody.content["application/json"]) {
2688
+ if (operation.requestBody.content["application/json"]) {
1280
2689
  outgoingContentType = "json";
1281
- } else if (requestBody.content["application/x-www-form-urlencoded"]) {
2690
+ } else if (operation.requestBody.content["application/x-www-form-urlencoded"]) {
1282
2691
  outgoingContentType = "urlencoded";
1283
- } else if (requestBody.content["multipart/form-data"]) {
2692
+ } else if (operation.requestBody.content["multipart/form-data"]) {
1284
2693
  outgoingContentType = "formdata";
1285
2694
  } else {
1286
2695
  outgoingContentType = "json";
1287
2696
  }
1288
2697
  } else {
1289
- const properties = additionalProperties.reduce(
1290
- (acc, p) => ({
2698
+ const properties = Object.entries(additionalProperties).reduce(
2699
+ (acc, [, p]) => ({
1291
2700
  ...acc,
1292
2701
  [p.name]: p.schema
1293
2702
  }),
@@ -1296,7 +2705,7 @@ function generateCode(config) {
1296
2705
  schemas[shortContenTypeMap["application/json"]] = zodDeserialzer.handle(
1297
2706
  {
1298
2707
  type: "object",
1299
- required: additionalProperties.filter((p) => p.required).map((p) => p.name),
2708
+ required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
1300
2709
  properties
1301
2710
  },
1302
2711
  true
@@ -1314,11 +2723,11 @@ function generateCode(config) {
1314
2723
  schemas,
1315
2724
  inputs
1316
2725
  },
1317
- { makeImport: config.makeImport }
2726
+ { makeImport: config.makeImport, style: config.style }
1318
2727
  );
1319
2728
  const output = [
1320
2729
  `import z from 'zod';`,
1321
- `import type * as http from '../http';`
2730
+ `import type * as http from '${config.makeImport("../http/index")}';`
1322
2731
  ];
1323
2732
  const responses = endpoint.responses.flatMap((it) => it.responses);
1324
2733
  const responsesImports = endpoint.responses.flatMap(
@@ -1368,14 +2777,13 @@ function generateCode(config) {
1368
2777
  {}
1369
2778
  );
1370
2779
  const allSchemas = Object.keys(endpoints).map((it) => ({
1371
- import: `import ${camelcase3(it)} from './${config.makeImport(spinalcase2(it))}';`,
1372
- use: ` ...${camelcase3(it)}`
2780
+ import: `import ${camelcase4(it)} from './${config.makeImport(spinalcase2(it))}';`,
2781
+ use: ` ...${camelcase4(it)}`
1373
2782
  }));
1374
2783
  const imports = [
1375
2784
  'import z from "zod";',
1376
2785
  `import type { ParseError } from '${config.makeImport("../http/parser")}';`,
1377
- `import type { ServerError } from '${config.makeImport("../http/response")}';`,
1378
- `import type { OutputType, Parser, Type } from '../http/send-request.ts';`
2786
+ `import type { ServerError } from '${config.makeImport("../http/response")}';`
1379
2787
  ];
1380
2788
  return {
1381
2789
  groups,
@@ -1391,12 +2799,9 @@ import type { ParseError } from '${config.makeImport("../http/parser")}';
1391
2799
  import type { ProblematicResponse, SuccessfulResponse } from '${config.makeImport(
1392
2800
  "../http/response"
1393
2801
  )}';
1394
- import type { OutputType, Parser, Type } from '${config.makeImport(
1395
- "../http/send-request"
1396
- )}';
1397
2802
 
1398
2803
  import schemas from '${config.makeImport("./schemas")}';
1399
-
2804
+ import type { Unionize } from '${config.makeImport("../http/dispatcher")}';
1400
2805
  ${template(endpoints_default)({ outputType: config.style?.outputType })}`,
1401
2806
  [`${join("api", "schemas.ts")}`]: `${allSchemas.map((it) => it.import).join("\n")}
1402
2807
  import { KIND } from "${config.makeImport("../http/index")}";
@@ -1421,12 +2826,14 @@ ${allSchemas.map((it) => it.use).join(",\n")}
1421
2826
  join("api", `${spinalcase2(name)}.ts`),
1422
2827
  `${[
1423
2828
  ...imps,
1424
- // ...imports,
1425
2829
  `import z from 'zod';`,
1426
2830
  `import * as http from '${config.makeImport("../http/response")}';`,
1427
- `import { toRequest, json, urlencoded, nobody, formdata, createUrl } from '${config.makeImport("../http/request")}';`,
2831
+ `import { toRequest, json, urlencoded, empty, formdata, createUrl, type HeadersInit } from '${config.makeImport("../http/request")}';`,
1428
2832
  `import { chunked, buffered } from "${config.makeImport("../http/parse-response")}";`,
1429
- `import * as ${camelcase3(name)} from '../inputs/${config.makeImport(spinalcase2(name))}';`
2833
+ `import * as ${camelcase4(name)} from '../inputs/${config.makeImport(spinalcase2(name))}';`,
2834
+ `import { createBaseUrlInterceptor, createHeadersInterceptor, type Interceptor } from '${config.makeImport("../http/interceptors")}';`,
2835
+ `import { Dispatcher, fetchType, type InstanceType } from '${config.makeImport("../http/dispatcher")}';`,
2836
+ `import { Pagination, OffsetPagination, CursorPagination } from "${config.makeImport("../pagination/index")}";`
1430
2837
  ].join(
1431
2838
  "\n"
1432
2839
  )}
@@ -1441,8 +2848,8 @@ ${endpoint.flatMap((it) => it.schemas).join(",\n")}
1441
2848
  };
1442
2849
  }
1443
2850
  function toProps(spec, schemaOrRef, aggregator = []) {
1444
- if (isRef5(schemaOrRef)) {
1445
- const schema = followRef4(spec, schemaOrRef.$ref);
2851
+ if (isRef10(schemaOrRef)) {
2852
+ const schema = followRef7(spec, schemaOrRef.$ref);
1446
2853
  return toProps(spec, schema, aggregator);
1447
2854
  } else if (schemaOrRef.type === "object") {
1448
2855
  for (const [name] of Object.entries(schemaOrRef.properties ?? {})) {
@@ -1486,6 +2893,9 @@ function bodyInputs(config, ctSchema) {
1486
2893
  );
1487
2894
  }
1488
2895
 
2896
+ // packages/typescript/src/lib/http/dispatcher.txt
2897
+ var dispatcher_default = "export type Unionize<T> = T extends [infer Single extends OutputType]\n ? InstanceType<Single>\n : T extends readonly [...infer Tuple extends OutputType[]]\n ? { [I in keyof Tuple]: InstanceType<Tuple[I]> }[number]\n : never;\n\nexport type InstanceType<T> =\n T extends Type<infer U>\n ? U\n : T extends { type: Type<infer U> }\n ? U\n : T extends Array<unknown>\n ? Unionize<T>\n : never;\n\nexport interface Type<T> {\n new (...args: any[]): T;\n}\nexport type Parser = (\n response: Response,\n) => Promise<unknown> | ReadableStream<any>;\nexport type OutputType =\n | Type<APIResponse>\n | { parser: Parser; type: Type<APIResponse> };\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 parse<T extends OutputType[]>(\n outputs: T,\n response: Response,\n) <% if(!throwError) { %>\n: Promise<\n [\n Extract<InstanceType<T>, SuccessfulResponse>['data'],\n Extract<InstanceType<T>, ProblematicResponse>['data'],\n ]\n>\n <% } %>\n\n\n\n {\n let output: typeof APIResponse | null = null;\n let parser: Parser = buffered;\n for (const outputType of outputs) {\n if ('parser' in outputType) {\n parser = outputType.parser;\n if (isTypeOf(outputType.type, APIResponse)) {\n if (response.status === outputType.type.status) {\n output = outputType.type;\n break;\n }\n }\n } else if (isTypeOf(outputType, APIResponse)) {\n if (response.status === outputType.status) {\n output = outputType;\n break;\n }\n }\n }\n\n\n if (response.ok) {\n const apiresponse = (output || APIResponse).create(\n response.status,\n await parser(response),\n );\n <% if(throwError) { %>\n return <% if (outputType === 'default') { %>apiresponse as Extract<InstanceType<T>, SuccessfulResponse><% } else { %>apiresponse as Extract<InstanceType<T>, SuccessfulResponse>;<% } %>;\n <% } else { %>\n return [<% if (outputType === 'default') { %>apiresponse.data as Extract<InstanceType<T>, SuccessfulResponse>['data']<% } else { %>apiresponse as Extract<InstanceType<T>, SuccessfulResponse><% } %>, null] as const;\n <% } %>\n }\n<% if(throwError) { %>\n throw (output || APIError).create(\n response.status,\n await parser(response),\n );\n<% } else { %>\n const data = (output || APIError).create(\n response.status,\n await parser(response),\n );\n return [null, data] as const;\n<% } %>\n}\n\nexport function isTypeOf<T extends Type<APIResponse>>(\n instance: any,\n baseType: T,\n): instance is T {\n if (instance === baseType) {\n return true;\n }\n const prototype = Object.getPrototypeOf(instance);\n if (prototype === null) {\n return false;\n }\n return isTypeOf(prototype, baseType);\n}\n\nexport class Dispatcher {\n #interceptors: Interceptor[] = [];\n #fetch: z.infer<typeof fetchType>;\n constructor(interceptors: Interceptor[], fetch?: z.infer<typeof fetchType>) {\n this.#interceptors = interceptors;\n this.#fetch = fetch;\n }\n\n async send<T extends OutputType[]>(\n config: RequestConfig,\n outputs: T,\n signal?: AbortSignal,\n ) {\n for (const interceptor of this.#interceptors) {\n if (interceptor.before) {\n config = await interceptor.before(config);\n }\n }\n\n let response = await (this.#fetch ?? fetch)(\n new Request(config.url, config.init),\n {\n ...config.init,\n signal: signal,\n },\n );\n\n for (let i = this.#interceptors.length - 1; i >= 0; i--) {\n const interceptor = this.#interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n\n return await parse(outputs, response);\n }\n}\n";
2898
+
1489
2899
  // packages/typescript/src/lib/http/interceptors.txt
1490
2900
  var interceptors_default = "export interface Interceptor {\n before?: (config: RequestConfig) => Promise<RequestConfig> | RequestConfig;\n after?: (response: Response) => Promise<Response> | Response;\n}\n\nexport const createHeadersInterceptor = (\n defaultHeaders: () => Record<string, string | undefined>,\n requestHeaders: HeadersInit,\n):Interceptor => {\n return {\n before({init, url}) {\n // Priority Levels\n // 1. Headers Input\n // 2. Request Headers\n // 3. Default Headers\n const headers = defaultHeaders();\n\n for (const [key, value] of new Headers(requestHeaders)) {\n // Only set the header if it doesn't already exist and has a value\n // even though these headers are passed at operation level\n // still they are lower priority compared to the headers input\n if (value !== undefined && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\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 && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\n\n return {init, url};\n },\n };\n};\n\nexport const createBaseUrlInterceptor = (\n getBaseUrl: () => string,\n): Interceptor => {\n return {\n before({ init, url }) {\n const baseUrl = getBaseUrl();\n if (url.protocol === 'local:') {\n return {\n init,\n url: new URL(url.href.replace('local://', baseUrl))\n };\n }\n return { init, url };\n },\n };\n};\n\nexport const logInterceptor: Interceptor = {\n before({ url, init }) {\n console.log('Request:', { url, init });\n return { url, init };\n },\n after(response) {\n console.log('Response:', response);\n return response;\n },\n};\n\n/**\n * Creates an interceptor that logs detailed information about requests and responses.\n * @param options Configuration options for the logger\n * @returns An interceptor object with before and after handlers\n */\nexport const createDetailedLogInterceptor = (options?: {\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n includeRequestBody?: boolean;\n includeResponseBody?: boolean;\n}) => {\n const logLevel = options?.logLevel || 'info';\n const includeRequestBody = options?.includeRequestBody || false;\n const includeResponseBody = options?.includeResponseBody || false;\n\n return {\n async before(request: Request) {\n const logData = {\n url: request.url,\n method: request.method,\n contentType: request.headers.get('Content-Type'),\n headers: Object.fromEntries([...request.headers.entries()]),\n };\n\n console[logLevel]('\u{1F680} Outgoing Request:', logData);\n\n if (includeRequestBody) {\n try {\n // Clone the request to avoid consuming the body stream\n const clonedRequest = request.clone();\n if (clonedRequest.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedRequest.json().catch(() => null);\n console[logLevel]('Request Body:', body);\n } else {\n const body = await clonedRequest.text().catch(() => null);\n console[logLevel]('Request Body:', body);\n }\n } catch (error) {\n console.error('Could not log request body:', error);\n }\n }\n\n return request;\n },\n\n async after(response: Response) {\n const logData = {\n status: response.status,\n statusText: response.statusText,\n url: response.url,\n headers: Object.fromEntries([...response.headers.entries()]),\n };\n\n console[logLevel]('\u{1F4E5} Incoming Response:', logData);\n\n if (includeResponseBody && response.body) {\n try {\n // Clone the response to avoid consuming the body stream\n const clonedResponse = response.clone();\n if (clonedResponse.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedResponse.json().catch(() => null);\n console[logLevel]('Response Body:', body);\n } else {\n const body = await clonedResponse.text().catch(() => null);\n if (body) {\n console[logLevel]('Response Body:', body.substring(0, 500) + (body.length > 500 ? '...' : ''));\n } else {\n console[logLevel]('No response body');\n }\n }\n } catch (error) {\n console.error('Could not log response body:', error);\n }\n }\n\n return response;\n },\n };\n};\n";
1491
2901
 
@@ -1496,7 +2906,7 @@ var parse_response_default = 'import { parse } from "fast-content-type-parse";\n
1496
2906
  var parser_default = "import { z } from 'zod';\n\nexport class ParseError<T extends z.ZodType<any, any, any>> {\n public data: z.typeToFlattenedError<T, z.ZodIssue>;\n constructor(data: z.typeToFlattenedError<T, z.ZodIssue>) {\n this.data = data;\n }\n}\n\nexport function parseInput<T extends z.ZodType<any, any, any>>(\n schema: T,\n input: unknown,\n) {\n const result = schema.safeParse(input);\n if (!result.success) {\n const error = result.error.flatten((issue) => issue);\n return [null, new ParseError(error)];\n }\n return [result.data as z.infer<T>, null];\n}\n";
1497
2907
 
1498
2908
  // packages/typescript/src/lib/http/request.txt
1499
- var request_default = "type Init = Omit<RequestInit, 'headers'> & { headers: Headers; };\nexport type RequestConfig = { init: Init; url: URL };\nexport type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';\nexport type ContentType = 'xml' | 'json' | 'urlencoded' | 'multipart' | 'formdata';\nexport type HeadersInit = [string, string][] | Record<string, string>;\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 if (\n this.props.inputBody.length === 1 &&\n this.props.inputBody[0] === '$body'\n ) {\n return JSON.stringify(this.input.$body);\n }\n\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 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n };\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 Accept: 'application/json',\n };\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): RequestConfig {\n const [method, path] = endpoint.split(' ');\n const pathVariable = template(path, input.params);\n\n return {\n url: createUrl(pathVariable, input.query),\n init: {\n method: method,\n headers: new Headers(input.headers),\n body: method === 'GET' ? undefined : input.body,\n },\n }\n}\n";
2909
+ var request_default = "type Init = Omit<RequestInit, 'headers'> & { headers: Headers; };\nexport type RequestConfig = { init: Init; url: URL };\nexport type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';\nexport type ContentType = 'xml' | 'json' | 'urlencoded' | 'multipart' | 'formdata';\nexport type HeadersInit = [string, string][] | Record<string, string>;\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 if (\n this.props.inputBody.length === 1 &&\n this.props.inputBody[0] === '$body'\n ) {\n return JSON.stringify(this.input.$body);\n }\n\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 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n };\n }\n}\n\nclass EmptySerializer 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 Accept: 'application/json',\n };\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 empty(input: Input, props: Props) {\n return new EmptySerializer(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): RequestConfig {\n const [method, path] = endpoint.split(' ');\n const pathVariable = template(path, input.params);\n\n return {\n url: createUrl(pathVariable, input.query),\n init: {\n method: method,\n headers: new Headers(input.headers),\n body: method === 'GET' ? undefined : input.body,\n },\n }\n}\n";
1500
2910
 
1501
2911
  // packages/typescript/src/lib/http/response.txt
1502
2912
  var response_default = `export const KIND = Symbol('APIDATA');
@@ -1909,16 +3319,323 @@ export type SuccessfulResponse =
1909
3319
  | NoContent;
1910
3320
  `;
1911
3321
 
1912
- // packages/typescript/src/lib/http/send-request.txt
1913
- var send_request_default = "export interface Type<T> {\n new (...args: any[]): T;\n}\nexport type Parser = (\n response: Response,\n) => Promise<unknown> | ReadableStream<any>;\nexport type OutputType =\n | Type<APIResponse>\n | { parser: Parser; type: Type<APIResponse> };\n\nexport interface RequestSchema {\n schema: z.ZodType;\n toRequest: (input: any) => RequestConfig;\n output: OutputType[];\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 dispatch(\n input: unknown,\n route: RequestSchema,\n options: {\n fetch?: z.infer<typeof fetchType>;\n interceptors?: Interceptor[];\n signal?: AbortSignal;\n },\n) {\n const { interceptors = [] } = options;\n const [parsedInput, parseError] = parseInput(route.schema, input);\n if (parseError) {\n <% if(throwError) { %>\n throw parseError;\n <% } else { %>\n return [null as never, parseError as never] as const;\n <% } %>\n }\n\n let config = route.toRequest(parsedInput as never);\n for (const interceptor of interceptors) {\n if (interceptor.before) {\n config = await interceptor.before(config);\n }\n }\n\n let response = await (options.fetch ?? fetch)(\n new Request(config.url, config.init),\n {\n ...config.init,\n signal: options.signal,\n },\n );\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 return await parse(route, response);\n}\n\nexport async function parse(route: RequestSchema, response: Response) {\n let output: typeof APIResponse | null = null;\n let parser: Parser = buffered;\n for (const outputType of route.output) {\n if ('parser' in outputType) {\n parser = outputType.parser;\n if (isTypeOf(outputType.type, APIResponse)) {\n if (response.status === outputType.type.status) {\n output = outputType.type;\n break;\n }\n }\n } else if (isTypeOf(outputType, APIResponse)) {\n if (response.status === outputType.status) {\n output = outputType;\n break;\n }\n }\n }\n\n if (response.ok) {\n const apiresponse = (output || APIResponse).create(\n response.status,\n await parser(response),\n );\n <% if(throwError) { %>\n return <% if (outputType === 'default') { %>apiresponse.data<% } else { %>apiresponse<% } %>;\n <% } else { %>\n return [<% if (outputType === 'default') { %>apiresponse.data<% } else { %>apiresponse<% } %> , null] as const;\n <% } %>\n }\n<% if(throwError) { %>\n throw (output || APIError).create(\n response.status,\n await parser(response),\n );\n<% } else { %>\n const data = (output || APIError).create(\n response.status,\n await parser(response),\n );\n return [null as never, data as never] as const;\n<% } %>\n}\n\nexport function isTypeOf<T extends Type<APIResponse>>(\n instance: any,\n baseType: T,\n): instance is T {\n if (instance === baseType) {\n return true;\n }\n const prototype = Object.getPrototypeOf(instance);\n if (prototype === null) {\n return false;\n }\n return isTypeOf(prototype, baseType);\n}\n";
3322
+ // packages/typescript/src/lib/paginations/cursor-pagination.txt
3323
+ var cursor_pagination_default = "type CursorPaginationParams = {\n cursor?: string;\n};\n\ninterface CursorMetadata extends Metadata {\n nextCursor?: string;\n}\n\ninterface Metadata {\n hasMore?: boolean;\n}\n\ntype PaginationResult<T, M extends CursorMetadata> = {\n data: T[];\n meta: M;\n};\n\ntype FetchFn<T, M extends CursorMetadata> = (\n input: CursorPaginationParams,\n) => Promise<PaginationResult<T, M>>;\n\n/**\n * @experimental\n */\nexport class CursorPagination<T, M extends CursorMetadata> {\n #meta: PaginationResult<T, M>['meta'] | null = null;\n #params: CursorPaginationParams;\n #currentPage: Page<T> | null = null;\n readonly #fetchFn: FetchFn<T, M>;\n\n constructor(\n initialParams: PartialNullable<CursorPaginationParams>,\n fetchFn: FetchFn<T, M>,\n ) {\n this.#fetchFn = fetchFn;\n this.#params = {\n cursor: initialParams.cursor ?? undefined,\n };\n }\n\n async getNextPage() {\n const result = await this.#fetchFn(this.#params);\n this.#currentPage = new Page(result.data);\n this.#meta = result.meta;\n this.#params = {\n ...this.#params,\n cursor: result.meta.nextCursor,\n };\n return this;\n }\n\n getCurrentPage() {\n if (!this.#currentPage) {\n throw new Error(\n 'No page data available. Please call getNextPage() first.',\n );\n }\n return this.#currentPage;\n }\n\n get hasMore() {\n if (!this.#meta) {\n throw new Error(\n 'No meta data available. Please call getNextPage() first.',\n );\n }\n return this.#meta.hasMore;\n }\n\n async *[Symbol.asyncIterator]() {\n for await (const page of this.iter()) {\n yield page.getCurrentPage();\n }\n }\n\n async *iter() {\n if (!this.#currentPage) {\n yield await this.getNextPage();\n }\n\n while (this.hasMore) {\n yield await this.getNextPage();\n }\n }\n\n get metadata() {\n if (!this.#meta) {\n throw new Error(\n 'No meta data available. Please call getNextPage() first.',\n );\n }\n return this.#meta;\n }\n}\n\nclass Page<T> {\n data: T[];\n constructor(data: T[]) {\n this.data = data;\n }\n}\n\ntype PartialNullable<T> = {\n [K in keyof T]?: T[K] | null;\n};\n";
3324
+
3325
+ // packages/typescript/src/lib/paginations/offset-pagination.txt
3326
+ var offset_pagination_default = "type OffsetPaginationParams = {\n offset: number;\n limit: number;\n};\n\ninterface Metadata {\n hasMore?: boolean;\n}\n\ntype PaginationResult<T, M extends Metadata> = {\n data: T[];\n meta: M;\n};\n\ntype FetchFn<T, M extends Metadata> = (\n input: OffsetPaginationParams,\n) => Promise<PaginationResult<T, M>>;\n\n/**\n * @experimental\n */\nexport class OffsetPagination<T, M extends Metadata> {\n #meta: PaginationResult<T, M>['meta'] | null = null;\n #params: OffsetPaginationParams;\n #currentPage: Page<T> | null = null;\n readonly #fetchFn: FetchFn<T, M>;\n\n constructor(\n initialParams: Partial<OffsetPaginationParams>,\n fetchFn: FetchFn<T, M>,\n ) {\n this.#fetchFn = fetchFn;\n this.#params = {\n limit: initialParams.limit ?? 0,\n offset: initialParams.offset ?? 0,\n };\n }\n\n async getNextPage() {\n const result = await this.#fetchFn(this.#params);\n this.#currentPage = new Page(result.data);\n this.#meta = result.meta;\n this.#params = {\n ...this.#params,\n offset: this.#params.offset + this.#params.limit,\n };\n return this;\n }\n\n getCurrentPage() {\n if (!this.#currentPage) {\n throw new Error(\n 'No page data available. Please call getNextPage() first.',\n );\n }\n return this.#currentPage;\n }\n\n get hasMore() {\n if (!this.#meta) {\n throw new Error(\n 'No meta data available. Please call getNextPage() first.',\n );\n }\n return this.#meta.hasMore;\n }\n\n async *[Symbol.asyncIterator]() {\n for await (const page of this.iter()) {\n yield page.getCurrentPage();\n }\n }\n\n async *iter() {\n if (!this.#currentPage) {\n yield await this.getNextPage();\n }\n\n while (this.hasMore) {\n yield await this.getNextPage();\n }\n }\n\n get metadata() {\n if (!this.#meta) {\n throw new Error(\n 'No meta data available. Please call getNextPage() first.',\n );\n }\n return this.#meta;\n }\n\n reset(params?: Partial<OffsetPaginationParams>) {\n this.#meta = null;\n this.#currentPage = null;\n if (params) {\n this.#params = { ...this.#params, ...params };\n } else {\n this.#params.offset = 0;\n }\n return this;\n }\n}\n\nclass Page<T> {\n data: T[];\n constructor(data: T[]) {\n this.data = data;\n }\n}\n";
3327
+
3328
+ // packages/typescript/src/lib/paginations/page-pagination.txt
3329
+ var page_pagination_default = "type InferPage<T> = T extends Page<infer U> ? U : never;\ntype PaginationParams<P extends number | bigint, S extends number | bigint> = {\n page?: P;\n pageSize?: S;\n};\n\ninterface Metadata {\n hasMore?: boolean;\n}\n\ntype PaginationResult<T, M extends Metadata> = {\n data: T[];\n meta: M;\n};\n\ntype FetchFn<\n T,\n M extends Metadata,\n P extends number | bigint,\n S extends number | bigint,\n> = (input: Partial<PaginationParams<P, S>>) => Promise<PaginationResult<T, M>>;\n\n/**\n * @experimental\n */\nexport class Pagination<\n T,\n M extends Metadata,\n P extends number | bigint,\n S extends number | bigint,\n> {\n #meta: PaginationResult<T, M>['meta'] | null = null;\n #params: PaginationParams<P, S>;\n #currentPage: Page<T> | null = null;\n readonly #fetchFn: FetchFn<T, M, P, S>;\n\n constructor(\n initialParams: Partial<PaginationParams<P, S>>,\n fetchFn: FetchFn<T, M, P, S>,\n ) {\n this.#fetchFn = fetchFn;\n this.#params = { ...initialParams, page: initialParams.page };\n }\n\n async getNextPage() {\n const result = await this.#fetchFn(this.#params);\n this.#currentPage = new Page(result.data);\n this.#meta = result.meta;\n this.#params = {\n ...this.#params,\n page: ((this.#params.page as number) || 0 + 1) as never,\n };\n return this;\n }\n\n getCurrentPage() {\n if (!this.#currentPage) {\n throw new Error(\n 'No page data available. Please call getNextPage() first.',\n );\n }\n return this.#currentPage;\n }\n\n get hasMore() {\n if (!this.#meta) {\n throw new Error(\n 'No meta data available. Please call getNextPage() first.',\n );\n }\n return this.#meta.hasMore;\n }\n\n async *[Symbol.asyncIterator]() {\n for await (const page of this.iter()) {\n yield page.getCurrentPage();\n }\n }\n\n async *iter() {\n if (!this.#currentPage) {\n yield await this.getNextPage();\n }\n\n while (this.hasMore) {\n yield await this.getNextPage();\n }\n }\n\n get metadata() {\n if (!this.#meta) {\n throw new Error(\n 'No meta data available. Please call getNextPage() first.',\n );\n }\n return this.#meta;\n }\n}\n\nclass Page<T> {\n data: T[];\n constructor(data: T[]) {\n this.data = data;\n }\n}\n";
3330
+
3331
+ // packages/typescript/src/lib/typescript-snippet.ts
3332
+ import { camelcase as camelcase5, spinalcase as spinalcase3 } from "stringcase";
3333
+ import { followRef as followRef9, isEmpty as isEmpty5, isRef as isRef12, pascalcase as pascalcase3 } from "@sdk-it/core";
3334
+
3335
+ // packages/typescript/src/lib/emitters/snippet.ts
3336
+ import { followRef as followRef8, isRef as isRef11 } from "@sdk-it/core";
3337
+ var SnippetEmitter = class {
3338
+ spec;
3339
+ generatedRefs = /* @__PURE__ */ new Set();
3340
+ cache = /* @__PURE__ */ new Map();
3341
+ constructor(spec) {
3342
+ this.spec = spec;
3343
+ }
3344
+ object(schema) {
3345
+ const schemaObj = isRef11(schema) ? followRef8(this.spec, schema.$ref) : schema;
3346
+ const result = {};
3347
+ const properties = schemaObj.properties || {};
3348
+ for (const [propName, propSchema] of Object.entries(properties)) {
3349
+ const isRequired = (schemaObj.required ?? []).includes(propName);
3350
+ const resolvedProp = isRef11(propSchema) ? followRef8(this.spec, propSchema.$ref) : propSchema;
3351
+ if (isRequired || resolvedProp.example !== void 0 || resolvedProp.default !== void 0 || Math.random() > 0.5) {
3352
+ result[propName] = this.handle(propSchema);
3353
+ }
3354
+ }
3355
+ if (schemaObj.additionalProperties && typeof schemaObj.additionalProperties === "object") {
3356
+ result["additionalPropExample"] = this.handle(
3357
+ schemaObj.additionalProperties
3358
+ );
3359
+ }
3360
+ return result;
3361
+ }
3362
+ array(schema) {
3363
+ const schemaObj = isRef11(schema) ? followRef8(this.spec, schema.$ref) : schema;
3364
+ const itemsSchema = schemaObj.items;
3365
+ if (!itemsSchema) {
3366
+ return [];
3367
+ }
3368
+ const count = Math.min(schemaObj.minItems ?? 1, 2);
3369
+ const result = [];
3370
+ for (let i = 0; i < count; i++) {
3371
+ result.push(this.handle(itemsSchema));
3372
+ }
3373
+ return result;
3374
+ }
3375
+ string(schema) {
3376
+ if (schema.example !== void 0)
3377
+ return String(schema.example);
3378
+ if (schema.default !== void 0)
3379
+ return String(schema.default);
3380
+ switch (schema.format) {
3381
+ case "date-time":
3382
+ case "datetime":
3383
+ return (/* @__PURE__ */ new Date()).toISOString();
3384
+ case "date":
3385
+ return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3386
+ case "time":
3387
+ return (/* @__PURE__ */ new Date()).toISOString().split("T")[1];
3388
+ case "email":
3389
+ return "user@example.com";
3390
+ case "uuid":
3391
+ return "123e4567-e89b-12d3-a456-426614174000";
3392
+ case "uri":
3393
+ case "url":
3394
+ return "https://example.com";
3395
+ case "ipv4":
3396
+ return "192.168.1.1";
3397
+ case "ipv6":
3398
+ return "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
3399
+ case "hostname":
3400
+ return "example.com";
3401
+ case "binary":
3402
+ case "byte":
3403
+ return "[binary data]";
3404
+ default:
3405
+ if (schema.enum && schema.enum.length > 0) {
3406
+ return String(schema.enum[0]);
3407
+ }
3408
+ return schema.pattern ? `string matching ${schema.pattern}` : "example";
3409
+ }
3410
+ }
3411
+ number(schema) {
3412
+ if (schema.example !== void 0)
3413
+ return Number(schema.example);
3414
+ if (schema.default !== void 0)
3415
+ return Number(schema.default);
3416
+ let value;
3417
+ if (typeof schema.exclusiveMinimum === "number") {
3418
+ value = schema.exclusiveMinimum + 1;
3419
+ } else if (typeof schema.minimum === "number") {
3420
+ value = schema.minimum;
3421
+ } else {
3422
+ value = schema.type === "integer" ? 42 : 42.42;
3423
+ }
3424
+ if (typeof schema.exclusiveMaximum === "number" && value >= schema.exclusiveMaximum) {
3425
+ value = schema.exclusiveMaximum - 1;
3426
+ } else if (typeof schema.maximum === "number" && value > schema.maximum) {
3427
+ value = schema.maximum;
3428
+ }
3429
+ if (typeof schema.multipleOf === "number" && value % schema.multipleOf !== 0) {
3430
+ value = Math.floor(value / schema.multipleOf) * schema.multipleOf;
3431
+ }
3432
+ return schema.type === "integer" ? Math.floor(value) : value;
3433
+ }
3434
+ boolean(schema) {
3435
+ if (schema.example !== void 0)
3436
+ return Boolean(schema.example);
3437
+ if (schema.default !== void 0)
3438
+ return Boolean(schema.default);
3439
+ return true;
3440
+ }
3441
+ null() {
3442
+ return null;
3443
+ }
3444
+ ref($ref) {
3445
+ const parts = $ref.split("/");
3446
+ const refKey = parts[parts.length - 1] || "";
3447
+ if (this.cache.has($ref)) {
3448
+ return this.cache.get($ref);
3449
+ }
3450
+ this.cache.set($ref, { _ref: refKey });
3451
+ const resolved = followRef8(this.spec, $ref);
3452
+ const result = this.handle(resolved);
3453
+ this.cache.set($ref, result);
3454
+ return result;
3455
+ }
3456
+ allOf(schemas) {
3457
+ const initial = {};
3458
+ return schemas.reduce((result, schema) => {
3459
+ const example = this.handle(schema);
3460
+ if (typeof example === "object" && example !== null) {
3461
+ return { ...result, ...example };
3462
+ }
3463
+ return result;
3464
+ }, initial);
3465
+ }
3466
+ anyOf(schemas) {
3467
+ if (schemas.length === 0)
3468
+ return {};
3469
+ return this.handle(schemas[0]);
3470
+ }
3471
+ oneOf(schemas) {
3472
+ if (schemas.length === 0)
3473
+ return {};
3474
+ return this.handle(schemas[0]);
3475
+ }
3476
+ enum(schema) {
3477
+ return Array.isArray(schema.enum) && schema.enum.length > 0 ? schema.enum[0] : void 0;
3478
+ }
3479
+ handle(schemaOrRef) {
3480
+ if (isRef11(schemaOrRef)) {
3481
+ return this.ref(schemaOrRef.$ref);
3482
+ }
3483
+ const schema = isRef11(schemaOrRef) ? followRef8(this.spec, schemaOrRef.$ref) : schemaOrRef;
3484
+ if (schema.example !== void 0) {
3485
+ return schema.example;
3486
+ }
3487
+ if (schema.default !== void 0) {
3488
+ return schema.default;
3489
+ }
3490
+ if (schema.allOf && Array.isArray(schema.allOf)) {
3491
+ return this.allOf(schema.allOf);
3492
+ }
3493
+ if (schema.anyOf && Array.isArray(schema.anyOf)) {
3494
+ return this.anyOf(schema.anyOf);
3495
+ }
3496
+ if (schema.oneOf && Array.isArray(schema.oneOf)) {
3497
+ return this.oneOf(schema.oneOf);
3498
+ }
3499
+ if (schema.enum && Array.isArray(schema.enum) && schema.enum.length > 0) {
3500
+ return this.enum(schema);
3501
+ }
3502
+ const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
3503
+ if (types.length === 0) {
3504
+ if (schema.properties || schema.additionalProperties) {
3505
+ return this.object(schema);
3506
+ } else if (schema.items) {
3507
+ return this.array(schema);
3508
+ }
3509
+ return "example";
3510
+ }
3511
+ const primaryType = types.find((t) => t !== "null") || types[0];
3512
+ switch (primaryType) {
3513
+ case "string":
3514
+ return this.string(schema);
3515
+ case "number":
3516
+ case "integer":
3517
+ return this.number(schema);
3518
+ case "boolean":
3519
+ return this.boolean(schema);
3520
+ case "object":
3521
+ return this.object(schema);
3522
+ case "array":
3523
+ return this.array(schema);
3524
+ case "null":
3525
+ return this.null();
3526
+ default:
3527
+ return "unknown";
3528
+ }
3529
+ }
3530
+ };
3531
+
3532
+ // packages/typescript/src/lib/typescript-snippet.ts
3533
+ var TypeScriptGenerator = class {
3534
+ #spec;
3535
+ #settings;
3536
+ #snippetEmitter;
3537
+ #clientName;
3538
+ #packageName;
3539
+ constructor(spec, settings) {
3540
+ this.#spec = spec;
3541
+ this.#settings = settings;
3542
+ this.#snippetEmitter = new SnippetEmitter(spec);
3543
+ this.#clientName = settings.name?.trim() ? pascalcase3(settings.name) : "Client";
3544
+ this.#packageName = settings.name ? `@${spinalcase3(this.#clientName.toLowerCase())}/sdk` : "sdk";
3545
+ }
3546
+ succinct(entry, operation, values) {
3547
+ let payload = "{}";
3548
+ if (!isEmpty5(operation.requestBody)) {
3549
+ const contentTypes = Object.keys(operation.requestBody.content || {});
3550
+ if (contentTypes.length > 0) {
3551
+ const firstContent = operation.requestBody.content[contentTypes[0]];
3552
+ let schema = isRef12(firstContent.schema) ? followRef9(this.#spec, firstContent.schema.$ref) : firstContent.schema;
3553
+ if (schema) {
3554
+ if (schema.type !== "object") {
3555
+ schema = {
3556
+ type: "object",
3557
+ required: [operation.requestBody.required ? "$body" : ""],
3558
+ properties: {
3559
+ $body: schema
3560
+ }
3561
+ };
3562
+ }
3563
+ const properties = {};
3564
+ patchParameters(
3565
+ this.#spec,
3566
+ { type: "object", properties },
3567
+ operation
3568
+ );
3569
+ const examplePayload = this.#snippetEmitter.handle({
3570
+ ...schema,
3571
+ properties: Object.assign({}, properties, schema.properties)
3572
+ });
3573
+ Object.assign(
3574
+ examplePayload,
3575
+ values.requestBody ?? {},
3576
+ values.pathParameters ?? {},
3577
+ values.queryParameters ?? {},
3578
+ values.headers ?? {},
3579
+ values.cookies ?? {}
3580
+ );
3581
+ payload = JSON.stringify(examplePayload, null, 2);
3582
+ }
3583
+ }
3584
+ } else {
3585
+ const properties = {};
3586
+ patchParameters(this.#spec, { type: "object", properties }, operation);
3587
+ const examplePayload = this.#snippetEmitter.handle({
3588
+ properties
3589
+ });
3590
+ Object.assign(
3591
+ examplePayload,
3592
+ values.pathParameters ?? {},
3593
+ values.queryParameters ?? {},
3594
+ values.headers ?? {},
3595
+ values.cookies ?? {}
3596
+ );
3597
+ payload = JSON.stringify(examplePayload, null, 2);
3598
+ }
3599
+ return `const result = await ${camelcase5(this.#clientName)}.request('${entry.method.toUpperCase()} ${entry.path}', ${payload});`;
3600
+ }
3601
+ snippet(entry, operation) {
3602
+ const payload = this.succinct(entry, operation, {});
3603
+ return [
3604
+ "```typescript",
3605
+ `${this.client()}
3606
+ ${payload}
3607
+
3608
+ console.log(result.data);
3609
+ `,
3610
+ "```"
3611
+ ].join("\n");
3612
+ }
3613
+ client() {
3614
+ return `import { ${this.#clientName} } from '${this.#packageName}';
3615
+
3616
+ const ${camelcase5(this.#clientName)} = new ${this.#clientName}({
3617
+ baseUrl: '${this.#spec.servers?.[0]?.url ?? "http://localhost:3000"}',
3618
+ });`;
3619
+ }
3620
+ };
1914
3621
 
1915
3622
  // packages/typescript/src/lib/generate.ts
3623
+ var ALWAYS_AVAILABLE_FILES = [
3624
+ /readme\.md$/i,
3625
+ // match readme.md, case-insensitive
3626
+ /^tsconfig.*\.json$/,
3627
+ // match any tsconfig*.json
3628
+ /package\.json$/,
3629
+ // exact package.json
3630
+ /metadata\.json$/
3631
+ // exact metadata.json
3632
+ ];
1916
3633
  function security(spec) {
1917
3634
  const security2 = spec.security || [];
1918
3635
  const components = spec.components || {};
1919
3636
  const securitySchemes = components.securitySchemes || {};
1920
3637
  const paths = Object.values(spec.paths ?? {});
1921
- const options = securityToOptions(security2, securitySchemes);
3638
+ const options = securityToOptions2(spec, security2, securitySchemes);
1922
3639
  for (const it of paths) {
1923
3640
  for (const method of methods) {
1924
3641
  const operation = it[method];
@@ -1927,13 +3644,20 @@ function security(spec) {
1927
3644
  }
1928
3645
  Object.assign(
1929
3646
  options,
1930
- securityToOptions(operation.security || [], securitySchemes, "input")
3647
+ securityToOptions2(
3648
+ spec,
3649
+ operation.security || [],
3650
+ securitySchemes,
3651
+ "input"
3652
+ )
1931
3653
  );
1932
3654
  }
1933
3655
  }
1934
3656
  return options;
1935
3657
  }
1936
3658
  async function generate(spec, settings) {
3659
+ spec = "x-sdk-augmented" in spec ? spec : augmentSpec({ spec });
3660
+ const generator = new TypeScriptGenerator(spec, settings);
1937
3661
  const style = Object.assign(
1938
3662
  {},
1939
3663
  {
@@ -1943,7 +3667,29 @@ async function generate(spec, settings) {
1943
3667
  },
1944
3668
  settings.style ?? {}
1945
3669
  );
3670
+ const output = settings.mode === "full" ? join2(settings.output, "src") : settings.output;
1946
3671
  settings.useTsExtension ??= true;
3672
+ const writtenFiles = /* @__PURE__ */ new Set();
3673
+ settings.writer ??= writeFiles;
3674
+ const originalWriter = settings.writer;
3675
+ settings.writer = async (dir, contents) => {
3676
+ await originalWriter(dir, contents);
3677
+ for (const file of Object.keys(contents)) {
3678
+ if (contents[file] !== null) {
3679
+ writtenFiles.add(
3680
+ addLeadingSlash(`${relative(settings.output, dir)}/${file}`)
3681
+ );
3682
+ }
3683
+ }
3684
+ };
3685
+ settings.readFolder ??= async (folder) => {
3686
+ const files = await readdir(folder, { withFileTypes: true });
3687
+ return files.map((file) => ({
3688
+ fileName: file.name,
3689
+ filePath: join2(file.parentPath, file.name),
3690
+ isFolder: file.isDirectory()
3691
+ }));
3692
+ };
1947
3693
  const makeImport = (moduleSpecifier) => {
1948
3694
  return settings.useTsExtension ? `${moduleSpecifier}.ts` : moduleSpecifier;
1949
3695
  };
@@ -1954,36 +3700,34 @@ async function generate(spec, settings) {
1954
3700
  makeImport
1955
3701
  }
1956
3702
  );
1957
- const output = settings.mode === "full" ? join2(settings.output, "src") : settings.output;
1958
3703
  const options = security(spec);
1959
- const clientName = settings.name || "Client";
3704
+ const clientName = pascalcase4((settings.name || "client").trim());
3705
+ const packageName = settings.name ? `@${spinalcase4(settings.name.trim().toLowerCase())}/sdk` : "sdk";
1960
3706
  const inputFiles = generateInputs(groups, commonZod, makeImport);
1961
- await writeFiles(output, {
3707
+ await settings.writer(output, {
1962
3708
  "outputs/.gitkeep": "",
1963
3709
  "inputs/.gitkeep": "",
1964
3710
  "models/.getkeep": ""
1965
- // 'README.md': readme,
1966
3711
  });
1967
- await writeFiles(join2(output, "http"), {
1968
- "interceptors.ts": `
1969
- import type { RequestConfig, HeadersInit } from './${makeImport("request")}';
1970
- ${interceptors_default}`,
3712
+ await settings.writer(join2(output, "http"), {
1971
3713
  "parse-response.ts": parse_response_default,
1972
- "send-request.ts": `import z from 'zod';
1973
- import type { Interceptor } from './${makeImport("interceptors")}';
1974
- import { buffered } from './${makeImport("parse-response")}';
1975
- import { parseInput } from './${makeImport("parser")}';
1976
- import type { RequestConfig } from './${makeImport("request")}';
1977
- import { APIError, APIResponse } from './${makeImport("response")}';
1978
-
1979
- ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputType: style.outputType })}`,
1980
3714
  "response.ts": response_default,
1981
3715
  "parser.ts": parser_default,
1982
- "request.ts": request_default
3716
+ "request.ts": request_default,
3717
+ "dispatcher.ts": `import z from 'zod';
3718
+ import { type Interceptor } from '${makeImport("../http/interceptors")}';
3719
+ import { type RequestConfig } from '${makeImport("../http/request")}';
3720
+ import { buffered } from '${makeImport("./parse-response")}';
3721
+ import { APIError, APIResponse, type SuccessfulResponse, type ProblematicResponse } from '${makeImport("./response")}';
3722
+
3723
+ ${template2(dispatcher_default, {})({ throwError: !style.errorAsValue, outputType: style.outputType })}`,
3724
+ "interceptors.ts": `
3725
+ import type { RequestConfig, HeadersInit } from './${makeImport("request")}';
3726
+ ${interceptors_default}`
1983
3727
  });
1984
- await writeFiles(join2(output, "outputs"), outputs);
3728
+ await settings.writer(join2(output, "outputs"), outputs);
1985
3729
  const modelsImports = Object.entries(commonSchemas).map(([name]) => name);
1986
- await writeFiles(output, {
3730
+ await settings.writer(output, {
1987
3731
  "client.ts": client_default(
1988
3732
  {
1989
3733
  name: clientName,
@@ -2008,47 +3752,114 @@ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputT
2008
3752
  ])
2009
3753
  )
2010
3754
  });
3755
+ await settings.writer(join2(output, "pagination"), {
3756
+ "cursor-pagination.ts": cursor_pagination_default,
3757
+ "offset-pagination.ts": offset_pagination_default,
3758
+ "page-pagination.ts": page_pagination_default
3759
+ });
3760
+ const metadata = await readJson(join2(settings.output, "metadata.json"));
3761
+ metadata.content.generatedFiles = Array.from(writtenFiles);
3762
+ metadata.content.userFiles ??= [];
3763
+ await metadata.write(metadata.content);
3764
+ if (settings.cleanup !== false && metadata.content.generatedFiles) {
3765
+ const generated = metadata.content.generatedFiles;
3766
+ const user = metadata.content.userFiles;
3767
+ const keep = /* @__PURE__ */ new Set([...generated, ...user]);
3768
+ const actualFiles = await readFolder(settings.output, true);
3769
+ const toRemove = actualFiles.filter((f) => !keep.has(addLeadingSlash(f))).filter(
3770
+ (f) => !ALWAYS_AVAILABLE_FILES.some((pattern) => pattern.test(f))
3771
+ );
3772
+ for (const file of toRemove) {
3773
+ if (file.endsWith(`${sep}index.ts`)) {
3774
+ continue;
3775
+ }
3776
+ const filePath = join2(settings.output, file);
3777
+ await unlink(filePath);
3778
+ }
3779
+ }
2011
3780
  const folders = [
2012
- getFolderExports(join2(output, "outputs"), settings.useTsExtension),
3781
+ getFolderExports(
3782
+ join2(output, "outputs"),
3783
+ settings.readFolder,
3784
+ settings.useTsExtension
3785
+ ),
2013
3786
  getFolderExports(
2014
3787
  join2(output, "inputs"),
3788
+ settings.readFolder,
2015
3789
  settings.useTsExtension,
2016
3790
  ["ts"],
2017
- (dirent) => dirent.isDirectory() && ["schemas"].includes(dirent.name)
3791
+ (dirent) => dirent.isFolder && ["schemas"].includes(dirent.fileName)
3792
+ ),
3793
+ getFolderExports(
3794
+ join2(output, "api"),
3795
+ settings.readFolder,
3796
+ settings.useTsExtension
2018
3797
  ),
2019
- getFolderExports(join2(output, "api"), settings.useTsExtension),
2020
3798
  getFolderExports(
2021
3799
  join2(output, "http"),
3800
+ settings.readFolder,
2022
3801
  settings.useTsExtension,
2023
3802
  ["ts"],
2024
- (dirent) => !["response.ts", "parser.ts"].includes(dirent.name)
3803
+ (dirent) => !["response.ts", "parser.ts"].includes(dirent.fileName)
2025
3804
  )
2026
3805
  ];
2027
3806
  if (modelsImports.length) {
2028
3807
  folders.push(
2029
- getFolderExports(join2(output, "models"), settings.useTsExtension)
3808
+ getFolderExports(
3809
+ join2(output, "models"),
3810
+ settings.readFolder,
3811
+ settings.useTsExtension
3812
+ )
2030
3813
  );
2031
3814
  }
2032
3815
  const [outputIndex, inputsIndex, apiIndex, httpIndex, modelsIndex] = await Promise.all(folders);
2033
- await writeFiles(output, {
3816
+ await settings.writer(join2(output, "pagination"), {
3817
+ "index.ts": await getFolderExports(
3818
+ join2(output, "pagination"),
3819
+ settings.readFolder,
3820
+ settings.useTsExtension,
3821
+ ["ts"]
3822
+ )
3823
+ });
3824
+ await settings.writer(output, {
2034
3825
  "api/index.ts": apiIndex,
2035
3826
  "outputs/index.ts": outputIndex,
2036
3827
  "inputs/index.ts": inputsIndex || null,
2037
3828
  "http/index.ts": httpIndex,
2038
3829
  ...modelsImports.length ? { "models/index.ts": modelsIndex } : {}
2039
3830
  });
2040
- await writeFiles(output, {
2041
- "index.ts": await getFolderExports(output, settings.useTsExtension, ["ts"])
3831
+ await settings.writer(output, {
3832
+ "index.ts": await getFolderExports(
3833
+ output,
3834
+ settings.readFolder,
3835
+ settings.useTsExtension,
3836
+ ["ts"],
3837
+ (config) => config.fileName.endsWith("pagination")
3838
+ )
2042
3839
  });
2043
3840
  if (settings.mode === "full") {
2044
- await writeFiles(settings.output, {
3841
+ const configFiles = {
2045
3842
  "package.json": {
2046
3843
  ignoreIfExists: true,
2047
3844
  content: JSON.stringify(
2048
3845
  {
2049
- name: settings.name ? `@${spinalcase3(clientName.toLowerCase())}/sdk` : "sdk",
3846
+ name: packageName,
3847
+ version: "0.0.1",
2050
3848
  type: "module",
2051
3849
  main: "./src/index.ts",
3850
+ module: "./src/index.ts",
3851
+ types: "./src/index.ts",
3852
+ publishConfig: {
3853
+ access: "public"
3854
+ },
3855
+ exports: {
3856
+ "./package.json": "./package.json",
3857
+ ".": {
3858
+ types: "./src/index.ts",
3859
+ import: "./src/index.ts",
3860
+ default: "./src/index.ts"
3861
+ }
3862
+ },
2052
3863
  dependencies: {
2053
3864
  "fast-content-type-parse": "^3.0.0",
2054
3865
  zod: "^3.24.2"
@@ -2080,27 +3891,32 @@ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputT
2080
3891
  2
2081
3892
  )
2082
3893
  }
2083
- });
3894
+ };
3895
+ if (settings.readme) {
3896
+ configFiles["README.md"] = {
3897
+ ignoreIfExists: false,
3898
+ content: toReadme(spec, {
3899
+ generateSnippet: (...args) => generator.snippet(...args)
3900
+ })
3901
+ };
3902
+ }
3903
+ await settings.writer(settings.output, configFiles);
2084
3904
  }
2085
3905
  await settings.formatCode?.({
2086
3906
  output,
2087
3907
  env: npmRunPathEnv()
2088
3908
  });
2089
3909
  }
2090
-
2091
- // packages/typescript/src/lib/watcher.ts
2092
- import { watch as nodeWatch } from "node:fs/promises";
2093
- import { debounceTime, from } from "rxjs";
2094
- function watch(path) {
2095
- return from(
2096
- nodeWatch(path, {
2097
- persistent: true,
2098
- recursive: true
2099
- })
2100
- ).pipe(debounceTime(400));
3910
+ async function readJson(path) {
3911
+ const content = await exist(path) ? JSON.parse(await readFile(path, "utf-8")) : { content: {} };
3912
+ return {
3913
+ content,
3914
+ write: (value = content) => writeFile(path, JSON.stringify(value, null, 2), "utf-8")
3915
+ };
2101
3916
  }
2102
3917
  export {
3918
+ TypeScriptGenerator,
2103
3919
  generate,
2104
- watch
3920
+ readJson
2105
3921
  };
2106
3922
  //# sourceMappingURL=index.js.map