@sdk-it/dart 0.21.0 → 0.22.1

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,1456 +1,299 @@
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, pluralize2) {
39
- if (typeof __require === "function" && typeof exports === "object" && typeof module === "object") {
40
- module.exports = pluralize2();
41
- } else if (typeof define === "function" && define.amd) {
42
- define(function() {
43
- return pluralize2();
44
- });
45
- } else {
46
- root.pluralize = pluralize2();
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 pluralize2(word, count, inclusive) {
121
- var pluralized = count === 1 ? pluralize2.singular(word) : pluralize2.plural(word);
122
- return (inclusive ? count + " " : "") + pluralized;
123
- }
124
- pluralize2.plural = replaceWord(
125
- irregularSingles,
126
- irregularPlurals,
127
- pluralRules
128
- );
129
- pluralize2.isPlural = checkWord(
130
- irregularSingles,
131
- irregularPlurals,
132
- pluralRules
133
- );
134
- pluralize2.singular = replaceWord(
135
- irregularPlurals,
136
- irregularSingles,
137
- singularRules
138
- );
139
- pluralize2.isSingular = checkWord(
140
- irregularPlurals,
141
- irregularSingles,
142
- singularRules
143
- );
144
- pluralize2.addPluralRule = function(rule, replacement) {
145
- pluralRules.push([sanitizeRule(rule), replacement]);
146
- };
147
- pluralize2.addSingularRule = function(rule, replacement) {
148
- singularRules.push([sanitizeRule(rule), replacement]);
149
- };
150
- pluralize2.addUncountableRule = function(word) {
151
- if (typeof word === "string") {
152
- uncountables[word.toLowerCase()] = true;
153
- return;
154
- }
155
- pluralize2.addPluralRule(word, "$0");
156
- pluralize2.addSingularRule(word, "$0");
157
- };
158
- pluralize2.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 pluralize2.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 pluralize2.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 pluralize2.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(pluralize2.addUncountableRule);
387
- return pluralize2;
388
- });
389
- }
390
- });
391
-
392
1
  // packages/dart/src/lib/generate.ts
393
2
  import { parse as partContentType } from "fast-content-type-parse";
394
- import { merge as merge2 } from "lodash-es";
395
- import assert2 from "node:assert";
396
3
  import { readdir, writeFile } from "node:fs/promises";
397
4
  import { join } from "node:path";
398
- import { camelcase as camelcase3 } from "stringcase";
5
+ import { camelcase as camelcase2 } from "stringcase";
399
6
  import yaml from "yaml";
400
7
  import {
401
- followRef as followRef3,
402
- isEmpty as isEmpty3,
403
- isRef as isRef4,
404
- notRef as notRef2,
8
+ isEmpty as isEmpty2,
9
+ isRef as isRef2,
405
10
  pascalcase as pascalcase2,
11
+ resolveRef as resolveRef2,
406
12
  snakecase as snakecase2
407
13
  } from "@sdk-it/core";
408
14
  import {
15
+ createWriterProxy,
409
16
  getFolderExportsV2,
410
17
  writeFiles
411
18
  } from "@sdk-it/core/file-system.js";
412
-
413
- // packages/spec/dist/lib/operation.js
414
- import { camelcase } from "stringcase";
415
- import { followRef, isRef as isRef2 } from "@sdk-it/core/ref.js";
416
-
417
- // packages/spec/dist/lib/pagination/pagination.js
418
- import { isRef } from "@sdk-it/core/ref.js";
419
- import { isEmpty } from "@sdk-it/core/utils.js";
420
-
421
- // packages/spec/dist/lib/pagination/pagination-result.js
422
- var import_pluralize = __toESM(require_pluralize(), 1);
423
- var PRIMARY_TOP_TIER_KEYWORDS = [
424
- "data",
425
- "items",
426
- "results",
427
- "value"
428
- ];
429
- var PRIMARY_OTHER_KEYWORDS = [
430
- "records",
431
- "content",
432
- "list",
433
- "payload",
434
- "entities",
435
- "collection",
436
- "users",
437
- "products",
438
- "orders",
439
- "bookings",
440
- "articles",
441
- "posts",
442
- "documents",
443
- "events"
444
- ];
445
- var SECONDARY_KEYWORDS = ["entries", "rows", "elements"];
446
- var PLURAL_DEPRIORITIZE_LIST = [
447
- "status",
448
- "success",
449
- "address",
450
- "details",
451
- "properties",
452
- "params",
453
- "headers",
454
- "cookies",
455
- "series",
456
- "links",
457
- "meta",
458
- "metadata",
459
- "statistics",
460
- "settings",
461
- "options",
462
- "permissions",
463
- "credentials",
464
- "diagnostics",
465
- "warnings",
466
- "errors",
467
- "actions",
468
- "attributes",
469
- "categories",
470
- "features",
471
- "includes",
472
- "tags"
473
- ];
474
- var HAS_MORE_PRIMARY_POSITIVE_EXACT = [
475
- "hasmore",
476
- "hasnext",
477
- "hasnextpage",
478
- "moreitems",
479
- "moreitemsavailable",
480
- "nextpage",
481
- "nextpageexists",
482
- "nextpageavailable",
483
- "hasadditionalresults",
484
- "moreresultsavailable",
485
- "canloadmore",
486
- "hasadditional",
487
- "additionalitems",
488
- "fetchmore"
489
- ];
490
- var HAS_MORE_SECONDARY_POSITIVE_EXACT = ["more", "next"];
491
- var HAS_MORE_PRIMARY_INVERTED_EXACT = [
492
- "islast",
493
- "lastpage",
494
- "endofresults",
495
- "endoflist",
496
- "nomoreitems",
497
- "nomoredata",
498
- "allitemsloaded",
499
- "iscomplete",
500
- "completed"
501
- ];
502
- var HAS_MORE_POSITIVE_REGEX_PATTERNS = [
503
- "\\bhas_?more\\b",
504
- "\\bhas_?next\\b",
505
- // e.g., itemsHasNext, items_has_next
506
- "\\bmore_?items\\b",
507
- "\\bnext_?page\\b",
508
- // e.g., userNextPageFlag
509
- "\\badditional\\b",
510
- // e.g., hasAdditionalData, additional_results_exist
511
- "\\bcontinuation\\b",
512
- // e.g., continuationAvailable, has_continuation_token
513
- "\\bmore_?results\\b",
514
- "\\bpage_?available\\b",
515
- "\\bnext(?:_?(page))?\\b"
516
- ];
517
- var COMPILED_HAS_MORE_POSITIVE_REGEXES = HAS_MORE_POSITIVE_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
518
- var HAS_MORE_INVERTED_REGEX_PATTERNS = [
519
- "\\bis_?last\\b",
520
- // e.g., pageIsLast
521
- "\\blast_?page\\b",
522
- // e.g., resultsAreLastPage
523
- "\\bend_?of_?(data|results|list|items|stream)\\b",
524
- "\\bno_?more_?(items|data|results)?\\b",
525
- "\\ball_?(items_?)?loaded\\b",
526
- "\\bis_?complete\\b"
527
- ];
528
- var COMPILED_HAS_MORE_INVERTED_REGEXES = HAS_MORE_INVERTED_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
529
- function getItemsName(properties) {
530
- const arrayPropertyNames = [];
531
- for (const propName in properties) {
532
- if (propName in properties) {
533
- const propSchema = properties[propName];
534
- if (propSchema && propSchema.type === "array") {
535
- arrayPropertyNames.push(propName);
536
- }
537
- }
538
- }
539
- if (arrayPropertyNames.length === 0) {
540
- return null;
541
- }
542
- if (arrayPropertyNames.length === 1) {
543
- return arrayPropertyNames[0];
544
- }
545
- let bestCandidate = null;
546
- let candidateRank = Infinity;
547
- const updateCandidate = (propName, rank) => {
548
- if (rank < candidateRank) {
549
- bestCandidate = propName;
550
- candidateRank = rank;
551
- }
552
- };
553
- for (const propName of arrayPropertyNames) {
554
- const lowerPropName = propName.toLowerCase();
555
- if (PRIMARY_TOP_TIER_KEYWORDS.includes(lowerPropName)) {
556
- updateCandidate(propName, 2);
557
- continue;
558
- }
559
- if (candidateRank > 3 && PRIMARY_OTHER_KEYWORDS.includes(lowerPropName)) {
560
- updateCandidate(propName, 3);
561
- continue;
562
- }
563
- if (candidateRank > 4 && SECONDARY_KEYWORDS.includes(lowerPropName)) {
564
- updateCandidate(propName, 4);
565
- continue;
566
- }
567
- if (candidateRank > 5 && import_pluralize.default.isPlural(propName) && !PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
568
- updateCandidate(propName, 5);
569
- continue;
570
- }
571
- if (candidateRank > 6 && import_pluralize.default.isPlural(propName) && PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
572
- updateCandidate(propName, 6);
573
- continue;
574
- }
575
- }
576
- if (bestCandidate) {
577
- return bestCandidate;
578
- }
579
- return arrayPropertyNames[0];
580
- }
581
- function guess(properties) {
582
- const booleanPropertyNames = [];
583
- for (const propName in properties) {
584
- if (Object.prototype.hasOwnProperty.call(properties, propName)) {
585
- const propSchema = properties[propName];
586
- if (propSchema && propSchema.type === "boolean" || propSchema.type === "integer") {
587
- booleanPropertyNames.push(propName);
588
- }
589
- }
590
- }
591
- if (booleanPropertyNames.length === 0) {
592
- return null;
593
- }
594
- if (booleanPropertyNames.length === 1) {
595
- return booleanPropertyNames[0];
596
- }
597
- let bestCandidate = null;
598
- let candidateRank = Infinity;
599
- const updateCandidate = (propName, rank) => {
600
- if (rank < candidateRank) {
601
- bestCandidate = propName;
602
- candidateRank = rank;
603
- }
604
- };
605
- for (const propName of booleanPropertyNames) {
606
- const normalizedForExactMatch = propName.toLowerCase().replace(/[-_]/g, "");
607
- let currentPropRank = Infinity;
608
- if (HAS_MORE_PRIMARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
609
- currentPropRank = 1;
610
- } else if (HAS_MORE_SECONDARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
611
- currentPropRank = 2;
612
- } else {
613
- let foundPositiveRegex = false;
614
- for (const regex of COMPILED_HAS_MORE_POSITIVE_REGEXES) {
615
- if (regex.test(propName)) {
616
- currentPropRank = 3;
617
- foundPositiveRegex = true;
618
- break;
619
- }
620
- }
621
- if (!foundPositiveRegex) {
622
- if (HAS_MORE_PRIMARY_INVERTED_EXACT.includes(normalizedForExactMatch)) {
623
- currentPropRank = 4;
624
- } else {
625
- for (const regex of COMPILED_HAS_MORE_INVERTED_REGEXES) {
626
- if (regex.test(propName)) {
627
- currentPropRank = 5;
628
- break;
629
- }
630
- }
631
- }
632
- }
633
- }
634
- updateCandidate(propName, currentPropRank);
635
- }
636
- return bestCandidate;
637
- }
638
- function getHasMoreName(properties) {
639
- const rootGuess = guess(properties);
640
- if (rootGuess) {
641
- return rootGuess;
642
- }
643
- for (const propName in properties) {
644
- const propSchema = properties[propName];
645
- if (propSchema.type === "object" && propSchema.properties) {
646
- const nested = getHasMoreName(propSchema.properties);
647
- if (nested) {
648
- return propName + "." + nested;
649
- }
650
- }
651
- }
652
- return null;
653
- }
654
-
655
- // packages/spec/dist/lib/pagination/pagination.js
656
- var OFFSET_PARAM_REGEXES = [
657
- /\boffset\b/i,
658
- /\bskip\b/i,
659
- /\bstart(?:ing_at|_index)?\b/i,
660
- // e.g., start, starting_at, start_index
661
- /\bfrom\b/i
662
- ];
663
- var GENERIC_LIMIT_PARAM_REGEXES = [
664
- /\blimit\b/i,
665
- /\bcount\b/i,
666
- /\b(?:page_?)?size\b/i,
667
- // e.g., size, page_size, pagesize
668
- /\bmax_results\b/i,
669
- /\bnum_results\b/i,
670
- /\bshow\b/i,
671
- // Can sometimes mean limit
672
- /\bper_?page\b/i,
673
- // e.g., per_page, perpage
674
- /\bper-page\b/i,
675
- /\btake\b/i
676
- ];
677
- var PAGE_NUMBER_REGEXES = [
678
- /^page$/i,
679
- // Exact match for "page"
680
- /^p$/i,
681
- // Exact match for "p" (common shorthand)
682
- /\bpage_?(?:number|num|idx|index)\b/i
683
- // e.g., page_number, pageNumber, page_num, page_idx
684
- ];
685
- var PAGE_SIZE_REGEXES = [
686
- /\bpage_?size\b/i,
687
- // e.g., page_size, pagesize
688
- /^size$/i,
689
- // Exact "size"
690
- // /\bsize\b/i, // Broader "size" - can be ambiguous, prefer more specific ones first
691
- /\blimit\b/i,
692
- // Limit is often used for page size
693
- /\bcount\b/i,
694
- // Count can also be used for page size
695
- /\bper_?page\b/i,
696
- // e.g., per_page, perpage
697
- /\bper-page\b/i,
698
- /\bnum_?(?:items|records|results)\b/i,
699
- // e.g., num_items, numitems
700
- /\bresults_?per_?page\b/i
701
- ];
702
- var CURSOR_REGEXES = [
703
- /\bcursor\b/i,
704
- /\bafter(?:_?cursor)?\b/i,
705
- // e.g., after, after_cursor
706
- /\bbefore(?:_?cursor)?\b/i,
707
- // e.g., before, before_cursor
708
- /\b(next|prev|previous)_?(?:page_?)?token\b/i,
709
- // e.g., next_page_token, nextPageToken, prev_token
710
- /\b(next|prev|previous)_?cursor\b/i,
711
- // e.g., next_cursor, previousCursor
712
- /\bcontinuation(?:_?token)?\b/i,
713
- // e.g., continuation, continuation_token
714
- /\bpage(?:_?(token|id))?\b/i,
715
- // e.g., after, after_cursor
716
- /\bstart_?(?:key|cursor|token|after)\b/i
717
- // e.g., start_key, startCursor, startToken, startAfter
718
- ];
719
- var CURSOR_LIMIT_REGEXES = [
720
- /\blimit\b/i,
721
- /\bcount\b/i,
722
- /\bsize\b/i,
723
- // General size
724
- /\bfirst\b/i,
725
- // Common in Relay-style cursor pagination (forward pagination)
726
- /\blast\b/i,
727
- // Common in Relay-style cursor pagination (backward pagination)
728
- /\bpage_?size\b/i,
729
- // Sometimes page_size is used with cursors
730
- /\bnum_?(?:items|records|results)\b/i,
731
- // e.g., num_items
732
- /\bmax_?items\b/i,
733
- /\btake\b/i
734
- ];
735
- function findParamAndKeyword(queryParams, regexes, excludeParamName) {
736
- for (const param of queryParams) {
737
- if (param.name === excludeParamName) {
738
- continue;
739
- }
740
- for (const regex of regexes) {
741
- const match = param.name.match(regex);
742
- if (match) {
743
- return { param, keyword: match[0] };
744
- }
745
- }
746
- }
747
- return null;
748
- }
749
- function isOffsetPagination(operation, parameters) {
750
- const offsetMatch = findParamAndKeyword(parameters, OFFSET_PARAM_REGEXES);
751
- if (!offsetMatch)
752
- return null;
753
- const limitMatch = findParamAndKeyword(
754
- parameters,
755
- GENERIC_LIMIT_PARAM_REGEXES,
756
- offsetMatch.param.name
757
- );
758
- if (!limitMatch)
759
- return null;
760
- return {
761
- type: "offset",
762
- offsetParamName: offsetMatch.param.name,
763
- offsetKeyword: offsetMatch.keyword,
764
- limitParamName: limitMatch.param.name,
765
- limitKeyword: limitMatch.keyword
766
- };
767
- }
768
- function isPagePagination(operation) {
769
- const queryParams = operation.parameters.filter((p) => p.in === "query").filter(
770
- (it) => it.schema && !isRef(it.schema) && it.schema.type === "integer"
771
- );
772
- if (queryParams.length < 2)
773
- return null;
774
- const pageNoMatch = findParamAndKeyword(queryParams, PAGE_NUMBER_REGEXES);
775
- if (!pageNoMatch)
776
- return null;
777
- const pageSizeMatch = findParamAndKeyword(
778
- queryParams,
779
- PAGE_SIZE_REGEXES,
780
- pageNoMatch.param.name
781
- );
782
- if (!pageSizeMatch)
783
- return null;
784
- return {
785
- type: "page",
786
- pageNumberParamName: pageNoMatch.param.name,
787
- pageNumberKeyword: pageNoMatch.keyword,
788
- pageSizeParamName: pageSizeMatch.param.name,
789
- pageSizeKeyword: pageSizeMatch.keyword
790
- };
791
- }
792
- function isCursorPagination(operation) {
793
- const queryParams = operation.parameters.filter((p) => p.in === "query");
794
- if (queryParams.length < 2)
795
- return null;
796
- const cursorMatch = findParamAndKeyword(queryParams, CURSOR_REGEXES);
797
- if (!cursorMatch)
798
- return null;
799
- const limitMatch = findParamAndKeyword(
800
- queryParams,
801
- CURSOR_LIMIT_REGEXES,
802
- cursorMatch.param.name
803
- );
804
- if (!limitMatch)
805
- return null;
806
- return {
807
- type: "cursor",
808
- cursorParamName: cursorMatch.param.name,
809
- cursorKeyword: cursorMatch.keyword,
810
- limitParamName: limitMatch.param.name,
811
- limitKeyword: limitMatch.keyword
812
- };
813
- }
814
- function guessPagination(operation, body, response) {
815
- const bodyParameters = body && body.properties ? Object.keys(body.properties).map((it) => ({ name: it })) : [];
816
- const parameters = operation.parameters;
817
- if (isEmpty(operation.parameters) && isEmpty(bodyParameters)) {
818
- return { type: "none", reason: "no parameters" };
819
- }
820
- if (!response) {
821
- return { type: "none", reason: "no response" };
822
- }
823
- if (!response.properties) {
824
- return { type: "none", reason: "empty response" };
825
- }
826
- const properties = response.properties;
827
- const itemsKey = getItemsName(properties);
828
- if (!itemsKey) {
829
- return { type: "none", reason: "no items key" };
830
- }
831
- const hasMoreKey = getHasMoreName(excludeKey(properties, itemsKey));
832
- if (!hasMoreKey) {
833
- return { type: "none", reason: "no hasMore key" };
834
- }
835
- const pagination = isOffsetPagination(operation, [...parameters, ...bodyParameters]) || isPagePagination(operation) || isCursorPagination(operation);
836
- return pagination ? { ...pagination, items: itemsKey, hasMore: hasMoreKey } : { type: "none", reason: "no pagination" };
837
- }
838
- function excludeKey(obj, key) {
839
- const { [key]: _, ...rest } = obj;
840
- return rest;
841
- }
842
-
843
- // packages/spec/dist/lib/operation.js
844
- function augmentSpec(config) {
845
- config.spec.paths ??= {};
846
- const paths = {};
847
- for (const [path, pathItem] of Object.entries(config.spec.paths)) {
848
- const { parameters = [], ...methods } = pathItem;
849
- const fixedPath = path.replace(/:([^/]+)/g, "{$1}");
850
- for (const [method, operation] of Object.entries(methods)) {
851
- const formatOperationId = config.operationId ?? defaults.operationId;
852
- const formatTag = config.tag ?? defaults.tag;
853
- const operationId = formatOperationId(operation, fixedPath, method);
854
- const operationTag = formatTag(operation, fixedPath);
855
- const requestBody = isRef2(operation.requestBody) ? followRef(config.spec, operation.requestBody.$ref) : operation.requestBody;
856
- const tunedOperation = {
857
- ...operation,
858
- parameters: [...parameters, ...operation.parameters ?? []].map(
859
- (it) => isRef2(it) ? followRef(config.spec, it.$ref) : it
860
- ),
861
- tags: [operationTag],
862
- operationId,
863
- responses: resolveResponses(config.spec, operation),
864
- requestBody
865
- };
866
- tunedOperation["x-pagination"] = toPagination(
867
- config.spec,
868
- tunedOperation
869
- );
870
- Object.assign(paths, {
871
- [fixedPath]: {
872
- ...paths[fixedPath],
873
- [method]: tunedOperation
874
- }
875
- });
876
- }
877
- }
878
- return { ...config.spec, paths };
879
- }
880
- function toPagination(spec, tunedOperation) {
881
- if (tunedOperation["x-pagination"]) {
882
- return tunedOperation["x-pagination"];
883
- }
884
- const schema = getResponseContentSchema(
885
- spec,
886
- tunedOperation.responses["200"],
887
- "application/json"
888
- );
889
- const pagination = guessPagination(
890
- tunedOperation,
891
- tunedOperation.requestBody ? getRequestContentSchema(
892
- spec,
893
- tunedOperation.requestBody,
894
- "application/json"
895
- ) : void 0,
896
- schema
897
- );
898
- if (pagination && pagination.type !== "none" && schema) {
899
- return pagination;
900
- }
901
- return void 0;
902
- }
903
- function getResponseContentSchema(spec, response, type) {
904
- if (!response) {
905
- return void 0;
906
- }
907
- const content = response.content;
908
- if (!content) {
909
- return void 0;
910
- }
911
- for (const contentType in content) {
912
- if (contentType.toLowerCase() === type.toLowerCase()) {
913
- return isRef2(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
914
- }
915
- }
916
- return void 0;
917
- }
918
- function getRequestContentSchema(spec, requestBody, type) {
919
- const content = requestBody.content;
920
- if (!content) {
921
- return void 0;
922
- }
923
- for (const contentType in content) {
924
- if (contentType.toLowerCase() === type.toLowerCase()) {
925
- return isRef2(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
926
- }
927
- }
928
- return void 0;
929
- }
930
- var defaults = {
931
- operationId: (operation, path, method) => {
932
- if (operation.operationId) {
933
- return camelcase(operation.operationId);
934
- }
935
- const metadata = operation["x-oaiMeta"];
936
- if (metadata && metadata.name) {
937
- return camelcase(metadata.name);
938
- }
939
- return camelcase(
940
- [method, ...path.replace(/[\\/\\{\\}]/g, " ").split(" ")].filter(Boolean).join(" ").trim()
941
- );
942
- },
943
- tag: (operation, path) => {
944
- return operation.tags?.[0] ? sanitizeTag(operation.tags?.[0]) : determineGenericTag(path, operation);
945
- }
946
- };
947
- function resolveResponses(spec, operation) {
948
- const responses = operation.responses ?? {};
949
- const resolved = {};
950
- for (const status in responses) {
951
- const response = isRef2(responses[status]) ? followRef(spec, responses[status].$ref) : responses[status];
952
- resolved[status] = response;
953
- }
954
- return resolved;
955
- }
956
- function forEachOperation(config, callback) {
957
- const result = [];
958
- for (const [path, pathItem] of Object.entries(config.spec.paths ?? {})) {
959
- const { parameters = [], ...methods } = pathItem;
960
- for (const [method, operation] of Object.entries(methods)) {
961
- const metadata = operation["x-oaiMeta"] ?? {};
962
- const operationTag = operation.tags?.[0];
963
- result.push(
964
- callback(
965
- {
966
- name: metadata.name,
967
- method,
968
- path,
969
- groupName: operationTag,
970
- tag: operationTag
971
- },
972
- operation
973
- )
974
- );
975
- }
976
- }
977
- return result;
978
- }
979
- var reservedKeywords = /* @__PURE__ */ new Set([
980
- "await",
981
- // Reserved in async functions
982
- "break",
983
- "case",
984
- "catch",
985
- "class",
986
- "const",
987
- "continue",
988
- "debugger",
989
- "default",
990
- "delete",
991
- "do",
992
- "else",
993
- "enum",
994
- "export",
995
- "extends",
996
- "false",
997
- "finally",
998
- "for",
999
- "function",
1000
- "if",
1001
- "implements",
1002
- // Strict mode
1003
- "import",
1004
- "in",
1005
- "instanceof",
1006
- "interface",
1007
- // Strict mode
1008
- "let",
1009
- // Strict mode
1010
- "new",
1011
- "null",
1012
- "package",
1013
- // Strict mode
1014
- "private",
1015
- // Strict mode
1016
- "protected",
1017
- // Strict mode
1018
- "public",
1019
- // Strict mode
1020
- "return",
1021
- "static",
1022
- // Strict mode
1023
- "super",
1024
- "switch",
1025
- "this",
1026
- "throw",
1027
- "true",
1028
- "try",
1029
- "typeof",
1030
- "var",
1031
- "void",
1032
- "while",
1033
- "with",
1034
- "yield",
1035
- // Strict mode / Generator functions
1036
- // 'arguments' is not technically a reserved word, but it's a special identifier within functions
1037
- // and assigning to it or declaring it can cause issues or unexpected behavior.
1038
- "arguments"
1039
- ]);
1040
- function sanitizeTag(camelCasedTag) {
1041
- if (/^\d/.test(camelCasedTag)) {
1042
- return `_${camelCasedTag}`;
1043
- }
1044
- return reservedKeywords.has(camelcase(camelCasedTag)) ? `${camelCasedTag}_` : camelCasedTag;
1045
- }
1046
- function determineGenericTag(pathString, operation) {
1047
- const operationId = operation.operationId || "";
1048
- const VERSION_REGEX = /^[vV]\d+$/;
1049
- const commonVerbs = /* @__PURE__ */ new Set([
1050
- // Verbs to potentially strip from operationId prefix
1051
- "get",
1052
- "list",
1053
- "create",
1054
- "update",
1055
- "delete",
1056
- "post",
1057
- "put",
1058
- "patch",
1059
- "do",
1060
- "send",
1061
- "add",
1062
- "remove",
1063
- "set",
1064
- "find",
1065
- "search",
1066
- "check",
1067
- "make"
1068
- ]);
1069
- const segments = pathString.split("/").filter(Boolean);
1070
- const potentialCandidates = segments.filter(
1071
- (segment) => segment && !segment.startsWith("{") && !segment.endsWith("}") && !VERSION_REGEX.test(segment)
1072
- );
1073
- for (let i = potentialCandidates.length - 1; i >= 0; i--) {
1074
- const segment = potentialCandidates[i];
1075
- if (!segment.startsWith("@")) {
1076
- return sanitizeTag(camelcase(segment));
1077
- }
1078
- }
1079
- const canFallbackToPathSegment = potentialCandidates.length > 0;
1080
- if (operationId) {
1081
- const lowerOpId = operationId.toLowerCase();
1082
- const parts = operationId.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").replace(/([a-zA-Z])(\d)/g, "$1_$2").replace(/(\d)([a-zA-Z])/g, "$1_$2").toLowerCase().split(/[_-\s]+/);
1083
- const validParts = parts.filter(Boolean);
1084
- if (commonVerbs.has(lowerOpId) && validParts.length === 1 && canFallbackToPathSegment) {
1085
- } else if (validParts.length > 0) {
1086
- const firstPart = validParts[0];
1087
- const isFirstPartVerb = commonVerbs.has(firstPart);
1088
- if (isFirstPartVerb && validParts.length > 1) {
1089
- const verbPrefixLength = firstPart.length;
1090
- let nextPartStartIndex = -1;
1091
- if (operationId.length > verbPrefixLength) {
1092
- const charAfterPrefix = operationId[verbPrefixLength];
1093
- if (charAfterPrefix >= "A" && charAfterPrefix <= "Z") {
1094
- nextPartStartIndex = verbPrefixLength;
1095
- } else if (charAfterPrefix >= "0" && charAfterPrefix <= "9") {
1096
- nextPartStartIndex = verbPrefixLength;
1097
- } else if (["_", "-"].includes(charAfterPrefix)) {
1098
- nextPartStartIndex = verbPrefixLength + 1;
1099
- } else {
1100
- const match = operationId.substring(verbPrefixLength).match(/[A-Z0-9]/);
1101
- if (match && match.index !== void 0) {
1102
- nextPartStartIndex = verbPrefixLength + match.index;
1103
- }
1104
- if (nextPartStartIndex === -1 && operationId.length > verbPrefixLength) {
1105
- nextPartStartIndex = verbPrefixLength;
1106
- }
1107
- }
1108
- }
1109
- if (nextPartStartIndex !== -1 && nextPartStartIndex < operationId.length) {
1110
- const remainingOriginalSubstring = operationId.substring(nextPartStartIndex);
1111
- const potentialTag = camelcase(remainingOriginalSubstring);
1112
- if (potentialTag) {
1113
- return sanitizeTag(potentialTag);
1114
- }
1115
- }
1116
- const potentialTagJoined = camelcase(validParts.slice(1).join("_"));
1117
- if (potentialTagJoined) {
1118
- return sanitizeTag(potentialTagJoined);
1119
- }
1120
- }
1121
- const potentialTagFull = camelcase(operationId);
1122
- if (potentialTagFull) {
1123
- const isResultSingleVerb = validParts.length === 1 && isFirstPartVerb;
1124
- if (!(isResultSingleVerb && canFallbackToPathSegment)) {
1125
- if (potentialTagFull.length > 0) {
1126
- return sanitizeTag(potentialTagFull);
1127
- }
1128
- }
1129
- }
1130
- const firstPartCamel = camelcase(firstPart);
1131
- if (firstPartCamel) {
1132
- const isFirstPartCamelVerb = commonVerbs.has(firstPartCamel);
1133
- if (!isFirstPartCamelVerb || validParts.length === 1 || !canFallbackToPathSegment) {
1134
- return sanitizeTag(firstPartCamel);
1135
- }
1136
- }
1137
- if (isFirstPartVerb && validParts.length > 1 && validParts[1] && canFallbackToPathSegment) {
1138
- const secondPartCamel = camelcase(validParts[1]);
1139
- if (secondPartCamel) {
1140
- return sanitizeTag(secondPartCamel);
1141
- }
1142
- }
1143
- }
1144
- }
1145
- if (potentialCandidates.length > 0) {
1146
- let firstCandidate = potentialCandidates[0];
1147
- if (firstCandidate.startsWith("@")) {
1148
- firstCandidate = firstCandidate.substring(1);
1149
- }
1150
- if (firstCandidate) {
1151
- return sanitizeTag(camelcase(firstCandidate));
1152
- }
1153
- }
1154
- console.warn(
1155
- `Could not determine a suitable tag for path: ${pathString}, operationId: ${operationId}. Using 'unknown'.`
1156
- );
1157
- return "unknown";
1158
- }
1159
- function parseJsonContentType(contentType) {
1160
- if (!contentType) {
1161
- return null;
1162
- }
1163
- let mainType = contentType.trim();
1164
- const semicolonIndex = mainType.indexOf(";");
1165
- if (semicolonIndex !== -1) {
1166
- mainType = mainType.substring(0, semicolonIndex).trim();
1167
- }
1168
- mainType = mainType.toLowerCase();
1169
- if (mainType.endsWith("/json")) {
1170
- return mainType.split("/")[1];
1171
- } else if (mainType.endsWith("+json")) {
1172
- return mainType.split("+")[1];
1173
- }
1174
- return null;
1175
- }
1176
- function isStreamingContentType(contentType) {
1177
- return contentType === "application/octet-stream";
1178
- }
1179
- function isSuccessStatusCode(statusCode) {
1180
- statusCode = Number(statusCode);
1181
- return statusCode >= 200 && statusCode < 300;
1182
- }
1183
- function patchParameters(spec, objectSchema, operation) {
1184
- const securitySchemes = spec.components?.securitySchemes ?? {};
1185
- const securityOptions = securityToOptions(
1186
- spec,
1187
- operation.security ?? [],
1188
- securitySchemes
1189
- );
1190
- objectSchema.properties ??= {};
1191
- objectSchema.required ??= [];
1192
- for (const param of operation.parameters) {
1193
- if (param.required) {
1194
- objectSchema.required.push(param.name);
1195
- }
1196
- objectSchema.properties[param.name] = isRef2(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" };
1197
- }
1198
- for (const param of securityOptions) {
1199
- objectSchema.required = (objectSchema.required ?? []).filter(
1200
- (name) => name !== param.name
1201
- );
1202
- objectSchema.properties[param.name] = isRef2(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" };
1203
- }
1204
- }
1205
- function securityToOptions(spec, security, securitySchemes, staticIn) {
1206
- securitySchemes ??= {};
1207
- const parameters = [];
1208
- for (const it of security) {
1209
- const [name] = Object.keys(it);
1210
- if (!name) {
1211
- continue;
1212
- }
1213
- const schema = isRef2(securitySchemes[name]) ? followRef(spec, securitySchemes[name].$ref) : securitySchemes[name];
1214
- if (schema.type === "http") {
1215
- parameters.push({
1216
- in: staticIn ?? "header",
1217
- name: "authorization",
1218
- schema: { type: "string" }
1219
- });
1220
- continue;
1221
- }
1222
- if (schema.type === "apiKey") {
1223
- if (!schema.in) {
1224
- throw new Error(`apiKey security schema must have an "in" field`);
1225
- }
1226
- if (!schema.name) {
1227
- throw new Error(`apiKey security schema must have a "name" field`);
1228
- }
1229
- parameters.push({
1230
- in: staticIn ?? schema.in,
1231
- name: schema.name,
1232
- schema: { type: "string" }
1233
- });
1234
- continue;
1235
- }
1236
- }
1237
- return parameters;
1238
- }
1239
-
1240
- // packages/spec/dist/lib/loaders/local-loader.js
1241
- import { parse } from "yaml";
1242
-
1243
- // packages/spec/dist/lib/loaders/remote-loader.js
1244
- import { parse as parse2 } from "yaml";
19
+ import {
20
+ augmentSpec,
21
+ cleanFiles,
22
+ forEachOperation,
23
+ isSseContentType,
24
+ isStreamingContentType,
25
+ isSuccessStatusCode,
26
+ parseJsonContentType,
27
+ readWriteMetadata
28
+ } from "@sdk-it/spec";
1245
29
 
1246
30
  // packages/dart/src/lib/dart-emitter.ts
1247
31
  import { merge } from "lodash-es";
1248
32
  import assert from "node:assert";
1249
- import { camelcase as camelcase2, snakecase } from "stringcase";
33
+ import { camelcase, snakecase } from "stringcase";
1250
34
  import {
1251
- cleanRef,
1252
- followRef as followRef2,
1253
- isEmpty as isEmpty2,
1254
- isRef as isRef3,
35
+ followRef,
36
+ isEmpty,
37
+ isRef,
38
+ joinSkipDigits,
1255
39
  notRef,
1256
40
  parseRef,
1257
- pascalcase
41
+ pascalcase,
42
+ resolveRef
1258
43
  } from "@sdk-it/core";
1259
- var formatName = (it) => {
1260
- const startsWithDigitPattern = /^-?\d/;
1261
- if (typeof it === "number") {
1262
- if (Math.sign(it) === -1) {
1263
- return `$_${Math.abs(it)}`;
1264
- }
1265
- return `$${it}`;
1266
- }
1267
- if (it === "default") {
1268
- return "$default";
44
+ import {
45
+ coerceTypes,
46
+ formatName,
47
+ getRefUsage,
48
+ isPrimitiveSchema,
49
+ sanitizeTag
50
+ } from "@sdk-it/spec";
51
+ function coearceObject(schema) {
52
+ schema = structuredClone(schema);
53
+ if (schema["x-properties"]) {
54
+ schema.properties = {
55
+ ...schema.properties ?? {},
56
+ ...schema["x-properties"] ?? {}
57
+ };
1269
58
  }
1270
- if (typeof it === "string") {
1271
- if (startsWithDigitPattern.test(it)) {
1272
- if (typeof it === "number") {
1273
- if (Math.sign(it) === -1) {
1274
- return `$_${Math.abs(it)}`;
1275
- }
1276
- return `$${it}`;
1277
- }
1278
- }
1279
- let nameToFormat = it;
1280
- if (nameToFormat.startsWith("[")) {
1281
- nameToFormat = nameToFormat.slice(1);
1282
- }
1283
- if (nameToFormat.endsWith("]")) {
1284
- nameToFormat = nameToFormat.slice(0, -1);
1285
- }
1286
- return snakecase(nameToFormat);
59
+ if (schema["x-required"]) {
60
+ schema.required = Array.from(
61
+ /* @__PURE__ */ new Set([
62
+ ...Array.isArray(schema.required) ? schema.required : [],
63
+ ...schema["x-required"] || []
64
+ ])
65
+ );
1287
66
  }
1288
- return snakecase(String(it));
1289
- };
67
+ return schema;
68
+ }
1290
69
  var DartSerializer = class {
1291
70
  #spec;
1292
- #emit;
1293
- constructor(spec, emit) {
71
+ #emitHandler;
72
+ #emitHistory = /* @__PURE__ */ new Set();
73
+ #emit(name, content, schema) {
74
+ if (this.#emitHistory.has(content)) {
75
+ return;
76
+ }
77
+ this.#emitHistory.add(content);
78
+ this.#emitHandler?.(name, content, schema);
79
+ }
80
+ constructor(spec) {
1294
81
  this.#spec = spec;
1295
- this.#emit = emit;
1296
82
  }
1297
- #getRefUsage(schemaName, list = []) {
1298
- this.#spec.components ??= {};
1299
- this.#spec.components.schemas ??= {};
1300
- this.#spec.components.responses ??= {};
1301
- const checkSchema = (schema) => {
1302
- if (isRef3(schema)) {
1303
- const { model } = parseRef(schema.$ref);
1304
- return model === schemaName;
1305
- }
1306
- if (schema.oneOf && Array.isArray(schema.oneOf)) {
1307
- return schema.oneOf.some(
1308
- (subSchema) => checkSchema(subSchema)
1309
- );
1310
- }
1311
- if (schema.type === "array" && schema.items && notRef(schema.items) && schema.items.oneOf) {
1312
- return checkSchema(schema.items);
1313
- }
1314
- return false;
1315
- };
1316
- for (const [key, value] of Object.entries(this.#spec.components.schemas)) {
1317
- if (checkSchema(value)) {
1318
- list.push(key);
1319
- }
83
+ onEmit(emit) {
84
+ this.#emitHandler = emit;
85
+ }
86
+ #formatKeyName(name) {
87
+ if (name.startsWith("$")) {
88
+ return `\\${name}`;
1320
89
  }
1321
- return list;
90
+ return name;
1322
91
  }
1323
92
  #object(className, schema, context) {
1324
- if (schema.additionalProperties) {
1325
- this.#emit(className, `typedef ${className} = Map<String, dynamic>;`);
1326
- return {
1327
- content: "",
1328
- use: "Map<String, dynamic>",
1329
- encode: "input",
1330
- toJson: `this.${camelcase2(context.name)}`,
1331
- fromJson: `json['${camelcase2(context.name)}']`,
1332
- matches: `json['${camelcase2(context.name)}'] is Map<String, dynamic>`
1333
- };
1334
- }
1335
- if (isEmpty2(schema.properties)) {
1336
- if (context.noEmit !== true) {
1337
- this.#emit(
93
+ const { properties = {}, required = [] } = coearceObject(schema);
94
+ if (schema.additionalProperties || isEmpty(properties)) {
95
+ if (context.requestize) {
96
+ return this.#object(
1338
97
  className,
1339
- `class ${className} {
1340
- const ${className}(); // Add const constructor
1341
-
1342
- factory ${className}.fromJson(Map<String, dynamic> json) {
1343
- return const ${className}();
1344
- }
1345
-
1346
- Map<String, dynamic> toJson() => {};
1347
-
1348
- /// Determines if a given map can be parsed into an instance of this class.
1349
- /// Returns true for any map since this class has no properties.
1350
- static bool matches(Map<String, dynamic> json) {
1351
- return true; // Any map is fine for an empty object
1352
- }
1353
- }`
98
+ {
99
+ type: "object",
100
+ properties: {
101
+ $body: { "x-special": true }
102
+ }
103
+ },
104
+ context
105
+ );
106
+ }
107
+ if (context.noEmit !== true && !context.propName) {
108
+ this.#emit(
109
+ pascalcase(formatName(className)),
110
+ `typedef ${pascalcase(className)} = Map<String, dynamic>;`,
111
+ schema
1354
112
  );
1355
113
  }
1356
114
  return {
1357
115
  content: "",
1358
- encode: "input.toJson()",
1359
- use: className,
1360
- toJson: `${this.#safe(context.name, context.required)}`,
1361
- fromJson: `${className}.fromJson(json['${context.name}'])`,
1362
- matches: `${className}.matches(json['${context.name}'])`
116
+ use: `Map<String, dynamic>`,
117
+ encode: "input",
118
+ encodeV2: "",
119
+ fromJson: context.parsable,
120
+ matches: `${context.parsable} is Map<String, dynamic>`
1363
121
  };
1364
122
  }
123
+ let requestContent = "";
1365
124
  const props = [];
1366
125
  const toJsonProperties = [];
1367
126
  const constructorParams = [];
1368
127
  const fromJsonParams = [];
1369
128
  const matches = [];
1370
- for (const [key, propSchema] of Object.entries(schema.properties)) {
129
+ const headers = [];
130
+ const params = [];
131
+ const queryParams = [];
132
+ const bodyParams = [];
133
+ for (const [key, propSchema] of Object.entries(properties)) {
1371
134
  const propName = key.replace("[]", "");
1372
- const required = (schema.required ?? []).includes(key);
1373
- const typeStr = this.handle(className, propSchema, required, {
135
+ const safePropName = camelcase(formatName(propName));
136
+ const requiredProp = required.includes(key);
137
+ const jsonKey = this.#formatKeyName(propName);
138
+ const serializedType = this.handle(className, propSchema, requiredProp, {
1374
139
  name: propName,
1375
- required,
1376
- propName: [className, propName].filter(Boolean).join("_")
140
+ safeName: safePropName,
141
+ required: requiredProp,
142
+ propName: isRef(propSchema) ? pascalcase(propName) : joinSkipDigits([className, propName], "_"),
143
+ parsable: `json['${jsonKey}']`
1377
144
  });
1378
- const nullable2 = typeStr.nullable || !required;
1379
- const nullableSuffix = nullable2 ? "?" : "";
145
+ const nullable2 = serializedType.nullable || !requiredProp;
146
+ const nullableSuffix = serializedType.use === "dynamic" ? "" : nullable2 ? "?" : "";
147
+ const withValue = serializedType.literal !== void 0 ? ` = ${serializedType.literal}` : "";
1380
148
  props.push(
1381
- `final ${typeStr.use}${nullableSuffix} ${camelcase2(propName)};`
149
+ `final ${serializedType.use}${nullableSuffix} ${safePropName} ${withValue};`
1382
150
  );
1383
- fromJsonParams.push(`${camelcase2(propName)}: ${typeStr.fromJson}`);
1384
- toJsonProperties.push(`'${propName}': ${typeStr.toJson}`);
1385
- constructorParams.push(
1386
- `${required ? "required " : ""}this.${camelcase2(propName)},`
151
+ toJsonProperties.push(
152
+ `'${jsonKey}': ${safePropName}${serializedType.encodeV2}`
1387
153
  );
1388
- if (required) {
154
+ if (!withValue) {
155
+ fromJsonParams.push(`${safePropName}: ${serializedType.fromJson}`);
156
+ constructorParams.push(
157
+ `${requiredProp ? "required " : ""}this.${safePropName},`
158
+ );
159
+ }
160
+ if (requiredProp) {
1389
161
  matches.push(`(
1390
- json.containsKey('${camelcase2(propName)}')
1391
- ? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`} ${typeStr.matches ? `&& ${typeStr.matches}` : ""}
162
+ json.containsKey('${jsonKey}')
163
+ ? ${nullable2 ? `json['${jsonKey}'] == null` : `json['${jsonKey}'] != null`} ${serializedType.matches ? `&& ${serializedType.matches}` : ""}
1392
164
  : false)`);
1393
165
  } else {
1394
166
  matches.push(`(
1395
- json.containsKey('${camelcase2(propName)}')
1396
- ? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`} ${typeStr.matches ? `|| ${typeStr.matches}` : ""}
167
+ json.containsKey('${jsonKey}')
168
+ ? ${nullable2 ? `json['${jsonKey}'] == null` : `json['${jsonKey}'] != null`} ${serializedType.matches ? `|| ${serializedType.matches}` : ""}
1397
169
  : true)`);
1398
170
  }
171
+ const { "x-in": source, "x-special": special } = propSchema;
172
+ if (source) {
173
+ switch (source) {
174
+ case "header":
175
+ headers.push(`'${jsonKey}': ${safePropName}`);
176
+ break;
177
+ case "path":
178
+ params.push(`'${jsonKey}': ${safePropName}`);
179
+ break;
180
+ case "query":
181
+ queryParams.push(`'${jsonKey}': ${safePropName}`);
182
+ break;
183
+ default:
184
+ bodyParams.push(`'${jsonKey}': ${safePropName}`);
185
+ }
186
+ } else {
187
+ if (special) {
188
+ bodyParams.push(`$body${serializedType.encodeV2}`);
189
+ } else {
190
+ bodyParams.push(
191
+ `'${jsonKey}': ${safePropName}${serializedType.encodeV2}`
192
+ );
193
+ }
194
+ }
1399
195
  }
1400
- const { mixins, withMixins } = this.#mixinise(className, context);
1401
- const content = `class ${className} ${withMixins} {
196
+ if (context.requestize) {
197
+ const body = bodyParams.length === 1 && bodyParams[0].startsWith("$body") ? bodyParams[0] : `${bodyParams.length ? `{${bodyParams.join(", ")}}` : "{}"}`;
198
+ requestContent = `
199
+ RequestInput toRequest() =>
200
+ RequestInput(
201
+ headers: ${headers.length ? `{${headers.join(", ")}}` : "{}"},
202
+ query: ${queryParams.length ? `{${queryParams.join(", ")}}` : "{}"},
203
+ params: ${params.length ? `{${params.join(", ")}}` : "{}"},
204
+ body: ${body}
205
+ );`;
206
+ }
207
+ const constructorP = constructorParams.length ? `{${constructorParams.join("\n")}}` : "";
208
+ const fixedClassName = pascalcase(sanitizeTag(className));
209
+ const content = `class ${fixedClassName} {
1402
210
  ${props.join("\n")}
1403
- ${!mixins.length ? "const" : ""} ${className}({
1404
- ${constructorParams.join("\n")}})${mixins.length > 1 ? "" : `:super()`};
1405
- factory ${className}.fromJson(Map<String, dynamic> json) {
1406
- return ${className}(
211
+ const ${fixedClassName}(${constructorP}): super();
212
+ factory ${fixedClassName}.fromJson(Map<String, dynamic> json) {
213
+ return ${fixedClassName}(
1407
214
  ${fromJsonParams.join(",\n")});
1408
215
  }
1409
216
  Map<String, dynamic> toJson() => {
1410
217
  ${toJsonProperties.join(",\n")}
1411
218
  };
1412
- static bool matches(Map<String, dynamic> json) {
1413
- return ${matches.join(" && ")};
1414
- }
219
+ static bool matches(Map<String, dynamic> json) {return ${matches.join(" && ")};}
220
+
221
+ ${requestContent}
222
+
1415
223
  }`;
1416
- if (context.noEmit !== true) {
1417
- this.#emit(className, content);
224
+ if (context.noEmit !== true && !context.propName) {
225
+ this.#emit(fixedClassName, content, schema);
1418
226
  }
1419
227
  const nullable = !context.required || context.nullable === true;
228
+ const generatedClassName = context.forJson || className;
1420
229
  return {
1421
- use: className,
230
+ use: fixedClassName,
1422
231
  content,
1423
232
  encode: "input.toJson()",
1424
- toJson: `${this.#safe(context.name, context.required)}`,
1425
- fromJson: context.name ? `${context.forJson || className}.fromJson(json['${context.name}'])` : `${context.forJson || className}.fromJson(json)`,
1426
- matches: `${className}.matches(json['${context.name}'])`
233
+ encodeV2: `${context.required ? "" : "?"}.toJson()`,
234
+ fromJson: `${generatedClassName}.fromJson(${context.parsable})`,
235
+ matches: `${generatedClassName}.matches(${context.parsable})`
1427
236
  };
1428
237
  }
1429
- #safe(accces, required) {
1430
- return required ? `this.${camelcase2(accces)}.toJson()` : `this.${camelcase2(accces)} != null ? this.${camelcase2(accces)}!.toJson() : null`;
238
+ #oneOfObject(className, varientName, schemaOrRef, context) {
239
+ const schema = resolveRef(this.#spec, schemaOrRef);
240
+ const entries = Object.entries(schema.properties || {});
241
+ if (entries.length === 1) {
242
+ const [key, prop] = entries[0];
243
+ const serializedType2 = this.handle(
244
+ pascalcase(`${className} ${varientName}`),
245
+ prop,
246
+ true,
247
+ {
248
+ ...context,
249
+ propName: joinSkipDigits([varientName, key], "_"),
250
+ safeName: varientName,
251
+ parsable: `json['${varientName}']`
252
+ }
253
+ );
254
+ return {
255
+ typeStr: `${serializedType2.use}${serializedType2.nullable ? "?" : ""} ${key}`,
256
+ returnValue: `_Value({'${key}': ${key}${serializedType2.encodeV2}});`,
257
+ fromJson: serializedType2.fromJson
258
+ };
259
+ }
260
+ const serializedType = this.handle(className, schemaOrRef, true, {
261
+ ...context,
262
+ propName: joinSkipDigits([className, varientName], "_"),
263
+ safeName: varientName,
264
+ parsable: `json['${varientName}']`
265
+ });
266
+ return {
267
+ typeStr: `${serializedType.use} value`,
268
+ returnValue: `_Value(value.toJson());`,
269
+ fromJson: serializedType.fromJson
270
+ };
1431
271
  }
1432
272
  #array(className, schema, required = false, context) {
1433
273
  if (!schema.items) {
1434
274
  return {
1435
275
  content: "",
1436
276
  use: "List<dynamic>",
1437
- toJson: "",
1438
- fromJson: `List<dynamic>.from(${context.name ? `json['${context.name}']` : `json`})})`,
277
+ encodeV2: "",
278
+ fromJson: `List<dynamic>.from(${context.parsable})`,
1439
279
  matches: ""
1440
280
  };
1441
281
  }
1442
- const itemsType = this.handle(className, schema.items, true, context);
1443
- const fromJson = required ? context.name ? `(json['${context.name}'] as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
1444
- .map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
1445
- .toList()` : `(json as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
1446
- .map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
1447
- .toList()` : context.name ? `json['${context.name}'] != null
1448
- ? (json['${context.name}'] as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
1449
- .map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
282
+ const itemsType = this.handle(className, schema.items, true, {
283
+ ...context,
284
+ parsable: "it"
285
+ });
286
+ const fromJson = required ? context.name ? `(${context.parsable} as List)
287
+ .map((it) => ${itemsType.simple ? `it as ${itemsType.use}` : `${itemsType.fromJson}`})
288
+ .toList()` : `(json as List)
289
+ .map((it) => ${itemsType.simple ? `it as ${itemsType.use}` : `${itemsType.fromJson}`})
290
+ .toList()` : context.name ? `${context.parsable} != null
291
+ ? (${context.parsable} as List)
292
+ .map((it) => ${itemsType.simple ? `it as ${itemsType.use}` : `${itemsType.fromJson}`})
1450
293
  .toList()
1451
294
  : null` : `json != null
1452
- ? (json as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
1453
- .map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
295
+ ? (json as List)
296
+ .map((it) => ${itemsType.simple ? `it as ${itemsType.use}` : `${itemsType.fromJson}`})
1454
297
  .toList()
1455
298
  : null`;
1456
299
  return {
@@ -1458,8 +301,9 @@ return ${matches.join(" && ")};
1458
301
  content: "",
1459
302
  use: `List<${itemsType.use}>`,
1460
303
  fromJson,
1461
- toJson: `${context.required ? `this.${camelcase2(context.name)}${itemsType.simple ? "" : ".map((it) => it.toJson()).toList()"}` : `this.${camelcase2(context.name)}!= null? this.${camelcase2(context.name)}${itemsType.simple ? "" : "!.map((it) => it.toJson()).toList()"} : null`}`,
1462
- matches: `json['${camelcase2(context.name)}'].every((it) => ${itemsType.matches})`
304
+ simple: true,
305
+ encodeV2: `${itemsType.simple ? "" : `${context.required ? "" : "?"}.map((it) => ${itemsType.simple ? "it" : `it.toJson()`}).toList()`}`,
306
+ matches: context.parsable ? `(${context.parsable} as List).every((it) => ${itemsType.matches})` : `${context.parsable} is List && ${context.parsable}.every((it) => ${itemsType.matches})`
1463
307
  };
1464
308
  }
1465
309
  /**
@@ -1471,14 +315,15 @@ return ${matches.join(" && ")};
1471
315
  return this.#string(schema, context);
1472
316
  case "number":
1473
317
  case "integer":
1474
- return this.number(schema, context);
318
+ return this.#number(schema, context);
1475
319
  case "boolean":
1476
320
  return {
1477
321
  content: "",
1478
322
  use: "bool",
1479
- toJson: `${camelcase2(context.name)}`,
1480
- fromJson: `json['${context.name}']`,
1481
- matches: `json['${context.name}'] is bool`
323
+ encodeV2: "",
324
+ simple: true,
325
+ fromJson: context.parsable,
326
+ matches: `${context.parsable} is bool`
1482
327
  };
1483
328
  case "object":
1484
329
  return this.#object(className, schema, context);
@@ -1487,38 +332,51 @@ return ${matches.join(" && ")};
1487
332
  case "null":
1488
333
  return {
1489
334
  content: "",
335
+ encodeV2: "",
336
+ simple: true,
1490
337
  use: "Null",
1491
- toJson: `${camelcase2(context.name)}`,
1492
- fromJson: `json['${context.name}']`
338
+ fromJson: context.parsable
1493
339
  };
1494
340
  default:
1495
341
  return {
1496
342
  content: "",
1497
343
  use: "dynamic",
1498
- nullable: false,
1499
- toJson: `${camelcase2(context.name)}`,
1500
- fromJson: `json['${context.name}']`
344
+ encodeV2: "",
345
+ simple: true,
346
+ fromJson: context.parsable,
347
+ matches: "",
348
+ nullable: false
1501
349
  };
1502
350
  }
1503
351
  }
1504
352
  #ref(className, $ref, required, context) {
1505
- const schemaName = cleanRef($ref).split("/").pop();
1506
- const serialized = this.handle(
1507
- schemaName,
1508
- followRef2(this.#spec, $ref),
1509
- required,
1510
- {
353
+ const schemaName = pascalcase(sanitizeTag(parseRef($ref).model));
354
+ const schema = followRef(this.#spec, $ref);
355
+ if (isPrimitiveSchema(schema)) {
356
+ return this.handle(schemaName, schema, required, {
1511
357
  ...context,
1512
358
  propName: schemaName,
1513
- noEmit: !!context.alias || !!className || !context.forceEmit
1514
- }
1515
- );
1516
- return serialized;
359
+ noEmit: true,
360
+ // default to json in case this method is root level
361
+ parsable: context.parsable || "json"
362
+ });
363
+ }
364
+ const generatedClassName = context.forJson || schemaName;
365
+ const isDynamicObject = schema.type === "object" ? !!schema.additionalProperties || isEmpty(schema.properties) : schema.anyOf ? false : !schema.oneOf;
366
+ return {
367
+ use: pascalcase(schemaName),
368
+ content: "",
369
+ encode: "input.toJson()",
370
+ encodeV2: isDynamicObject ? "" : `${context.required ? "" : "?"}.toJson()`,
371
+ fromJson: isDynamicObject ? `${generatedClassName}.from(json)` : `${generatedClassName}.fromJson(${context.parsable || "json"})`,
372
+ matches: isDynamicObject ? `${context.parsable} is ${generatedClassName}` : `${generatedClassName}.matches(${context.parsable})`,
373
+ simple: isDynamicObject
374
+ };
1517
375
  }
1518
376
  // fixme: this method should no longer be needed because the logic in it is being preprocessed before emitting begins
1519
377
  #allOf(className, schemas, context) {
1520
378
  const name = pascalcase(context.propName || className);
1521
- const refs = schemas.filter(isRef3);
379
+ const refs = schemas.filter(isRef);
1522
380
  const nonRefs = schemas.filter(notRef);
1523
381
  if (nonRefs.some((it) => it.type && it.type !== "object")) {
1524
382
  assert(false, `allOf ${name} must be an object`);
@@ -1526,174 +384,169 @@ return ${matches.join(" && ")};
1526
384
  const objectSchema = merge(
1527
385
  {},
1528
386
  ...nonRefs,
1529
- ...refs.map((ref) => followRef2(this.#spec, ref.$ref))
387
+ ...refs.map((ref) => followRef(this.#spec, ref.$ref))
1530
388
  );
1531
389
  delete objectSchema.allOf;
1532
390
  return this.handle(name, objectSchema, true, context);
1533
391
  }
1534
- #anyOf(className, schemas, context) {
1535
- if (schemas.length === 0) {
1536
- return {
1537
- content: "",
1538
- nullable: false,
1539
- use: "dynamic",
1540
- toJson: `${camelcase2(context.name)}`,
1541
- fromJson: `json['${context.name}']`
1542
- };
1543
- }
1544
- const nullSchemaIndex = schemas.findIndex((schema) => {
1545
- if (isRef3(schema)) {
1546
- const refSchema = followRef2(this.#spec, schema.$ref);
1547
- return refSchema.type === "null";
1548
- }
1549
- return schema.type === "null";
1550
- });
1551
- const anyOfSchemas = schemas.slice(0);
1552
- if (nullSchemaIndex >= 0) {
1553
- anyOfSchemas.splice(nullSchemaIndex, 1);
1554
- }
1555
- return this.handle(className, anyOfSchemas[0], true, {
1556
- ...context,
1557
- nullable: nullSchemaIndex >= 0
1558
- });
1559
- }
1560
392
  #mixinise(name, context) {
1561
- const mixins = this.#getRefUsage(name);
393
+ const mixins = getRefUsage(this.#spec, name);
1562
394
  if (context.mixin) {
1563
395
  mixins.unshift(context.mixin);
1564
396
  }
1565
- const withMixins = mixins.length > 1 ? ` with ${mixins.join(", ")}` : mixins.length === 1 ? `extends ${mixins[0]}` : "";
397
+ const withMixins = mixins.length > 1 ? ` implements ${mixins.join(", ")}` : mixins.length === 1 ? `extends ${mixins[0]}` : "";
1566
398
  return {
1567
399
  withMixins,
1568
400
  mixins
1569
401
  };
1570
402
  }
1571
- #oneOf(className, schemas, context) {
1572
- const name = pascalcase(context.propName || className);
403
+ #buildClass(options) {
404
+ return [
405
+ options.abstract ? "abstract" : "",
406
+ options.mixins?.length ? "" : "mixin",
407
+ `class ${options.name} ${options.withMixins} ${options.implements?.length ? `implements ${options.implements.join(", ")}` : ""}`,
408
+ "{",
409
+ options.content.join("\n"),
410
+ "}"
411
+ ].filter(Boolean).join("\n");
412
+ }
413
+ #oneOf(className, schema, context) {
414
+ const varients = schema["x-varients"];
415
+ const schemas = schema.oneOf || schema.anyOf || [];
416
+ const name = pascalcase(sanitizeTag(context.propName || className));
1573
417
  if (schemas.length === 0) {
1574
418
  return {
1575
419
  content: "",
1576
420
  nullable: false,
1577
421
  use: "dynamic",
1578
- toJson: `${camelcase2(context.name)}`,
1579
- fromJson: `json['${context.name}']`
422
+ encodeV2: "",
423
+ fromJson: context.parsable
1580
424
  };
1581
425
  }
1582
426
  const content = [];
1583
427
  const patterns = [];
1584
- const objects = schemas.filter(notRef).filter((it) => it.type === "object");
1585
- for (const schema of schemas) {
1586
- if (isRef3(schema)) {
1587
- const refType = this.#ref(className, schema.$ref, true, context);
1588
- patterns.push({
1589
- pattern: `case ${refType.type || "Map<String, dynamic>"} map when ${refType.use}.matches(map): return ${refType.use}.fromJson(map);`,
1590
- name: refType.use
1591
- });
1592
- } else if (schema.type === "string") {
1593
- content.push(`class ${name}Text with ${name} {
1594
- final String value;
1595
- ${name}Text(this.value);
1596
- @override
1597
- dynamic toJson() => value;
1598
- static bool matches(dynamic value) {
1599
- return value is String;
1600
- }}
1601
- `);
1602
- patterns.push({
1603
- pattern: `case String(): return ${name}Text(json);`,
1604
- name: `${name}Text`
1605
- });
1606
- } else if (schema.type === "array") {
1607
- const itemsType = this.handle(name, schema.items, true, {
1608
- ...context,
1609
- noEmit: true
1610
- });
1611
- content.push(`class ${name}List with ${name} {
1612
- final List<${itemsType.use}> value;
1613
- ${name}List(this.value);
1614
- @override
1615
- dynamic toJson() => value;
1616
- static bool matches(dynamic value) {
1617
- return value is List;
1618
- }}`);
1619
- patterns.push({
1620
- pattern: `case List(): return ${name}List(List<${itemsType.use}>.from(json));`,
1621
- name: `${name}List`
1622
- });
1623
- }
1624
- }
1625
- if (objects.length) {
1626
- const candidates = {};
1627
- for (const schema of objects) {
1628
- if (schema.additionalProperties === true) {
428
+ content.push(`class _Value implements ${name} {
429
+ final dynamic value;
430
+ const _Value(this.value);
431
+ @override
432
+ toJson() => value;
433
+ }`);
434
+ for (const { name: varientName, ...varient } of varients) {
435
+ switch (varientName) {
436
+ case "empty":
437
+ patterns.push({
438
+ name: `static ${name} ${formatName(varientName)}() => _Value("");`,
439
+ pattern: `case '': return ${name}.${formatName(varientName)}();`
440
+ });
441
+ continue;
442
+ case "uri":
443
+ patterns.push({
444
+ name: `static ${name} ${formatName(varientName)}(Uri value) => _Value(value);`,
445
+ pattern: `case String: return ${name}.${formatName(varientName)}(Uri.parse(json));`
446
+ });
447
+ continue;
448
+ case "number":
449
+ patterns.push({
450
+ name: `static ${name} ${formatName(varientName)}(num value) => _Value(value);`,
451
+ pattern: `case num: return ${name}.${formatName(varientName)}(json);`
452
+ });
453
+ continue;
454
+ case "object": {
455
+ const result = this.handle(className, schemas[varient.position]);
456
+ patterns.push({
457
+ name: `static ${name} ${formatName(varientName)}(${result.use} value) => _Value(value);`,
458
+ pattern: `case Map<String, dynamic> map: return ${name}.${formatName(varientName)}(${pascalcase(
459
+ `${name} ${formatName(varientName)}`
460
+ )}.fromJson(map));`
461
+ });
1629
462
  continue;
1630
- }
1631
- assert(
1632
- schema.properties,
1633
- `Schema ${name} has no properties which are required in oneOf in order to determine the discriminator.`
1634
- );
1635
- for (const [propName, propSchema] of Object.entries(
1636
- schema.properties
1637
- )) {
1638
- if (notRef(propSchema) && propSchema.enum && // fixme: the enum can have more than one value as long as it is not duplicated else where on the other schemas
1639
- propSchema.enum.length === 1) {
1640
- candidates[propName] ??= /* @__PURE__ */ new Set();
1641
- candidates[propName].add(String(propSchema.enum[0]));
1642
- }
1643
463
  }
1644
464
  }
1645
- let discriminatorProp;
1646
- for (const [name2, values] of Object.entries(candidates)) {
1647
- if (
1648
- // make sure we pick the prop that exists on all objects
1649
- values.size === objects.filter((it) => it.properties?.[name2]).length
1650
- ) {
1651
- discriminatorProp = name2;
465
+ switch (varient.type) {
466
+ case "string":
467
+ patterns.push({
468
+ name: `static ${name} ${formatName(varientName)}(String value) => _Value(value);`,
469
+ pattern: `case String: return ${name}.${formatName(varientName)}(json);`
470
+ });
1652
471
  break;
1653
- }
1654
- }
1655
- if (discriminatorProp) {
1656
- for (const schema of objects) {
1657
- const discriminatorValue = schema.properties[discriminatorProp].enum?.[0];
1658
- const varientName = `${name}${pascalcase(discriminatorValue)}`;
472
+ case "object": {
473
+ const { typeStr, returnValue, fromJson } = this.#oneOfObject(
474
+ name,
475
+ formatName(varientName),
476
+ schemas[varient.position],
477
+ context
478
+ );
479
+ const staticStr = varient.static ? `&& json['${varient.source}'] == '${formatName(varientName)}'` : "";
480
+ const caseStr = `case Map<String, dynamic> json when json.containsKey('${varient.source}') ${staticStr}`;
481
+ const returnStr = `return ${name}.${formatName(varientName)}(${fromJson})`;
1659
482
  patterns.push({
1660
- pattern: `case Map<String, dynamic> map when ${varientName}.matches(json): return ${varientName}.fromJson(map);`,
1661
- name: varientName
483
+ name: `static ${name} ${formatName(varientName)}(${typeStr}) => ${returnValue}`,
484
+ pattern: `${caseStr}: ${returnStr};`
1662
485
  });
1663
- const objResult = this.#object(varientName, schema, {
1664
- ...context,
1665
- noEmit: true,
1666
- mixin: name
486
+ break;
487
+ }
488
+ case "array": {
489
+ const serializedType = this.handle(
490
+ className,
491
+ schemas[varient.position],
492
+ true,
493
+ {
494
+ ...context,
495
+ noEmit: true,
496
+ parsable: "json"
497
+ }
498
+ );
499
+ patterns.push({
500
+ name: `static ${name} ${formatName(varientName)}(${serializedType.use} value) => _Value(value);`,
501
+ pattern: `case ${serializedType.use} json: return ${name}.${formatName(varientName)}(${serializedType.fromJson});`
1667
502
  });
1668
- content.push(objResult.content);
503
+ break;
504
+ }
505
+ default: {
1669
506
  }
1670
507
  }
1671
508
  }
1672
509
  const { mixins, withMixins } = this.#mixinise(name, context);
1673
- content.unshift(`abstract ${mixins.length ? "" : "mixin"} class ${name} ${withMixins} {
1674
- dynamic toJson();
1675
- ${patterns.length ? `static ${name} fromJson(dynamic json) {
1676
- switch (json){
1677
- ${patterns.map((it) => it.pattern).join("\n")}
1678
- default:
1679
- throw ArgumentError("Invalid type for query property: \${json}");
1680
- }
1681
- }
1682
-
1683
-
1684
- ${patterns.length ? ` static bool matches(dynamic value) {
1685
- return ${patterns.map((it) => `value is ${it.name}`).join(" || ")};
1686
- }` : ""}
1687
-
1688
- ` : ""}
1689
- }`);
1690
- this.#emit(name, content.join("\n"));
510
+ content.push(
511
+ this.#buildClass({
512
+ name,
513
+ abstract: true,
514
+ mixins,
515
+ withMixins,
516
+ content: [
517
+ // 'dynamic get value;',
518
+ "dynamic toJson();",
519
+ ...patterns.map((it) => it.name),
520
+ `${name}();`,
521
+ `factory ${name}.fromJson(dynamic json)`,
522
+ `{`,
523
+ `switch (json) {`,
524
+ ...patterns.map((it) => it.pattern),
525
+ `default: throw ArgumentError("Invalid type: \${json}");`,
526
+ `}`,
527
+ `}`,
528
+ `static bool matches(dynamic value) {
529
+ try {
530
+ ${name}.fromJson(value);
531
+ return true;
532
+ } catch (error) {
533
+ return false;
534
+ }
535
+ }`
536
+ ]
537
+ })
538
+ );
539
+ if (context.noEmit !== true) {
540
+ this.#emit(name, content.join("\n"), {
541
+ oneOf: schemas
542
+ });
543
+ }
1691
544
  return {
1692
545
  content: content.join("\n"),
1693
546
  use: name,
1694
- toJson: `${this.#safe(context.name, context.required)}`,
1695
- fromJson: `${name}.fromJson(json['${context.name}'])`,
1696
- matches: `${name}.matches(json['${context.name}'])`
547
+ encodeV2: `${context.required ? "" : "?"}.toJson()`,
548
+ fromJson: `${name}.fromJson(${context.parsable})`,
549
+ matches: `${name}.matches(${context.parsable})`
1697
550
  };
1698
551
  }
1699
552
  #simple(type) {
@@ -1706,6 +559,12 @@ return ${matches.join(" && ")};
1706
559
  return "int";
1707
560
  case "boolean":
1708
561
  return "bool";
562
+ case "object":
563
+ return "Map<String, dynamic>";
564
+ case "array":
565
+ return "List<dynamic>";
566
+ case "null":
567
+ return "Null";
1709
568
  default:
1710
569
  return "dynamic";
1711
570
  }
@@ -1713,17 +572,17 @@ return ${matches.join(" && ")};
1713
572
  #enum(className, schema, context) {
1714
573
  const name = context.propName || className;
1715
574
  const values = schema.enum;
1716
- const valType = this.#simple(schema.type || "string");
1717
- const { mixins, withMixins } = this.#mixinise(className, context);
575
+ const valType = this.#simple(coerceTypes(schema)[0]);
576
+ const { mixins, withMixins } = this.#mixinise(name, context);
1718
577
  const content = `
1719
- class _EnumValue implements ${pascalcase(name)} {
578
+ class _EnumValue implements ${pascalcase(formatName(name))} {
1720
579
  final ${valType} value;
1721
580
  const _EnumValue(this.value);
1722
581
  @override
1723
- toJson() {return this.value;}
582
+ toJson() => value;
1724
583
  }
1725
- abstract ${mixins.length ? "" : "mixin"} class ${pascalcase(name)} ${withMixins} {
1726
- ${values.map((it) => `static const _EnumValue ${formatName(it)} = _EnumValue(${typeof it === "number" ? it : `'${it}'`});`).join("\n")}
584
+ abstract ${mixins.length ? "" : "mixin"} class ${pascalcase(formatName(name))} ${withMixins} {
585
+ ${values.map((it) => `static const _EnumValue ${formatName(snakecase(formatName(it)))} = _EnumValue(${typeof it === "number" ? it : `'${it}'`});`).join("\n")}
1727
586
  dynamic toJson();
1728
587
 
1729
588
  ${valType} get value;
@@ -1731,7 +590,7 @@ return ${matches.join(" && ")};
1731
590
  static _EnumValue fromJson(${valType} value) {
1732
591
  switch (value) {
1733
592
  ${values.map(
1734
- (it) => `case ${typeof it === "number" ? it : `'${it}'`}: return ${formatName(it)};`
593
+ (it) => `case ${typeof it === "number" ? it : `'${it}'`}: return ${formatName(snakecase(formatName(it)))};`
1735
594
  ).join("\n")}
1736
595
  default:
1737
596
  throw ArgumentError.value(value, "value", "No enum value with that name");
@@ -1749,15 +608,28 @@ return false;
1749
608
 
1750
609
  }`;
1751
610
  if (context.noEmit !== true) {
1752
- this.#emit(name, content);
611
+ this.#emit(pascalcase(formatName(name)), content, schema);
1753
612
  }
1754
613
  return {
1755
- type: Array.isArray(schema.type) ? this.#simple(schema.type[0]) : schema.type ? this.#simple(schema.type) : void 0,
614
+ use: pascalcase(formatName(name)),
615
+ type: this.#simple(coerceTypes(schema)[0]),
1756
616
  content,
1757
- use: pascalcase(name),
1758
- toJson: `${context.required ? `this.${camelcase2(context.name)}.toJson()` : `this.${camelcase2(context.name)} != null ? this.${camelcase2(context.name)}!.toJson() : null`}`,
1759
- fromJson: `${pascalcase(name)}.fromJson(json['${context.name}'])`,
1760
- matches: `${pascalcase(name)}.matches(json['${context.name}'])`
617
+ encodeV2: `${context.required ? "" : "?"}.toJson()`,
618
+ fromJson: `${pascalcase(name)}.fromJson(${context.parsable})`,
619
+ matches: `${pascalcase(name)}.matches(${context.parsable})`
620
+ };
621
+ }
622
+ #const(className, schema, context) {
623
+ const valType = this.#simple(coerceTypes(schema)[0]);
624
+ return {
625
+ content: "",
626
+ literal: valType === "String" ? `'${schema.const}'` : schema.const,
627
+ use: valType,
628
+ encode: "input",
629
+ encodeV2: "",
630
+ fromJson: context.parsable,
631
+ matches: context.parsable,
632
+ simple: true
1761
633
  };
1762
634
  }
1763
635
  /**
@@ -1772,46 +644,44 @@ return false;
1772
644
  content: "",
1773
645
  use: "DateTime",
1774
646
  simple: true,
1775
- toJson: context.required ? `this.${camelcase2(context.name)}.toIso8601String()` : `this.${camelcase2(context.name)} != null ? this.${camelcase2(
1776
- context.name
1777
- )}!.toIso8601String() : null`,
1778
- fromJson: context.name ? `json['${context.name}'] != null ? DateTime.parse(json['${context.name}']) : null` : "json",
1779
- matches: `json['${context.name}'] is String`
647
+ encodeV2: `${context.required ? "" : "?"}.toIso8601String()`,
648
+ fromJson: context.parsable ? context.required ? `DateTime.parse(${context.parsable})` : `${context.parsable} != null ? DateTime.parse(${context.parsable}) : null` : "json",
649
+ matches: `${context.parsable} is String`
1780
650
  };
1781
651
  case "binary":
1782
652
  case "byte":
1783
653
  return {
1784
654
  content: "",
1785
655
  use: "File",
1786
- toJson: `this.${camelcase2(context.name)}`,
656
+ encodeV2: "",
1787
657
  simple: true,
1788
- fromJson: context.name ? `json['${context.name}']` : "json",
1789
- matches: `json['${context.name}'] is Uint8List`
658
+ fromJson: context.parsable,
659
+ matches: `${context.parsable} is Uint8List`
1790
660
  };
1791
661
  default:
1792
662
  return {
1793
663
  encode: "input",
1794
664
  use: `String`,
1795
665
  content: "",
666
+ encodeV2: "",
1796
667
  simple: true,
1797
- toJson: `this.${camelcase2(context.name)}`,
1798
- fromJson: context.name ? `json['${context.name}'] as String` : "json",
1799
- matches: `json['${context.name}'] is String`
668
+ fromJson: `${context.parsable} as String`,
669
+ matches: `${context.parsable} is String`
1800
670
  };
1801
671
  }
1802
672
  }
1803
673
  /**
1804
674
  * Handle number/integer types with formats
1805
675
  */
1806
- number(schema, context) {
676
+ #number(schema, context) {
1807
677
  if (schema.type === "integer") {
1808
678
  return {
1809
679
  content: "",
1810
680
  use: "int",
1811
681
  simple: true,
1812
- toJson: `this.${camelcase2(context.name)}`,
1813
- fromJson: `json['${context.name}']`,
1814
- matches: `json['${context.name}'] is int`
682
+ encodeV2: "",
683
+ fromJson: context.parsable,
684
+ matches: `${context.parsable} is int`
1815
685
  };
1816
686
  }
1817
687
  if (["float", "double"].includes(schema.format)) {
@@ -1819,37 +689,40 @@ return false;
1819
689
  content: "",
1820
690
  use: "double",
1821
691
  simple: true,
1822
- toJson: `this.${camelcase2(context.name)}`,
1823
- fromJson: `json['${context.name}']`,
1824
- matches: `json['${context.name}'] is double`
692
+ encodeV2: "",
693
+ fromJson: context.parsable,
694
+ matches: `${context.parsable} is double`
1825
695
  };
1826
696
  }
1827
697
  return {
1828
698
  content: "",
1829
699
  use: "num",
1830
700
  simple: true,
1831
- toJson: `this.${camelcase2(context.name)}`,
1832
- fromJson: `json['${context.name}']`,
1833
- matches: `json['${context.name}'] is double`
701
+ encodeV2: "",
702
+ fromJson: context.parsable,
703
+ matches: `${context.parsable} is double`
1834
704
  };
1835
705
  }
1836
706
  #serialize(className, schema, required = true, context = {}) {
1837
- if (isRef3(schema)) {
707
+ if (isRef(schema)) {
1838
708
  return this.#ref(className, schema.$ref, required, context);
1839
709
  }
1840
710
  if (schema.allOf && Array.isArray(schema.allOf)) {
1841
711
  return this.#allOf(className, schema.allOf, context);
1842
712
  }
1843
713
  if (schema.oneOf && Array.isArray(schema.oneOf)) {
1844
- return this.#oneOf(className, schema.oneOf, context);
714
+ return this.#oneOf(className, schema, context);
1845
715
  }
1846
716
  if (schema.anyOf && Array.isArray(schema.anyOf)) {
1847
- return this.#anyOf(className, schema.anyOf, context);
717
+ return this.#oneOf(className, schema, context);
718
+ }
719
+ if (schema.const !== void 0) {
720
+ return this.#const(className, schema, context);
1848
721
  }
1849
722
  if (schema.enum && Array.isArray(schema.enum)) {
1850
723
  return this.#enum(className, schema, context);
1851
724
  }
1852
- const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
725
+ const types = coerceTypes(schema, false);
1853
726
  let nullable = false;
1854
727
  if ("nullable" in schema && schema.nullable) {
1855
728
  nullable = true;
@@ -1866,10 +739,12 @@ return false;
1866
739
  return this.#array(className, schema, true, context);
1867
740
  }
1868
741
  return {
742
+ // Unknown type -> fallback
1869
743
  content: "",
1870
744
  use: "dynamic",
1871
- toJson: `${camelcase2(context.name)}`,
1872
- fromJson: `json['${context.name}']`,
745
+ encodeV2: "",
746
+ simple: true,
747
+ fromJson: context.parsable,
1873
748
  nullable: false,
1874
749
  matches: ""
1875
750
  // keep it empty as 'type is dynamic' is always true
@@ -1891,93 +766,39 @@ return false;
1891
766
  forJson: alias
1892
767
  });
1893
768
  if (alias) {
1894
- this.#emit(className, `typedef ${alias} = ${serialized.use};`);
769
+ this.#emit(
770
+ pascalcase(formatName(className)),
771
+ `typedef ${pascalcase(alias)} = ${serialized.use};`,
772
+ resolveRef(this.#spec, schema)
773
+ );
1895
774
  return serialized;
1896
775
  }
1897
776
  return serialized;
1898
777
  }
1899
778
  };
1900
779
  function isObjectSchema(schema) {
1901
- return !isRef3(schema) && (schema.type === "object" || !!schema.properties);
780
+ return !isRef(schema) && (schema.type === "object" || !!coearceObject(schema).properties);
1902
781
  }
1903
782
 
1904
783
  // packages/dart/src/lib/http/dispatcher.txt
1905
- var dispatcher_default = "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:http/http.dart' as http;\nimport 'package:http_parser/http_parser.dart';\nimport 'package:mime/mime.dart' as mime;\n\nimport './interceptors.dart';\nimport './responses.dart';\n\nclass Dispatcher {\n final List<Interceptor> interceptors;\n\n Dispatcher(this.interceptors);\n\n Future<http.StreamedResponse> multipart(\n RequestConfig config,\n Map<String, dynamic> body,\n ) async {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n final request = http.MultipartRequest(\n modifiedConfig.method,\n modifiedConfig.url,\n );\n request.headers.addAll(modifiedConfig.headers);\n for (var entry in body.entries) {\n final key = entry.key;\n final value = entry.value;\n if (value is File) {\n final mimeType = mime.lookupMimeType(value.path);\n request.files.add(\n http.MultipartFile(\n key,\n value.openRead(),\n await value.length(),\n filename: value.uri.pathSegments.last,\n contentType: mimeType != null ? MediaType.parse(mimeType) : null,\n ),\n );\n } else {\n request.fields[key] = value.toString();\n }\n }\n\n return request.send();\n }\n\n Future<http.StreamedResponse> empty(RequestConfig config) {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n request.headers.addAll(modifiedConfig.headers);\n return request.send();\n }\n\n Future<http.StreamedResponse> json(RequestConfig config, dynamic body) {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n request.headers.addAll(modifiedConfig.headers);\n\n request.headers['Content-Type'] = 'application/json';\n if ((body is Map || body is List)) {\n request.body = jsonEncode(body);\n } else if (body is String) {\n request.body = body;\n } else {\n throw ArgumentError('Unsupported body type: ${body.runtimeType}');\n }\n\n return request.send();\n }\n}\n\nclass Receiver {\n final List<Interceptor> interceptors;\n Receiver(this.interceptors);\n\n dynamic _parse(http.Response response) {\n final contentTypeHeader = response.headers['content-type'];\n final parsed = parseContentType(contentTypeHeader);\n if (parsed.type == 'application/json') {\n return jsonDecode(response.body);\n } else if (parsed.type == 'text/plain') {\n return response.body;\n } else if (parsed.type == 'application/octet-stream') {\n return response.bodyBytes;\n } else {\n throw UnsupportedError('Unsupported content type: ${parsed.type}');\n }\n }\n\n dynamic json(http.StreamedResponse stream) async {\n if (stream.statusCode >= 200 && stream.statusCode < 300) {\n final response = await http.Response.fromStream(stream);\n return _parse(response);\n }\n switch (stream.statusCode) {\n case 400:\n throw BadRequestError('');\n case 401:\n throw UnauthorizedError('');\n case 403:\n throw ForbiddenError('');\n case 404:\n throw NotFoundError('');\n case 500:\n throw InternalServerError('');\n case 402:\n throw PaymentRequiredError('');\n case 405:\n throw MethodNotAllowedError('');\n case 406:\n throw NotAcceptableError('');\n case 409:\n throw ConflictError('');\n case 410:\n throw GoneError('');\n case 422:\n throw UnprocessableEntityError('');\n case 429:\n throw TooManyRequestsError('');\n case 413:\n throw PayloadTooLargeError('');\n case 415:\n throw UnsupportedMediaTypeError('');\n case 501:\n throw NotImplementedError('');\n case 502:\n throw BadGatewayError('');\n case 503:\n throw ServiceUnavailableError('');\n case 504:\n throw GatewayTimeoutError('');\n default:\n throw UnknownApiError('', stream.statusCode);\n }\n }\n}\n\n({String type, Map<String, String> parameters}) parseContentType(\n String? contentTypeHeader,\n) {\n if (contentTypeHeader == null || contentTypeHeader.isEmpty) {\n return (type: '', parameters: {});\n }\n final parts = contentTypeHeader.split(';');\n final type = parts[0].trim();\n final parameters = <String, String>{};\n for (var i = 1; i < parts.length; i++) {\n final param = parts[i].split('=');\n if (param.length == 2) {\n parameters[param[0].trim()] = param[1].trim();\n }\n }\n\n return (type: type, parameters: parameters);\n}\n";
784
+ var dispatcher_default = "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:http/http.dart' as http;\nimport 'package:http_parser/http_parser.dart';\nimport 'package:mime/mime.dart' as mime;\n\nimport './interceptors.dart';\nimport './responses.dart';\n\nclass Dispatcher {\n final List<Interceptor> interceptors;\n\n Dispatcher(this.interceptors);\n\n void _setHeaders(\n http.BaseRequest request,\n RequestConfig config, [\n String? contentType,\n ]) {\n request.headers.addAll(config.headers);\n for (var entry in config.input.headers.entries) {\n final value = entry.value;\n if (value != null) {\n request.headers[entry.key] = value;\n }\n }\n if (contentType != null) {\n request.headers['Content-Type'] = contentType;\n }\n }\n\n void _setParams(RequestConfig config) {\n final params = config.input.params;\n if (params.isEmpty) return;\n\n String path = config.url.toString();\n for (var entry in params.entries) {\n path = path.replaceAll('{${entry.key}}', entry.value.toString());\n }\n config.url = Uri.parse(path);\n }\n\n void _setQuery(RequestConfig config) {\n final query = config.input.query;\n if (query.isEmpty) return;\n\n final uri = config.url;\n final queryParameters = Map<String, String>.from(uri.queryParameters);\n\n for (var entry in query.entries) {\n if (entry.value != null) {\n queryParameters[entry.key] = entry.value.toString();\n }\n }\n\n config.url = uri.replace(queryParameters: queryParameters);\n }\n\n Future<http.StreamedResponse> formdata(RequestConfig config) async {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n _setParams(modifiedConfig);\n _setQuery(modifiedConfig);\n final request = http.MultipartRequest(\n modifiedConfig.method,\n modifiedConfig.url,\n );\n _setHeaders(request, modifiedConfig);\n\n for (var entry in config.input.body.entries) {\n final key = entry.key;\n final value = entry.value;\n if (value is File) {\n final mimeType = mime.lookupMimeType(value.path);\n request.files.add(\n http.MultipartFile(\n key,\n value.openRead(),\n await value.length(),\n filename: value.uri.pathSegments.last,\n contentType: mimeType != null ? MediaType.parse(mimeType) : null,\n ),\n );\n } else {\n request.fields[key] = value.toString();\n }\n }\n\n return request.send();\n }\n\n Future<http.StreamedResponse> empty(RequestConfig config) {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n _setParams(modifiedConfig);\n _setQuery(modifiedConfig);\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n _setHeaders(request, modifiedConfig);\n return request.send();\n }\n\n Future<http.StreamedResponse> json(RequestConfig config) {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n _setParams(modifiedConfig);\n _setQuery(modifiedConfig);\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n _setHeaders(request, modifiedConfig, 'application/json');\n\n if ((config.input.body is Map || config.input.body is List)) {\n request.body = jsonEncode(config.input.body);\n } else if (config.input.body is String) {\n request.body = config.input.body;\n } else {\n throw ArgumentError(\n 'Unsupported body type: ${config.input.body.runtimeType}',\n );\n }\n\n return request.send();\n }\n\n Future<http.StreamedResponse> binary(RequestConfig config) async {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n _setParams(modifiedConfig);\n _setQuery(modifiedConfig);\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n _setHeaders(request, modifiedConfig, 'application/octet-stream');\n\n switch (config.input.body) {\n case List<int> bytes:\n request.bodyBytes = bytes;\n case Stream<List<int>> stream:\n request.bodyBytes = await stream.expand((x) => x).toList();\n case String body:\n request.body = body;\n case File file:\n request.bodyBytes = await file.readAsBytes();\n default:\n throw ArgumentError(\n 'Unsupported body type for binary request: ${config.input.body.runtimeType}',\n );\n }\n\n return request.send();\n }\n\n Future<http.StreamedResponse> urlencoded(RequestConfig config) async {\n final modifiedConfig = interceptors.fold(\n config,\n (acc, interceptor) => interceptor.before(acc),\n );\n _setParams(modifiedConfig);\n _setQuery(modifiedConfig);\n final request = http.Request(modifiedConfig.method, modifiedConfig.url);\n _setHeaders(request, modifiedConfig, 'application/x-www-form-urlencoded');\n\n // Convert body to URL-encoded string\n final bodyMap = config.input.body;\n request.body = bodyMap.entries\n .map(\n (entry) =>\n '${Uri.encodeComponent(entry.key)}=${Uri.encodeComponent(entry.value.toString())}',\n )\n .join('&');\n\n return request.send();\n }\n}\n\nclass Receiver {\n final List<Interceptor> interceptors;\n Receiver(this.interceptors);\n\n dynamic _parse(http.Response response) {\n final contentTypeHeader = response.headers['content-type'];\n final parsed = parseContentType(contentTypeHeader);\n if (parsed.type.startsWith('application/json')) {\n return jsonDecode(response.body);\n } else if (parsed.type.startsWith('text/')) {\n return response.body;\n } else if (parsed.type == 'application/octet-stream') {\n return response.bodyBytes;\n } else {\n throw UnsupportedError('Unsupported content type: ${parsed.type}');\n }\n }\n\n Future<dynamic> json(http.StreamedResponse stream) async {\n if (stream.statusCode >= 200 && stream.statusCode < 300) {\n final response = await http.Response.fromStream(stream);\n return _parse(response);\n }\n throw _handleError(stream);\n }\n\n Future<String> text(http.StreamedResponse stream) async {\n if (stream.statusCode >= 200 && stream.statusCode < 300) {\n final response = await http.Response.fromStream(stream);\n return _parse(response);\n }\n throw _handleError(stream);\n }\n\n _handleError(http.StreamedResponse stream) {\n switch (stream.statusCode) {\n case 400:\n throw BadRequestError('');\n case 401:\n throw UnauthorizedError('');\n case 403:\n throw ForbiddenError('');\n case 404:\n throw NotFoundError('');\n case 500:\n throw InternalServerError('');\n case 402:\n throw PaymentRequiredError('');\n case 405:\n throw MethodNotAllowedError('');\n case 406:\n throw NotAcceptableError('');\n case 409:\n throw ConflictError('');\n case 410:\n throw GoneError('');\n case 422:\n throw UnprocessableEntityError('');\n case 429:\n throw TooManyRequestsError('');\n case 413:\n throw PayloadTooLargeError('');\n case 415:\n throw UnsupportedMediaTypeError('');\n case 501:\n throw NotImplementedError('');\n case 502:\n throw BadGatewayError('');\n case 503:\n throw ServiceUnavailableError('');\n case 504:\n throw GatewayTimeoutError('');\n default:\n throw UnknownApiError('', stream.statusCode);\n }\n }\n}\n\n({String type, Map<String, String> parameters}) parseContentType(\n String? contentTypeHeader,\n) {\n if (contentTypeHeader == null || contentTypeHeader.isEmpty) {\n return (type: '', parameters: {});\n }\n final parts = contentTypeHeader.split(';');\n final type = parts[0].trim();\n final parameters = <String, String>{};\n\n for (var i = 1; i < parts.length; i++) {\n final param = parts[i].split('=');\n if (param.length == 2) {\n parameters[param[0].trim()] = param[1].trim();\n }\n }\n\n return (type: type, parameters: parameters);\n}\n";
1906
785
 
1907
786
  // packages/dart/src/lib/http/interceptors.txt
1908
- var interceptors_default = "abstract class Interceptor {\n RequestConfig before(RequestConfig config);\n void after();\n}\n\nclass BaseUrlInterceptor extends Interceptor {\n final String Function() getBaseUrl;\n BaseUrlInterceptor(this.getBaseUrl);\n\n @override\n RequestConfig before(RequestConfig config) {\n final baseUrl = getBaseUrl();\n if (config.url.scheme.isEmpty) {\n config.url = Uri.parse(baseUrl + config.url.toString());\n }\n return config;\n }\n\n @override\n void after() {\n //\n }\n}\n\n\nclass LoggingInterceptor extends Interceptor {\n // ANSI color codes\n static const String _reset = '\\x1B[0m';\n static const String _green = '\\x1B[32m';\n static const String _yellow = '\\x1B[33m';\n static const String _blue = '\\x1B[34m';\n static const String _magenta = '\\x1B[35m';\n static const String _cyan = '\\x1B[36m';\n\n @override\n RequestConfig before(RequestConfig config) {\n print('${_cyan}[LOG] --- HTTP REQUEST ---$_reset');\n print('${_yellow}[LOG] Method: ${config.method.toUpperCase()}$_reset');\n print('${_green}[LOG] URL: ${config.url}$_reset');\n print('${_magenta}[ContentType] ${config.headers['content-type']}$_reset');\n if (config.headers.isNotEmpty) {\n print('${_blue}[LOG] Headers: ${config.headers}$_reset');\n } else {\n print('${_blue}[LOG] Headers: <none>$_reset');\n }\n print('${_cyan}[LOG] --------------$_reset');\n return config;\n }\n\n @override\n void after() {\n // Optionally log after the request\n }\n}\n\nclass RequestConfig {\n final String method;\n Uri url;\n final Map<String, String> headers;\n RequestConfig({required this.method, required this.url, required this.headers});\n}\n";
787
+ var interceptors_default = "abstract class Interceptor {\n RequestConfig before(RequestConfig config);\n void after();\n}\n\nclass BaseUrlInterceptor extends Interceptor {\n final String Function() getBaseUrl;\n BaseUrlInterceptor(this.getBaseUrl);\n\n @override\n RequestConfig before(RequestConfig config) {\n final baseUrl = getBaseUrl();\n if (config.url.scheme.isEmpty) {\n config.url = Uri.parse(baseUrl + config.url.toString());\n }\n return config;\n }\n\n @override\n void after() {\n //\n }\n}\n\nclass LoggingInterceptor extends Interceptor {\n // ANSI color codes\n static const String _reset = '\\x1B[0m';\n static const String _green = '\\x1B[32m';\n static const String _yellow = '\\x1B[33m';\n static const String _blue = '\\x1B[34m';\n static const String _magenta = '\\x1B[35m';\n static const String _cyan = '\\x1B[36m';\n\n @override\n RequestConfig before(RequestConfig config) {\n print('${_cyan}[LOG] --- HTTP REQUEST ---$_reset');\n print('${_yellow}[LOG] Method: ${config.method.toUpperCase()}$_reset');\n print('${_green}[LOG] URL: ${config.url}$_reset');\n print('${_magenta}[ContentType] ${config.headers['content-type']}$_reset');\n if (config.headers.isNotEmpty) {\n print('${_blue}[LOG] Headers: ${config.headers}$_reset');\n } else {\n print('${_blue}[LOG] Headers: <none>$_reset');\n }\n print('${_cyan}[LOG] --------------$_reset');\n return config;\n }\n\n @override\n void after() {\n // Optionally log after the request\n }\n}\n\nclass RequestConfig {\n final String method;\n Uri url;\n final Map<String, String> headers;\n final RequestInput input;\n\n RequestConfig({\n required this.method,\n required this.url,\n required this.headers,\n this.input = const RequestInput.empty(),\n });\n}\n\nclass RequestInput {\n final Map<String, String?> headers;\n final Map<String, dynamic> query;\n final Map<String, dynamic> params;\n final dynamic body;\n RequestInput({\n this.headers = const {},\n this.query = const {},\n this.params = const {},\n required this.body,\n });\n\n const RequestInput.empty()\n : headers = const {},\n query = const {},\n params = const {},\n body = null;\n}\n";
1909
788
 
1910
789
  // packages/dart/src/lib/http/responses.txt
1911
790
  var responses_default = "sealed class ApiError {\n final String message;\n final int? statusCode;\n final String status;\n const ApiError(this.message, {this.statusCode, this.status = ''});\n\n @override\n String toString() =>\n 'ApiError(status: $status, statusCode: $statusCode, message: $message)';\n}\n\nbase class BadRequestError extends ApiError {\n const BadRequestError(String message)\n : super(message, statusCode: 400, status: 'BadRequest');\n}\n\nbase class UnauthorizedError extends ApiError {\n const UnauthorizedError(String message)\n : super(message, statusCode: 401, status: 'Unauthorized');\n}\n\nbase class ForbiddenError extends ApiError {\n const ForbiddenError(String message)\n : super(message, statusCode: 403, status: 'Forbidden');\n}\n\nbase class NotFoundError extends ApiError {\n const NotFoundError(String message)\n : super(message, statusCode: 404, status: 'NotFound');\n}\n\nbase class InternalServerError extends ApiError {\n const InternalServerError(String message)\n : super(message, statusCode: 500, status: 'InternalServerError');\n}\n\nbase class UnknownApiError extends ApiError {\n const UnknownApiError(String message, int statusCode)\n : super(message, statusCode: statusCode, status: 'UnknownApiError');\n}\n\nbase class PaymentRequiredError extends ApiError {\n const PaymentRequiredError(String message)\n : super(message, statusCode: 402, status: 'PaymentRequired');\n}\n\nbase class MethodNotAllowedError extends ApiError {\n const MethodNotAllowedError(String message)\n : super(message, statusCode: 405, status: 'MethodNotAllowed');\n}\n\nbase class NotAcceptableError extends ApiError {\n const NotAcceptableError(String message)\n : super(message, statusCode: 406, status: 'NotAcceptable');\n}\n\nbase class ConflictError extends ApiError {\n const ConflictError(String message)\n : super(message, statusCode: 409, status: 'Conflict');\n}\n\nbase class GoneError extends ApiError {\n const GoneError(String message)\n : super(message, statusCode: 410, status: 'Gone');\n}\n\nbase class UnprocessableEntityError extends ApiError {\n const UnprocessableEntityError(String message)\n : super(message, statusCode: 422, status: 'UnprocessableEntity');\n}\n\nbase class TooManyRequestsError extends ApiError {\n const TooManyRequestsError(String message)\n : super(message, statusCode: 429, status: 'TooManyRequests');\n}\n\nbase class PayloadTooLargeError extends ApiError {\n const PayloadTooLargeError(String message)\n : super(message, statusCode: 413, status: 'PayloadTooLarge');\n}\n\nbase class UnsupportedMediaTypeError extends ApiError {\n const UnsupportedMediaTypeError(String message)\n : super(message, statusCode: 415, status: 'UnsupportedMediaType');\n}\n\nbase class NotImplementedError extends ApiError {\n const NotImplementedError(String message)\n : super(message, statusCode: 501, status: 'NotImplemented');\n}\n\nbase class BadGatewayError extends ApiError {\n const BadGatewayError(String message)\n : super(message, statusCode: 502, status: 'BadGateway');\n}\n\nbase class ServiceUnavailableError extends ApiError {\n const ServiceUnavailableError(String message)\n : super(message, statusCode: 503, status: 'ServiceUnavailable');\n}\n\nbase class GatewayTimeoutError extends ApiError {\n const GatewayTimeoutError(String message)\n : super(message, statusCode: 504, status: 'GatewayTimeout');\n}\n";
1912
791
 
1913
792
  // packages/dart/src/lib/generate.ts
1914
- function tuneSpec(spec, schemas, refs) {
1915
- for (const [name, schema] of Object.entries(schemas)) {
1916
- if (isRef4(schema))
1917
- continue;
1918
- if (!isEmpty3(schema.anyOf) && !isEmpty3(schema.oneOf)) {
1919
- delete schema.anyOf;
1920
- }
1921
- if (!isEmpty3(schema.allOf)) {
1922
- const schemas2 = schema.allOf;
1923
- const refs2 = schemas2.filter(isRef4);
1924
- const nonRefs = schemas2.filter(notRef2);
1925
- if (nonRefs.some((it) => it.type && it.type !== "object")) {
1926
- assert2(false, `allOf ${name} must be an object`);
1927
- }
1928
- const objectSchema = merge2(
1929
- {},
1930
- ...nonRefs,
1931
- ...refs2.map((ref) => followRef3(spec, ref.$ref))
1932
- );
1933
- delete objectSchema.allOf;
1934
- delete schema.allOf;
1935
- Object.assign(schema, objectSchema);
1936
- }
1937
- if (schema.type === "object") {
1938
- if (!isEmpty3(schema.oneOf)) {
1939
- for (const oneOfIdx in schema.oneOf) {
1940
- const oneOf = schema.oneOf[oneOfIdx];
1941
- if (isRef4(oneOf))
1942
- continue;
1943
- if (!isEmpty3(oneOf.required) && schema.properties) {
1944
- schema.oneOf[oneOfIdx] = schema.properties[oneOf.required[0]];
1945
- }
1946
- }
1947
- delete schema.type;
1948
- tuneSpec(spec, schemas, refs);
1949
- continue;
1950
- }
1951
- schema.properties ??= {};
1952
- for (const [propName, value] of Object.entries(schema.properties)) {
1953
- if (isRef4(value))
1954
- continue;
1955
- const refName = pascalcase2(`${name} ${propName.replace("[]", "")}`);
1956
- refs.push({ name: refName, value });
1957
- schema.properties[propName] = {
1958
- $ref: `#/components/schemas/${refName}`
1959
- };
1960
- const props = Object.fromEntries(
1961
- Object.entries(value.properties ?? {}).map(([key, value2]) => {
1962
- return [pascalcase2(`${refName} ${key}`), value2];
1963
- })
1964
- );
1965
- tuneSpec(spec, props, refs);
1966
- }
1967
- } else if (schema.type === "array") {
1968
- if (isRef4(schema.items))
1969
- continue;
1970
- const refName = name;
1971
- refs.push({ name: refName, value: schema.items ?? {} });
1972
- schema.items = {
1973
- $ref: `#/components/schemas/${refName}`
1974
- };
1975
- }
1976
- }
1977
- }
1978
- async function generate(spec, settings) {
1979
- spec = "x-sdk-augmented" in spec ? spec : augmentSpec({ spec });
1980
- settings.writer ??= writeFiles;
793
+ async function generate(openapi, settings) {
794
+ const spec = augmentSpec({ spec: openapi }, true);
795
+ const clientName = settings.name || "Client";
796
+ const output = join(settings.output, "lib");
797
+ const { writer, files: writtenFiles } = createWriterProxy(
798
+ settings.writer ?? writeFiles,
799
+ settings.output
800
+ );
801
+ settings.writer = writer;
1981
802
  settings.readFolder ??= async (folder) => {
1982
803
  const files = await readdir(folder, { withFileTypes: true });
1983
804
  return files.map((file) => ({
@@ -1986,80 +807,29 @@ async function generate(spec, settings) {
1986
807
  isFolder: file.isDirectory()
1987
808
  }));
1988
809
  };
1989
- const clientName = settings.name || "Client";
1990
- const output = join(settings.output, "lib");
1991
810
  const groups = {};
1992
- spec.components ??= {};
1993
- spec.components.schemas ??= {};
1994
- const inputs = {};
1995
- const outputs = {};
1996
- forEachOperation({ spec }, (entry, operation) => {
1997
- operation.responses ??= {};
1998
- spec.components ??= {};
1999
- spec.components.schemas ??= {};
2000
- for (const status in operation.responses) {
2001
- if (!isSuccessStatusCode(status))
2002
- continue;
2003
- const response2 = operation.responses[status];
2004
- if (!isEmpty3(response2.content)) {
2005
- for (const [contentType, mediaType] of Object.entries(
2006
- response2.content
2007
- )) {
2008
- if (parseJsonContentType(contentType)) {
2009
- if (mediaType.schema && !isRef4(mediaType.schema)) {
2010
- const outputName = pascalcase2(`${operation.operationId} output`);
2011
- spec.components.schemas[outputName] = mediaType.schema;
2012
- operation.responses[status].content ??= {};
2013
- operation.responses[status].content[contentType].schema = {
2014
- $ref: `#/components/schemas/${outputName}`
2015
- };
2016
- }
2017
- }
2018
- }
2019
- }
2020
- }
811
+ forEachOperation(spec, (entry, operation) => {
2021
812
  console.log(`Processing ${entry.method} ${entry.path}`);
2022
- const group = groups[entry.groupName] ?? (groups[entry.groupName] = {
813
+ const group = groups[entry.groupName] ??= {
2023
814
  methods: [],
2024
815
  use: `final ${entry.groupName} = new ${pascalcase2(entry.groupName)}();`
2025
- });
816
+ };
2026
817
  const input = toInputs(spec, { entry, operation });
2027
- Object.assign(inputs, input.inputs);
2028
818
  const response = toOutput(spec, operation);
2029
- if (response) {
2030
- Object.assign(outputs, response.outputs);
2031
- }
2032
819
  group.methods.push(`
2033
- Future<${response ? response.returnType : "http.StreamedResponse"}> ${camelcase3(operation.operationId)}(
2034
- ${isEmpty3(operation.requestBody) ? "" : `${input.inputName} input`}
820
+ Future<${response ? response.returnType : "http.StreamedResponse"}> ${camelcase2(operation.operationId)}(
821
+ ${input.haveInput ? `${input.inputName} input` : ""}
2035
822
  ) async {
2036
823
  final stream = await this.dispatcher.${input.contentType}(RequestConfig(
2037
824
  method: '${entry.method}',
2038
825
  url: Uri.parse('${entry.path}'),
2039
826
  headers: {},
2040
- ), ${["json", "multipart"].includes(input.contentType) ? input.encode : ``});
2041
- ${response ? `${response.decode};` : "return stream;"}
2042
- }
2043
- `);
827
+ ${input.haveInput ? "input: input.toRequest()" : ""}));
828
+ ${response ? `${response.decode};` : "return stream;"}
829
+ }
830
+ `);
2044
831
  });
2045
- const newRefs = [];
2046
- tuneSpec(spec, spec.components.schemas, newRefs);
2047
- for (const ref of newRefs) {
2048
- spec.components.schemas[ref.name] = ref.value;
2049
- }
2050
- await writeFile(
2051
- join(process.cwd(), "openai.json"),
2052
- JSON.stringify(spec, null, 2)
2053
- );
2054
- const models = Object.entries(spec.components.schemas).reduce((acc, [name, schema]) => {
2055
- const serializer = new DartSerializer(spec, (name2, content) => {
2056
- acc[`models/${snakecase2(name2)}.dart`] = `import 'dart:io';import 'dart:typed_data'; import './index.dart';
2057
-
2058
- ${content}`;
2059
- });
2060
- serializer.handle(pascalcase2(name), schema);
2061
- return acc;
2062
- }, {});
832
+ const models = serializeModels(spec);
2063
833
  const clazzez = Object.entries(groups).reduce(
2064
834
  (acc, [name, { methods }]) => {
2065
835
  return {
@@ -2093,14 +863,14 @@ import './http.dart';
2093
863
 
2094
864
  class ${clientName} {
2095
865
  final Options options;
2096
- ${Object.keys(groups).map((name) => `late final ${pascalcase2(name)}Client ${camelcase3(name)};`).join("\n")}
866
+ ${Object.keys(groups).map((name) => `late final ${pascalcase2(name)}Client ${camelcase2(name)};`).join("\n")}
2097
867
 
2098
868
  ${clientName}(this.options) {
2099
869
  final interceptors = [BaseUrlInterceptor(() => this.options.baseUrl), LoggingInterceptor()];
2100
870
  final dispatcher = Dispatcher(interceptors);
2101
871
  final receiver = Receiver(interceptors);
2102
872
  ${Object.keys(groups).map(
2103
- (name) => `this.${camelcase3(name)} = ${pascalcase2(name)}Client(dispatcher, receiver);`
873
+ (name) => `this.${camelcase2(name)} = ${pascalcase2(name)}Client(dispatcher, receiver);`
2104
874
  ).join("\n")}
2105
875
 
2106
876
  }
@@ -2119,11 +889,19 @@ class Options {
2119
889
  }
2120
890
 
2121
891
  `;
2122
- await settings.writer(output, {
2123
- ...models,
2124
- ...inputs,
2125
- ...outputs
2126
- });
892
+ await settings.writer(output, models);
893
+ const metadata = await readWriteMetadata(
894
+ settings.output,
895
+ Array.from(writtenFiles)
896
+ );
897
+ if (settings.cleanup !== false && writtenFiles.size > 0) {
898
+ await cleanFiles(metadata.content, settings.output, [
899
+ "/package.dart",
900
+ "/**/index.dart",
901
+ "pubspec.yaml",
902
+ "/metadata.json"
903
+ ]);
904
+ }
2127
905
  await settings.writer(output, {
2128
906
  "models/index.dart": await getFolderExportsV2(
2129
907
  join(output, "models"),
@@ -2183,101 +961,152 @@ class Options {
2183
961
  })
2184
962
  }
2185
963
  });
964
+ await writeFile(
965
+ join(settings.output, "openapi.json"),
966
+ JSON.stringify(spec, null, 2)
967
+ );
2186
968
  await settings.formatCode?.({
2187
969
  output
2188
970
  });
2189
971
  }
2190
972
  function toInputs(spec, { entry, operation }) {
2191
- const inputs = {};
2192
- const inputName = pascalcase2(`${operation.operationId} input`);
973
+ let inputName;
2193
974
  let contentType = "empty";
2194
975
  let encode = "";
2195
- if (!isEmpty3(operation.requestBody)) {
2196
- for (const type in operation.requestBody.content) {
2197
- const ctSchema = isRef4(operation.requestBody.content[type].schema) ? followRef3(spec, operation.requestBody.content[type].schema.$ref) : operation.requestBody.content[type].schema;
2198
- if (!ctSchema) {
2199
- console.warn(
2200
- `Schema not found for ${type} in ${entry.method} ${entry.path}`
2201
- );
2202
- continue;
2203
- }
2204
- ctSchema.properties ??= {};
2205
- ctSchema.required ??= [];
2206
- patchParameters(spec, ctSchema, operation);
2207
- const serializer = new DartSerializer(spec, (name, content) => {
2208
- inputs[join(`inputs/${name}.dart`)] = `import 'dart:io';import 'dart:typed_data';import '../models/index.dart'; import './index.dart';
2209
-
2210
- ${content}`;
2211
- });
2212
- const serialized = serializer.handle(inputName, ctSchema, true, {
2213
- alias: isObjectSchema(ctSchema) ? void 0 : inputName
2214
- });
2215
- encode = serialized.encode;
2216
- const [mediaType, mediaSubType] = partContentType(type).type.split("/");
2217
- if (mediaType === "application") {
2218
- contentType = parseJsonContentType(type);
2219
- } else {
2220
- contentType = mediaType;
2221
- }
2222
- }
2223
- } else {
2224
- const ctSchema = {
2225
- type: "object"
2226
- };
2227
- patchParameters(spec, ctSchema, operation);
2228
- const serializer = new DartSerializer(spec, (name, content) => {
2229
- inputs[join(`inputs/${name}.dart`)] = `import 'dart:io';import 'dart:typed_data';import '../models/index.dart'; import './index.dart';
2230
-
2231
- ${content}`;
2232
- });
2233
- const serialized = serializer.handle(inputName, ctSchema, true, {
2234
- alias: isObjectSchema(ctSchema) ? void 0 : inputName
976
+ let haveInput = false;
977
+ for (const type in operation.requestBody.content) {
978
+ const objectSchema = resolveRef2(
979
+ spec,
980
+ operation.requestBody.content[type].schema
981
+ );
982
+ const serializer = new DartSerializer(spec);
983
+ inputName = objectSchema["x-inputname"];
984
+ const serialized = serializer.handle(inputName, objectSchema, true, {
985
+ alias: isObjectSchema(objectSchema) ? void 0 : inputName,
986
+ requestize: objectSchema["x-requestbody"] === true,
987
+ noEmit: true
2235
988
  });
2236
989
  encode = serialized.encode;
990
+ const [mediaType, mediaSubType] = partContentType(type).type.split("/");
991
+ if (mediaSubType === "empty") {
992
+ contentType = "empty";
993
+ } else if (mediaSubType === "x-www-form-urlencoded") {
994
+ contentType = "urlencoded";
995
+ } else if (mediaSubType === "octet-stream") {
996
+ contentType = "binary";
997
+ } else if (mediaType === "application") {
998
+ contentType = parseJsonContentType(type);
999
+ } else if (mediaType === "multipart") {
1000
+ contentType = "formdata";
1001
+ } else {
1002
+ contentType = "binary";
1003
+ }
1004
+ if (!isEmpty2({ ...objectSchema.properties, ...objectSchema["x-properties"] })) {
1005
+ haveInput = true;
1006
+ }
1007
+ }
1008
+ if (!inputName) {
1009
+ throw new Error(
1010
+ `No input name found for operation ${operation.operationId} in ${entry.path}`
1011
+ );
2237
1012
  }
2238
- return { inputs, inputName, contentType, encode };
1013
+ return { inputName, contentType, encode, haveInput };
2239
1014
  }
2240
1015
  function toOutput(spec, operation) {
2241
- const outputName = pascalcase2(`${operation.operationId} output`);
2242
1016
  operation.responses ??= {};
2243
- const outputs = {};
2244
1017
  for (const status in operation.responses) {
2245
- const response = isRef4(operation.responses[status]) ? followRef3(spec, operation.responses[status].$ref) : operation.responses[status];
1018
+ if (!isSuccessStatusCode(status))
1019
+ continue;
1020
+ const response = operation.responses[status];
1021
+ const outputName = response["x-response-name"];
1022
+ if ((response.headers ?? {})["Transfer-Encoding"]) {
1023
+ return streamedOutput();
1024
+ }
2246
1025
  for (const type in response.content) {
2247
- const { schema } = response.content[type];
2248
- if (!schema) {
2249
- console.warn(
2250
- `Schema not found for ${type} in ${operation.operationId}`
2251
- );
2252
- continue;
2253
- }
2254
- const serializer = new DartSerializer(spec, (name, content) => {
2255
- });
1026
+ const schema = response.content[type].schema;
2256
1027
  if (isStreamingContentType(type)) {
2257
- return {
2258
- type: "stream",
2259
- outputName,
2260
- outputs,
2261
- decode: `return stream`,
2262
- returnType: `http.StreamedResponse`
2263
- };
1028
+ return streamedOutput();
1029
+ }
1030
+ if (isSseContentType(type)) {
1031
+ continue;
2264
1032
  }
2265
1033
  if (parseJsonContentType(type)) {
1034
+ if (!schema) {
1035
+ return streamedOutput();
1036
+ }
1037
+ const serializer = new DartSerializer(spec);
2266
1038
  const serialized = serializer.handle(outputName, schema, true, {
2267
1039
  // alias: outputName,
2268
1040
  noEmit: true
2269
1041
  });
2270
1042
  return {
2271
1043
  type: "json",
2272
- outputName,
2273
- outputs,
2274
1044
  decode: `final json = await this.receiver.json(stream); return ${serialized.fromJson}`,
2275
1045
  returnType: serialized.use
2276
1046
  };
2277
1047
  }
2278
1048
  }
2279
1049
  }
2280
- return null;
1050
+ return streamedOutput();
1051
+ }
1052
+ function streamedOutput() {
1053
+ return {
1054
+ type: "stream",
1055
+ decode: `return stream`,
1056
+ returnType: `http.StreamedResponse`
1057
+ };
1058
+ }
1059
+ function serializeModels(spec) {
1060
+ const serializer = new DartSerializer(spec);
1061
+ return Object.entries(spec.components.schemas).reduce(
1062
+ (acc, [name, schema]) => {
1063
+ serializer.onEmit((name2, content, schema2) => {
1064
+ const isResponseBody = schema2["x-responsebody"];
1065
+ const isRequestBody = schema2["x-requestbody"];
1066
+ const folder = isRequestBody ? "inputs" : isResponseBody ? "outputs" : "models";
1067
+ acc[join(folder, `${snakecase2(name2)}.dart`)] = [
1068
+ `import 'dart:io';`,
1069
+ `import 'dart:typed_data';`,
1070
+ // `import './index.dart';`,
1071
+ // `import '../interceptors.dart';`,
1072
+ `import '../package.dart';`,
1073
+ // folder === 'inputs' || folder === 'outputs'
1074
+ // ? `import '../models/index.dart';`
1075
+ // : `import '../inputs/index.dart';`,
1076
+ content
1077
+ ].join("\n");
1078
+ });
1079
+ if (isRef2(schema)) {
1080
+ serializer.handle(pascalcase2(name), schema, true, {
1081
+ alias: isRef2(schema) ? name : void 0
1082
+ });
1083
+ return acc;
1084
+ }
1085
+ if (schema["x-requestbody"]) {
1086
+ serializer.handle(
1087
+ pascalcase2(name),
1088
+ schema.type !== "object" ? {
1089
+ type: "object",
1090
+ required: ["$body"],
1091
+ properties: {
1092
+ $body: {
1093
+ ...schema,
1094
+ "x-special": true
1095
+ }
1096
+ }
1097
+ } : schema,
1098
+ true,
1099
+ {
1100
+ requestize: true
1101
+ }
1102
+ );
1103
+ } else {
1104
+ serializer.handle(pascalcase2(name), schema, true, {});
1105
+ }
1106
+ return acc;
1107
+ },
1108
+ {}
1109
+ );
2281
1110
  }
2282
1111
  export {
2283
1112
  generate