@sdk-it/dart 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +216 -96
- package/dist/index.js.map +4 -4
- package/dist/lib/dart-emitter.d.ts.map +1 -1
- package/dist/lib/generate.d.ts.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -2,20 +2,142 @@
|
|
|
2
2
|
import { parse as partContentType } from "fast-content-type-parse";
|
|
3
3
|
import { merge as merge2 } from "lodash-es";
|
|
4
4
|
import assert2 from "node:assert";
|
|
5
|
-
import { writeFile } from "node:fs/promises";
|
|
6
|
-
import { join } from "node:path";
|
|
5
|
+
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
6
|
+
import { join as join2 } from "node:path";
|
|
7
7
|
import { camelcase as camelcase3 } from "stringcase";
|
|
8
8
|
import yaml from "yaml";
|
|
9
|
+
|
|
10
|
+
// packages/core/dist/index.js
|
|
9
11
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
pascalcase as _pascalcase,
|
|
13
|
+
snakecase as _snakecase,
|
|
14
|
+
spinalcase as _spinalcase
|
|
15
|
+
} from "stringcase";
|
|
16
|
+
import ts, { TypeFlags, symbolName } from "typescript";
|
|
17
|
+
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
18
|
+
import { dirname, extname, isAbsolute, join } from "node:path";
|
|
19
|
+
import debug from "debug";
|
|
20
|
+
import ts2 from "typescript";
|
|
21
|
+
import { get } from "lodash-es";
|
|
22
|
+
var deriveSymbol = Symbol.for("serialize");
|
|
23
|
+
var $types = Symbol.for("types");
|
|
24
|
+
async function exist(file) {
|
|
25
|
+
return stat(file).then(() => true).catch(() => false);
|
|
26
|
+
}
|
|
27
|
+
async function writeFiles(dir, contents) {
|
|
28
|
+
await Promise.all(
|
|
29
|
+
Object.entries(contents).map(async ([file, content]) => {
|
|
30
|
+
if (content === null) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const filePath = isAbsolute(file) ? file : join(dir, file);
|
|
34
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
35
|
+
if (typeof content === "string") {
|
|
36
|
+
await writeFile(filePath, content, "utf-8");
|
|
37
|
+
} else {
|
|
38
|
+
if (content.ignoreIfExists) {
|
|
39
|
+
if (!await exist(filePath)) {
|
|
40
|
+
await writeFile(filePath, content.content, "utf-8");
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
await writeFile(filePath, content.content, "utf-8");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
async function getFolderExportsV2(folder, options = {
|
|
50
|
+
extensions: "ts",
|
|
51
|
+
ignore: () => false,
|
|
52
|
+
includeExtension: true,
|
|
53
|
+
exportSyntax: "export * from "
|
|
54
|
+
}) {
|
|
55
|
+
options.includeExtension ??= true;
|
|
56
|
+
if (!await exist(folder)) {
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
const files = await readdir(folder, { withFileTypes: true });
|
|
60
|
+
const exports = [];
|
|
61
|
+
for (const file of files) {
|
|
62
|
+
if (options.ignore?.(file)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (file.isDirectory()) {
|
|
66
|
+
if (await exist(
|
|
67
|
+
`${file.parentPath}/${file.name}/index.${options.extensions}`
|
|
68
|
+
)) {
|
|
69
|
+
exports.push(
|
|
70
|
+
`${options.exportSyntax} './${file.name}/index${options.includeExtension ? `.${options.extensions}` : ""}';`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
} else if (file.name !== `index.${options.extensions}` && options.extensions.includes(getExt(file.name))) {
|
|
74
|
+
exports.push(
|
|
75
|
+
`${options.exportSyntax} './${options.includeExtension ? file.name : file.name.replace(extname(file.name), "")}';`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return exports.join("\n");
|
|
80
|
+
}
|
|
81
|
+
var getExt = (fileName) => {
|
|
82
|
+
if (!fileName) {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
const lastDot = fileName.lastIndexOf(".");
|
|
86
|
+
if (lastDot === -1) {
|
|
87
|
+
return "";
|
|
88
|
+
}
|
|
89
|
+
const ext = fileName.slice(lastDot + 1).split("/").filter(Boolean).join("");
|
|
90
|
+
if (ext === fileName) {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
return ext || "txt";
|
|
94
|
+
};
|
|
95
|
+
var logger = debug("january:client");
|
|
96
|
+
function isRef(obj) {
|
|
97
|
+
return obj && "$ref" in obj;
|
|
98
|
+
}
|
|
99
|
+
function notRef(obj) {
|
|
100
|
+
return !isRef(obj);
|
|
101
|
+
}
|
|
102
|
+
function cleanRef(ref) {
|
|
103
|
+
return ref.replace(/^#\//, "");
|
|
104
|
+
}
|
|
105
|
+
function parseRef(ref) {
|
|
106
|
+
const parts = ref.split("/");
|
|
107
|
+
const [model] = parts.splice(-1);
|
|
108
|
+
const [namespace] = parts.splice(-1);
|
|
109
|
+
return {
|
|
110
|
+
model,
|
|
111
|
+
namespace,
|
|
112
|
+
path: cleanRef(parts.join("/"))
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function followRef(spec, ref) {
|
|
116
|
+
const pathParts = cleanRef(ref).split("/");
|
|
117
|
+
const entry = get(spec, pathParts);
|
|
118
|
+
if (entry && "$ref" in entry) {
|
|
119
|
+
return followRef(spec, entry.$ref);
|
|
120
|
+
}
|
|
121
|
+
return entry;
|
|
122
|
+
}
|
|
123
|
+
function isEmpty(value) {
|
|
124
|
+
if (value === null || value === void 0 || value === "") {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (typeof value === "object" && Object.keys(value).length === 0) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
function pascalcase(value) {
|
|
136
|
+
return _pascalcase(value.split("/").join(" "));
|
|
137
|
+
}
|
|
138
|
+
function snakecase(value) {
|
|
139
|
+
return _snakecase(value.split("/").join(" "));
|
|
140
|
+
}
|
|
19
141
|
|
|
20
142
|
// packages/spec/dist/lib/loaders/local-loader.js
|
|
21
143
|
import { parse } from "yaml";
|
|
@@ -312,16 +434,7 @@ function isSuccessStatusCode(statusCode) {
|
|
|
312
434
|
// packages/dart/src/lib/dart-emitter.ts
|
|
313
435
|
import { merge } from "lodash-es";
|
|
314
436
|
import assert from "node:assert";
|
|
315
|
-
import { camelcase as camelcase2, snakecase } from "stringcase";
|
|
316
|
-
import {
|
|
317
|
-
cleanRef,
|
|
318
|
-
followRef,
|
|
319
|
-
isEmpty,
|
|
320
|
-
isRef,
|
|
321
|
-
notRef,
|
|
322
|
-
parseRef,
|
|
323
|
-
pascalcase
|
|
324
|
-
} from "@sdk-it/core";
|
|
437
|
+
import { camelcase as camelcase2, snakecase as snakecase2 } from "stringcase";
|
|
325
438
|
var formatName = (it) => {
|
|
326
439
|
const startsWithDigitPattern = /^-?\d/;
|
|
327
440
|
if (typeof it === "number") {
|
|
@@ -349,9 +462,9 @@ var formatName = (it) => {
|
|
|
349
462
|
if (nameToFormat.endsWith("]")) {
|
|
350
463
|
nameToFormat = nameToFormat.slice(0, -1);
|
|
351
464
|
}
|
|
352
|
-
return
|
|
465
|
+
return snakecase2(nameToFormat);
|
|
353
466
|
}
|
|
354
|
-
return
|
|
467
|
+
return snakecase2(String(it));
|
|
355
468
|
};
|
|
356
469
|
var DartSerializer = class {
|
|
357
470
|
#spec;
|
|
@@ -454,12 +567,12 @@ var DartSerializer = class {
|
|
|
454
567
|
if (required) {
|
|
455
568
|
matches.push(`(
|
|
456
569
|
json.containsKey('${camelcase2(propName)}')
|
|
457
|
-
? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`}
|
|
570
|
+
? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`} ${typeStr.matches ? `&& ${typeStr.matches}` : ""}
|
|
458
571
|
: false)`);
|
|
459
572
|
} else {
|
|
460
573
|
matches.push(`(
|
|
461
574
|
json.containsKey('${camelcase2(propName)}')
|
|
462
|
-
? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`}
|
|
575
|
+
? ${nullable2 ? `json['${propName}'] == null` : `json['${propName}'] != null`} ${typeStr.matches ? `|| ${typeStr.matches}` : ""}
|
|
463
576
|
: true)`);
|
|
464
577
|
}
|
|
465
578
|
}
|
|
@@ -496,18 +609,17 @@ return ${matches.join(" && ")};
|
|
|
496
609
|
return required ? `this.${camelcase2(accces)}.toJson()` : `this.${camelcase2(accces)} != null ? this.${camelcase2(accces)}!.toJson() : null`;
|
|
497
610
|
}
|
|
498
611
|
#array(className, schema, required = false, context) {
|
|
499
|
-
let serialized;
|
|
500
612
|
if (!schema.items) {
|
|
501
|
-
|
|
613
|
+
return {
|
|
502
614
|
content: "",
|
|
503
615
|
use: "List<dynamic>",
|
|
504
616
|
toJson: "",
|
|
505
617
|
fromJson: `List<dynamic>.from(${context.name ? `json['${context.name}']` : `json`})})`,
|
|
506
618
|
matches: ""
|
|
507
619
|
};
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
|
|
620
|
+
}
|
|
621
|
+
const itemsType = this.handle(className, schema.items, true, context);
|
|
622
|
+
const fromJson = required ? context.name ? `(json['${context.name}'] as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
|
|
511
623
|
.map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
|
|
512
624
|
.toList()` : `(json as List<${itemsType.simple ? itemsType.use : "dynamic"}>)
|
|
513
625
|
.map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
|
|
@@ -520,16 +632,14 @@ return ${matches.join(" && ")};
|
|
|
520
632
|
.map((it) => ${itemsType.simple ? "it" : `${itemsType.use}.fromJson(it)`})
|
|
521
633
|
.toList()
|
|
522
634
|
: null`;
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
return serialized;
|
|
635
|
+
return {
|
|
636
|
+
encode: `input.map((it) => ${itemsType.simple ? "it" : `it.toJson()`}).toList()`,
|
|
637
|
+
content: "",
|
|
638
|
+
use: `List<${itemsType.use}>`,
|
|
639
|
+
fromJson,
|
|
640
|
+
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`}`,
|
|
641
|
+
matches: `json['${camelcase2(context.name)}'].every((it) => ${itemsType.matches})`
|
|
642
|
+
};
|
|
533
643
|
}
|
|
534
644
|
/**
|
|
535
645
|
* Convert a basic type to Dart
|
|
@@ -564,6 +674,7 @@ return ${matches.join(" && ")};
|
|
|
564
674
|
return {
|
|
565
675
|
content: "",
|
|
566
676
|
use: "dynamic",
|
|
677
|
+
nullable: false,
|
|
567
678
|
toJson: `${camelcase2(context.name)}`,
|
|
568
679
|
fromJson: `json['${context.name}']`
|
|
569
680
|
};
|
|
@@ -603,6 +714,7 @@ return ${matches.join(" && ")};
|
|
|
603
714
|
if (schemas.length === 0) {
|
|
604
715
|
return {
|
|
605
716
|
content: "",
|
|
717
|
+
nullable: false,
|
|
606
718
|
use: "dynamic",
|
|
607
719
|
toJson: `${camelcase2(context.name)}`,
|
|
608
720
|
fromJson: `json['${context.name}']`
|
|
@@ -640,6 +752,7 @@ return ${matches.join(" && ")};
|
|
|
640
752
|
if (schemas.length === 0) {
|
|
641
753
|
return {
|
|
642
754
|
content: "",
|
|
755
|
+
nullable: false,
|
|
643
756
|
use: "dynamic",
|
|
644
757
|
toJson: `${camelcase2(context.name)}`,
|
|
645
758
|
fromJson: `json['${context.name}']`
|
|
@@ -906,12 +1019,12 @@ return false;
|
|
|
906
1019
|
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
907
1020
|
return this.#allOf(className, schema.allOf, context);
|
|
908
1021
|
}
|
|
909
|
-
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
910
|
-
return this.#anyOf(className, schema.anyOf, context);
|
|
911
|
-
}
|
|
912
1022
|
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
913
1023
|
return this.#oneOf(className, schema.oneOf, context);
|
|
914
1024
|
}
|
|
1025
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
1026
|
+
return this.#anyOf(className, schema.anyOf, context);
|
|
1027
|
+
}
|
|
915
1028
|
if (schema.enum && Array.isArray(schema.enum)) {
|
|
916
1029
|
return this.#enum(className, schema, context);
|
|
917
1030
|
}
|
|
@@ -935,7 +1048,10 @@ return false;
|
|
|
935
1048
|
content: "",
|
|
936
1049
|
use: "dynamic",
|
|
937
1050
|
toJson: `${camelcase2(context.name)}`,
|
|
938
|
-
fromJson: `json['${context.name}']
|
|
1051
|
+
fromJson: `json['${context.name}']`,
|
|
1052
|
+
nullable: false,
|
|
1053
|
+
matches: ""
|
|
1054
|
+
// keep it empty as 'type is dynamic' is always true
|
|
939
1055
|
};
|
|
940
1056
|
}
|
|
941
1057
|
return this.#primitive(
|
|
@@ -965,39 +1081,45 @@ function isObjectSchema(schema) {
|
|
|
965
1081
|
}
|
|
966
1082
|
|
|
967
1083
|
// packages/dart/src/lib/http/dispatcher.txt
|
|
968
|
-
var dispatcher_default = "import '
|
|
1084
|
+
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";
|
|
969
1085
|
|
|
970
1086
|
// packages/dart/src/lib/http/interceptors.txt
|
|
971
1087
|
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 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";
|
|
972
1088
|
|
|
1089
|
+
// packages/dart/src/lib/http/responses.txt
|
|
1090
|
+
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";
|
|
1091
|
+
|
|
973
1092
|
// packages/dart/src/lib/generate.ts
|
|
974
1093
|
function tuneSpec(spec, schemas, refs) {
|
|
975
1094
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
976
|
-
if (
|
|
1095
|
+
if (isRef(schema))
|
|
977
1096
|
continue;
|
|
978
|
-
if (
|
|
1097
|
+
if (!isEmpty(schema.anyOf) && !isEmpty(schema.oneOf)) {
|
|
1098
|
+
delete schema.anyOf;
|
|
1099
|
+
}
|
|
1100
|
+
if (!isEmpty(schema.allOf)) {
|
|
979
1101
|
const schemas2 = schema.allOf;
|
|
980
|
-
const refs2 = schemas2.filter(
|
|
981
|
-
const nonRefs = schemas2.filter(
|
|
1102
|
+
const refs2 = schemas2.filter(isRef);
|
|
1103
|
+
const nonRefs = schemas2.filter(notRef);
|
|
982
1104
|
if (nonRefs.some((it) => it.type && it.type !== "object")) {
|
|
983
1105
|
assert2(false, `allOf ${name} must be an object`);
|
|
984
1106
|
}
|
|
985
1107
|
const objectSchema = merge2(
|
|
986
1108
|
{},
|
|
987
1109
|
...nonRefs,
|
|
988
|
-
...refs2.map((ref) =>
|
|
1110
|
+
...refs2.map((ref) => followRef(spec, ref.$ref))
|
|
989
1111
|
);
|
|
990
1112
|
delete objectSchema.allOf;
|
|
991
1113
|
delete schema.allOf;
|
|
992
1114
|
Object.assign(schema, objectSchema);
|
|
993
1115
|
}
|
|
994
1116
|
if (schema.type === "object") {
|
|
995
|
-
if (!
|
|
1117
|
+
if (!isEmpty(schema.oneOf)) {
|
|
996
1118
|
for (const oneOfIdx in schema.oneOf) {
|
|
997
1119
|
const oneOf = schema.oneOf[oneOfIdx];
|
|
998
|
-
if (
|
|
1120
|
+
if (isRef(oneOf))
|
|
999
1121
|
continue;
|
|
1000
|
-
if (!
|
|
1122
|
+
if (!isEmpty(oneOf.required) && schema.properties) {
|
|
1001
1123
|
schema.oneOf[oneOfIdx] = schema.properties[oneOf.required[0]];
|
|
1002
1124
|
}
|
|
1003
1125
|
}
|
|
@@ -1007,22 +1129,22 @@ function tuneSpec(spec, schemas, refs) {
|
|
|
1007
1129
|
}
|
|
1008
1130
|
schema.properties ??= {};
|
|
1009
1131
|
for (const [propName, value] of Object.entries(schema.properties)) {
|
|
1010
|
-
if (
|
|
1132
|
+
if (isRef(value))
|
|
1011
1133
|
continue;
|
|
1012
|
-
const refName =
|
|
1134
|
+
const refName = pascalcase(`${name} ${propName.replace("[]", "")}`);
|
|
1013
1135
|
refs.push({ name: refName, value });
|
|
1014
1136
|
schema.properties[propName] = {
|
|
1015
1137
|
$ref: `#/components/schemas/${refName}`
|
|
1016
1138
|
};
|
|
1017
1139
|
const props = Object.fromEntries(
|
|
1018
1140
|
Object.entries(value.properties ?? {}).map(([key, value2]) => {
|
|
1019
|
-
return [
|
|
1141
|
+
return [pascalcase(`${refName} ${key}`), value2];
|
|
1020
1142
|
})
|
|
1021
1143
|
);
|
|
1022
1144
|
tuneSpec(spec, props, refs);
|
|
1023
1145
|
}
|
|
1024
1146
|
} else if (schema.type === "array") {
|
|
1025
|
-
if (
|
|
1147
|
+
if (isRef(schema.items))
|
|
1026
1148
|
continue;
|
|
1027
1149
|
const refName = name;
|
|
1028
1150
|
refs.push({ name: refName, value: schema.items ?? {} });
|
|
@@ -1034,7 +1156,7 @@ function tuneSpec(spec, schemas, refs) {
|
|
|
1034
1156
|
}
|
|
1035
1157
|
async function generate(spec, settings) {
|
|
1036
1158
|
const clientName = settings.name || "Client";
|
|
1037
|
-
const output =
|
|
1159
|
+
const output = join2(settings.output, "lib");
|
|
1038
1160
|
const groups = {};
|
|
1039
1161
|
spec.components ??= {};
|
|
1040
1162
|
spec.components.schemas ??= {};
|
|
@@ -1042,19 +1164,19 @@ async function generate(spec, settings) {
|
|
|
1042
1164
|
const outputs = {};
|
|
1043
1165
|
forEachOperation({ spec }, (entry, operation) => {
|
|
1044
1166
|
operation.responses ??= {};
|
|
1167
|
+
spec.components ??= {};
|
|
1168
|
+
spec.components.schemas ??= {};
|
|
1045
1169
|
for (const status in operation.responses) {
|
|
1046
1170
|
if (!isSuccessStatusCode(status))
|
|
1047
1171
|
continue;
|
|
1048
|
-
const response2 =
|
|
1049
|
-
if (
|
|
1172
|
+
const response2 = isRef(operation.responses[status]) ? followRef(spec, operation.responses[status].$ref) : operation.responses[status];
|
|
1173
|
+
if (!isEmpty(response2.content)) {
|
|
1050
1174
|
for (const [contentType, mediaType] of Object.entries(
|
|
1051
1175
|
response2.content
|
|
1052
1176
|
)) {
|
|
1053
1177
|
if (parseJsonContentType(contentType)) {
|
|
1054
|
-
if (mediaType.schema && !
|
|
1055
|
-
|
|
1056
|
-
spec.components.schemas ??= {};
|
|
1057
|
-
const outputName = pascalcase2(`${operation.operationId} output`);
|
|
1178
|
+
if (mediaType.schema && !isRef(mediaType.schema)) {
|
|
1179
|
+
const outputName = pascalcase(`${operation.operationId} output`);
|
|
1058
1180
|
spec.components.schemas[outputName] = mediaType.schema;
|
|
1059
1181
|
operation.responses[status].content[contentType].schema = {
|
|
1060
1182
|
$ref: `#/components/schemas/${outputName}`
|
|
@@ -1067,7 +1189,7 @@ async function generate(spec, settings) {
|
|
|
1067
1189
|
console.log(`Processing ${entry.method} ${entry.path}`);
|
|
1068
1190
|
const group = groups[entry.groupName] ?? (groups[entry.groupName] = {
|
|
1069
1191
|
methods: [],
|
|
1070
|
-
use: `final ${entry.groupName} = new ${
|
|
1192
|
+
use: `final ${entry.groupName} = new ${pascalcase(entry.groupName)}();`
|
|
1071
1193
|
});
|
|
1072
1194
|
const input = toInputs(spec, { entry, operation });
|
|
1073
1195
|
Object.assign(inputs, input.inputs);
|
|
@@ -1077,7 +1199,7 @@ async function generate(spec, settings) {
|
|
|
1077
1199
|
}
|
|
1078
1200
|
group.methods.push(`
|
|
1079
1201
|
Future<${response ? response.returnType : "http.StreamedResponse"}> ${camelcase3(operation.operationId)}(
|
|
1080
|
-
${
|
|
1202
|
+
${isEmpty(operation.requestBody) ? "" : `${input.inputName} input`}
|
|
1081
1203
|
) async {
|
|
1082
1204
|
final stream = await this.dispatcher.${input.contentType}(RequestConfig(
|
|
1083
1205
|
method: '${entry.method}',
|
|
@@ -1093,24 +1215,24 @@ async function generate(spec, settings) {
|
|
|
1093
1215
|
for (const ref of newRefs) {
|
|
1094
1216
|
spec.components.schemas[ref.name] = ref.value;
|
|
1095
1217
|
}
|
|
1096
|
-
await
|
|
1097
|
-
|
|
1218
|
+
await writeFile2(
|
|
1219
|
+
join2(process.cwd(), "openai.json"),
|
|
1098
1220
|
JSON.stringify(spec, null, 2)
|
|
1099
1221
|
);
|
|
1100
1222
|
const models = Object.entries(spec.components.schemas).reduce((acc, [name, schema]) => {
|
|
1101
1223
|
const serializer = new DartSerializer(spec, (name2, content) => {
|
|
1102
|
-
acc[`models/${
|
|
1224
|
+
acc[`models/${snakecase(name2)}.dart`] = `import 'dart:io';import 'dart:typed_data'; import './index.dart';
|
|
1103
1225
|
|
|
1104
1226
|
${content}`;
|
|
1105
1227
|
});
|
|
1106
|
-
serializer.handle(
|
|
1228
|
+
serializer.handle(pascalcase(name), schema);
|
|
1107
1229
|
return acc;
|
|
1108
1230
|
}, {});
|
|
1109
1231
|
const clazzez = Object.entries(groups).reduce(
|
|
1110
1232
|
(acc, [name, { methods }]) => {
|
|
1111
1233
|
return {
|
|
1112
1234
|
...acc,
|
|
1113
|
-
[`api/${
|
|
1235
|
+
[`api/${snakecase(name)}.dart`]: `
|
|
1114
1236
|
import 'dart:convert';
|
|
1115
1237
|
|
|
1116
1238
|
import 'package:http/http.dart' as http;
|
|
@@ -1121,9 +1243,10 @@ import '../outputs/index.dart';
|
|
|
1121
1243
|
import '../models/index.dart';
|
|
1122
1244
|
import '../http.dart';
|
|
1123
1245
|
|
|
1124
|
-
class ${
|
|
1246
|
+
class ${pascalcase(name)}Client {
|
|
1125
1247
|
final Dispatcher dispatcher;
|
|
1126
|
-
|
|
1248
|
+
final Receiver receiver;
|
|
1249
|
+
${pascalcase(name)}Client(this.dispatcher, this.receiver);
|
|
1127
1250
|
${methods.join("\n")}
|
|
1128
1251
|
}
|
|
1129
1252
|
`
|
|
@@ -1132,19 +1255,20 @@ import '../http.dart';
|
|
|
1132
1255
|
{}
|
|
1133
1256
|
);
|
|
1134
1257
|
const client = `
|
|
1135
|
-
${Object.keys(groups).map((name) => `import './api/${
|
|
1258
|
+
${Object.keys(groups).map((name) => `import './api/${snakecase(name)}.dart';`).join("\n")}
|
|
1136
1259
|
import './interceptors.dart';
|
|
1137
1260
|
import './http.dart';
|
|
1138
1261
|
|
|
1139
1262
|
class ${clientName} {
|
|
1140
1263
|
final Options options;
|
|
1141
|
-
${Object.keys(groups).map((name) => `late final ${
|
|
1264
|
+
${Object.keys(groups).map((name) => `late final ${pascalcase(name)}Client ${camelcase3(name)};`).join("\n")}
|
|
1142
1265
|
|
|
1143
1266
|
${clientName}(this.options) {
|
|
1144
|
-
final interceptors = [
|
|
1145
|
-
final dispatcher =
|
|
1267
|
+
final interceptors = [BaseUrlInterceptor(() => this.options.baseUrl)];
|
|
1268
|
+
final dispatcher = Dispatcher(interceptors);
|
|
1269
|
+
final receiver = Receiver(interceptors);
|
|
1146
1270
|
${Object.keys(groups).map(
|
|
1147
|
-
(name) => `this.${camelcase3(name)} =
|
|
1271
|
+
(name) => `this.${camelcase3(name)} = ${pascalcase(name)}Client(dispatcher, receiver);`
|
|
1148
1272
|
).join("\n")}
|
|
1149
1273
|
|
|
1150
1274
|
}
|
|
@@ -1169,24 +1293,25 @@ class Options {
|
|
|
1169
1293
|
...outputs
|
|
1170
1294
|
});
|
|
1171
1295
|
await writeFiles(output, {
|
|
1172
|
-
"models/index.dart": await getFolderExportsV2(
|
|
1296
|
+
"models/index.dart": await getFolderExportsV2(join2(output, "models"), {
|
|
1173
1297
|
exportSyntax: "export",
|
|
1174
1298
|
extensions: "dart"
|
|
1175
1299
|
}),
|
|
1176
|
-
"inputs/index.dart": await getFolderExportsV2(
|
|
1300
|
+
"inputs/index.dart": await getFolderExportsV2(join2(output, "inputs"), {
|
|
1177
1301
|
exportSyntax: "export",
|
|
1178
1302
|
extensions: "dart"
|
|
1179
1303
|
}),
|
|
1180
|
-
"outputs/index.dart": await getFolderExportsV2(
|
|
1304
|
+
"outputs/index.dart": await getFolderExportsV2(join2(output, "outputs"), {
|
|
1181
1305
|
exportSyntax: "export",
|
|
1182
1306
|
extensions: "dart"
|
|
1183
1307
|
}),
|
|
1184
1308
|
"interceptors.dart": interceptors_default,
|
|
1185
1309
|
"http.dart": dispatcher_default,
|
|
1310
|
+
"responses.dart": responses_default,
|
|
1186
1311
|
...clazzez
|
|
1187
1312
|
});
|
|
1188
1313
|
await writeFiles(output, {
|
|
1189
|
-
"package.dart": `${await getFolderExportsV2(
|
|
1314
|
+
"package.dart": `${await getFolderExportsV2(join2(output), {
|
|
1190
1315
|
exportSyntax: "export",
|
|
1191
1316
|
extensions: "dart",
|
|
1192
1317
|
ignore(dirent) {
|
|
@@ -1198,7 +1323,7 @@ class Options {
|
|
|
1198
1323
|
"pubspec.yaml": {
|
|
1199
1324
|
ignoreIfExists: true,
|
|
1200
1325
|
content: yaml.stringify({
|
|
1201
|
-
name: settings.name ? `${
|
|
1326
|
+
name: settings.name ? `${snakecase(clientName.toLowerCase())}_sdk` : "sdk",
|
|
1202
1327
|
version: "0.0.1",
|
|
1203
1328
|
environment: {
|
|
1204
1329
|
sdk: "^3.7.2"
|
|
@@ -1216,13 +1341,13 @@ class Options {
|
|
|
1216
1341
|
}
|
|
1217
1342
|
function toInputs(spec, { entry, operation }) {
|
|
1218
1343
|
const inputs = {};
|
|
1219
|
-
const inputName =
|
|
1344
|
+
const inputName = pascalcase(`${operation.operationId} input`);
|
|
1220
1345
|
let contentType = "empty";
|
|
1221
1346
|
let encode = "";
|
|
1222
|
-
if (!
|
|
1223
|
-
const requestBody =
|
|
1347
|
+
if (!isEmpty(operation.requestBody)) {
|
|
1348
|
+
const requestBody = isRef(operation.requestBody) ? followRef(spec, operation.requestBody.$ref) : operation.requestBody;
|
|
1224
1349
|
for (const type in requestBody.content) {
|
|
1225
|
-
const ctSchema =
|
|
1350
|
+
const ctSchema = isRef(requestBody.content[type].schema) ? followRef(spec, requestBody.content[type].schema.$ref) : requestBody.content[type].schema;
|
|
1226
1351
|
if (!ctSchema) {
|
|
1227
1352
|
console.warn(
|
|
1228
1353
|
`Schema not found for ${type} in ${entry.method} ${entry.path}`
|
|
@@ -1230,7 +1355,7 @@ function toInputs(spec, { entry, operation }) {
|
|
|
1230
1355
|
continue;
|
|
1231
1356
|
}
|
|
1232
1357
|
const serializer = new DartSerializer(spec, (name, content) => {
|
|
1233
|
-
inputs[
|
|
1358
|
+
inputs[join2(`inputs/${name}.dart`)] = `import 'dart:io';import 'dart:typed_data';import '../models/index.dart'; import './index.dart';
|
|
1234
1359
|
|
|
1235
1360
|
${content}`;
|
|
1236
1361
|
});
|
|
@@ -1238,11 +1363,6 @@ ${content}`;
|
|
|
1238
1363
|
alias: isObjectSchema(ctSchema) ? void 0 : inputName
|
|
1239
1364
|
});
|
|
1240
1365
|
encode = serialized.encode;
|
|
1241
|
-
if (contentType) {
|
|
1242
|
-
console.warn(
|
|
1243
|
-
`${entry.method} ${entry.path} have more than one content type`
|
|
1244
|
-
);
|
|
1245
|
-
}
|
|
1246
1366
|
const [mediaType, mediaSubType] = partContentType(type).type.split("/");
|
|
1247
1367
|
if (mediaType === "application") {
|
|
1248
1368
|
contentType = parseJsonContentType(type);
|
|
@@ -1254,11 +1374,11 @@ ${content}`;
|
|
|
1254
1374
|
return { inputs, inputName, contentType, encode };
|
|
1255
1375
|
}
|
|
1256
1376
|
function toOutput(spec, operation) {
|
|
1257
|
-
const outputName =
|
|
1377
|
+
const outputName = pascalcase(`${operation.operationId} output`);
|
|
1258
1378
|
operation.responses ??= {};
|
|
1259
1379
|
const outputs = {};
|
|
1260
1380
|
for (const status in operation.responses) {
|
|
1261
|
-
const response =
|
|
1381
|
+
const response = isRef(operation.responses[status]) ? followRef(spec, operation.responses[status].$ref) : operation.responses[status];
|
|
1262
1382
|
for (const type in response.content) {
|
|
1263
1383
|
const { schema } = response.content[type];
|
|
1264
1384
|
if (!schema) {
|
|
@@ -1287,7 +1407,7 @@ function toOutput(spec, operation) {
|
|
|
1287
1407
|
type: "json",
|
|
1288
1408
|
outputName,
|
|
1289
1409
|
outputs,
|
|
1290
|
-
decode: `final
|
|
1410
|
+
decode: `final json = await this.receiver.json(stream); return ${serialized.fromJson}`,
|
|
1291
1411
|
returnType: serialized.use
|
|
1292
1412
|
};
|
|
1293
1413
|
}
|