@arrirpc/codegen-dart 0.49.1 → 0.51.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs CHANGED
@@ -1,1021 +1,1223 @@
1
- import { defineClientGeneratorPlugin, unflattenProcedures, isRpcDefinition, isServiceDefinition, pascalCase, isSchemaFormProperties, isSchemaFormDiscriminator, isSchemaFormValues, isSchemaFormType, isSchemaFormElements, isSchemaFormEnum, isSchemaFormRef, camelCase } from '@arrirpc/codegen-utils';
2
- import { a } from '@arrirpc/schema';
3
- import { execSync } from 'child_process';
4
- import { writeFileSync } from 'fs';
5
-
6
- var __defProp = Object.defineProperty;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
- var __publicField = (obj, key, value) => {
9
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
10
- return value;
11
- };
12
- function camelCaseWrapper(input) {
13
- if (input.toUpperCase() === input) {
14
- return camelCase(input, { normalize: true });
1
+ import { execSync } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import { camelCase, pascalCase, removeDisallowedChars, isServiceDefinition, isRpcDefinition, defineGeneratorPlugin, unflattenProcedures, isSchemaFormType, isSchemaFormEnum, isSchemaFormProperties, isSchemaFormElements, isSchemaFormValues, isSchemaFormDiscriminator, isSchemaFormRef } from '@arrirpc/codegen-utils';
4
+ import path from 'pathe';
5
+
6
+ function outputIsNullable(schema, context) {
7
+ if (schema.nullable) {
8
+ return true;
15
9
  }
16
- return camelCase(input);
10
+ if (context.isOptional) {
11
+ return true;
12
+ }
13
+ return false;
17
14
  }
18
- const dartClientGenerator = defineClientGeneratorPlugin(
19
- (options) => {
20
- return {
21
- generator: async (def) => {
22
- if (!options.clientName) {
23
- throw new Error(
24
- 'Missing "clientName" cannot generate dart client'
25
- );
26
- }
27
- if (!options.outputFile) {
28
- throw new Error(
29
- 'Missing "outputFile" cannot generate dart client'
30
- );
31
- }
32
- const numProcedures = Object.keys(def.procedures).length;
33
- if (numProcedures <= 0) {
34
- console.warn(
35
- "No procedures found in definition file. Dart client will not be generated"
36
- );
37
- }
38
- const result = createDartClient(def, options);
39
- writeFileSync(options.outputFile, result);
40
- try {
41
- execSync(`dart format ${options.outputFile}`);
42
- } catch (err) {
43
- console.error("Error formatting dart client", err);
44
- }
45
- },
46
- options
47
- };
15
+ const reservedIdentifierKeywords = {
16
+ abstract: 2,
17
+ as: 2,
18
+ assert: 0,
19
+ async: 3,
20
+ await: 1,
21
+ base: 3,
22
+ break: 0,
23
+ case: 0,
24
+ catch: 0,
25
+ class: 0,
26
+ const: 0,
27
+ continue: 0,
28
+ covariant: 2,
29
+ default: 0,
30
+ deferred: 2,
31
+ do: 0,
32
+ dynamic: 2,
33
+ else: 0,
34
+ enum: 0,
35
+ export: 2,
36
+ extends: 0,
37
+ extension: 2,
38
+ external: 2,
39
+ factory: 2,
40
+ false: 0,
41
+ final: 0,
42
+ finally: 0,
43
+ for: 0,
44
+ Function: 2,
45
+ get: 2,
46
+ hide: 3,
47
+ if: 0,
48
+ implements: 2,
49
+ import: 2,
50
+ in: 0,
51
+ interface: 2,
52
+ is: 0,
53
+ late: 2,
54
+ library: 2,
55
+ mixin: 2,
56
+ new: 0,
57
+ null: 0,
58
+ of: 3,
59
+ on: 3,
60
+ operator: 2,
61
+ part: 2,
62
+ required: 2,
63
+ rethrow: 0,
64
+ return: 0,
65
+ sealed: 3,
66
+ set: 2,
67
+ show: 3,
68
+ static: 2,
69
+ super: 0,
70
+ switch: 0,
71
+ sync: 3,
72
+ this: 0,
73
+ throw: 0,
74
+ true: 0,
75
+ try: 0,
76
+ type: 2,
77
+ typedef: 2,
78
+ var: 0,
79
+ void: 0,
80
+ when: 3,
81
+ with: 0,
82
+ while: 0,
83
+ yield: 1
84
+ };
85
+ function canUseIdentifier(input) {
86
+ const reservedId = reservedIdentifierKeywords[input];
87
+ if (typeof reservedId === "undefined") {
88
+ return true;
48
89
  }
49
- );
50
- class DartClientGenerator {
51
- constructor() {
52
- __publicField(this, "generatedModels", []);
90
+ switch (reservedId) {
91
+ case 0:
92
+ return false;
93
+ case 1:
94
+ return true;
95
+ case 2:
96
+ return true;
97
+ case 3:
98
+ return true;
99
+ default:
100
+ throw new Error("Unhandled case");
53
101
  }
54
102
  }
55
- function getAnnotations(metadata) {
56
- const commentParts = [];
57
- if (metadata?.description?.length) {
58
- const parts = metadata.description.split("\n");
59
- for (const part of parts) {
60
- commentParts.push(`/// ${part}`);
61
- }
103
+ function sanitizeIdentifier(input) {
104
+ const bannedCharacters = "!@#$%^&*()-=+[{}]\\|/?.><,;`~";
105
+ const result = removeDisallowedChars(input, bannedCharacters);
106
+ const numbers = "0123456789";
107
+ if (numbers.includes(result.charAt(0))) {
108
+ return `k_${result}`;
62
109
  }
63
- if (metadata?.isDeprecated) {
64
- commentParts.push("@deprecated");
110
+ return result;
111
+ }
112
+ function canUseClassName(input) {
113
+ const reservedId = reservedIdentifierKeywords[input];
114
+ if (typeof reservedId === "undefined") {
115
+ return true;
65
116
  }
66
- if (commentParts.length === 0) {
67
- return "";
117
+ switch (reservedId) {
118
+ case 0:
119
+ return false;
120
+ case 1:
121
+ return false;
122
+ case 2:
123
+ return false;
124
+ case 3:
125
+ return true;
126
+ default:
127
+ throw new Error("Unhandled case");
68
128
  }
69
- return `${commentParts.join("\n")}
70
- `;
71
129
  }
72
- function createDartClient(def, opts) {
73
- const existingClassNames = [];
74
- const clientVersion = def.info?.version ?? "";
75
- const services = unflattenProcedures(def.procedures);
76
- const rpcParts = [];
77
- const serviceGetterParts = [];
78
- const serviceParts = [];
79
- const modelParts = [];
80
- for (const key of Object.keys(services)) {
81
- const item = services[key];
82
- if (isRpcDefinition(item)) {
83
- const rpc = dartRpcFromDefinition(key, item);
84
- if (rpc) {
85
- rpcParts.push(rpc);
86
- }
87
- continue;
88
- }
89
- if (isServiceDefinition(item)) {
90
- const serviceName = pascalCase(`${opts.clientName}_${key}`);
91
- const service = dartServiceFromDefinition(serviceName, item, {
92
- versionNumber: clientVersion,
93
- ...opts
94
- });
95
- serviceParts.push(service);
96
- serviceGetterParts.push(`${serviceName}Service get ${key} {
97
- return ${serviceName}Service(
98
- httpClient: _httpClient,
99
- baseUrl: _baseUrl,
100
- headers: _headers,
130
+ function validDartIdentifier(input) {
131
+ const finalIdentifier = sanitizeIdentifier(
132
+ camelCase(input, { normalize: true })
101
133
  );
102
- }`);
103
- }
104
- }
105
- for (const key of Object.keys(def.definitions)) {
106
- const item = def.definitions[key];
107
- if (isSchemaFormProperties(item) || isSchemaFormDiscriminator(item) || isSchemaFormValues(item)) {
108
- const result = dartTypeFromJtdSchema(key, item, {
109
- isOptional: false,
110
- existingClassNames
111
- });
112
- modelParts.push(result.content);
113
- }
114
- }
115
- if (rpcParts.length === 0 && serviceGetterParts.length === 0) {
116
- return `// this file was autogenerated by arri
117
- // ignore_for_file: type=lint
118
- import "package:arri_client/arri_client.dart";
119
-
120
- ${modelParts.join("\n")}`;
134
+ if (!canUseIdentifier(finalIdentifier)) {
135
+ return `k_${finalIdentifier}`;
121
136
  }
122
- return `// this file was autogenerated by arri
123
- // ignore_for_file: type=lint, unused_field
124
- import "dart:async";
125
- import "dart:convert";
126
- import "package:arri_client/arri_client.dart";
127
- import "package:http/http.dart" as http;
128
-
129
- class ${opts.clientName} {
130
- final http.Client? _httpClient;
131
- final String _baseUrl;
132
- final String _clientVersion = "${def.info?.version ?? ""}";
133
- late final FutureOr<Map<String, String>> Function()? _headers;
134
- ${opts.clientName}({
135
- http.Client? httpClient,
136
- String baseUrl = "",
137
- FutureOr<Map<String, String>> Function()? headers,
138
- }) : _httpClient = httpClient,
139
- _baseUrl = baseUrl,
140
- _headers = headers;
141
- ${rpcParts.join("\n ")}
142
- ${serviceGetterParts.join("\n ")}
137
+ return finalIdentifier;
143
138
  }
144
-
145
- ${serviceParts.join("\n")}
146
-
147
- ${modelParts.join("\n")}
148
- `;
149
- }
150
- function dartServiceFromDefinition(name, def, opts) {
151
- const rpcParts = [];
152
- const subServiceParts = [];
153
- const serviceName = `${name}`;
154
- Object.keys(def).forEach((key) => {
155
- const item = def[key];
156
- if (isRpcDefinition(item)) {
157
- const rpc = dartRpcFromDefinition(key, item);
158
- if (rpc) {
159
- rpcParts.push(rpc);
160
- }
161
- return;
162
- }
163
- if (isServiceDefinition(item)) {
164
- const subServiceName = pascalCase(`${serviceName}_${key}`);
165
- const subService = dartServiceFromDefinition(
166
- subServiceName,
167
- item,
168
- opts
169
- );
170
- subServiceParts.push({
171
- name: subServiceName,
172
- key,
173
- content: subService
174
- });
175
- }
176
- });
177
- return `class ${serviceName}Service {
178
- final http.Client? _httpClient;
179
- final String _baseUrl;
180
- final String _clientVersion = "${opts.versionNumber}";
181
- late final FutureOr<Map<String, String>> Function()? _headers;
182
- ${serviceName}Service({
183
- http.Client? httpClient,
184
- String baseUrl = "",
185
- FutureOr<Map<String, String>> Function()? headers,
186
- }) : _httpClient = httpClient,
187
- _baseUrl = baseUrl,
188
- _headers = headers;
189
- ${subServiceParts.map(
190
- (sub) => `${sub.name}Service get ${sub.key} {
191
- return ${sub.name}Service(
192
- httpClient: _httpClient,
193
- baseUrl: _baseUrl,
194
- headers: _headers,
195
- );
196
- }`
197
- ).join("\n")}
198
- ${rpcParts.join("\n ")}
199
- }
200
- ${subServiceParts.map((sub) => sub.content).join("\n")}
201
- `;
202
- }
203
- function dartRpcFromDefinition(key, def, opts) {
204
- if (def.transport === "http") {
205
- return dartHttpRpcFromSchema(key, def);
206
- }
207
- if (def.transport === "ws") {
208
- return dartWsRpcFromSchema(key, def);
209
- }
210
- console.warn(
211
- `[codegen-dart] WARNING: unsupported transport "${def.transport}". Skipping "${def.path}".`
139
+ function validDartClassName(input, modelPrefix) {
140
+ const className = sanitizeIdentifier(
141
+ pascalCase(input, { normalize: true })
212
142
  );
213
- return "";
214
- }
215
- function dartHttpRpcFromSchema(key, def, _opts) {
216
- let returnType = `Future<String>`;
217
- let returnTypeName = "String";
218
- if (def.response) {
219
- returnTypeName = pascalCase(def.response);
220
- if (def.isEventStream) {
221
- returnType = `EventSource<${returnTypeName}>`;
222
- } else {
223
- returnType = `Future<${returnTypeName}>`;
224
- }
225
- } else {
226
- returnType = "Future<void>";
227
- }
228
- let paramsInput = "";
229
- if (def.params) {
230
- paramsInput = `${pascalCase(def.params)} params`;
231
- }
232
- let responseParser = "(body) => body;";
233
- switch (returnType) {
234
- case "Future<String>":
235
- break;
236
- case "Future<int>":
237
- responseParser = `(body) => Int.parse(body)`;
238
- break;
239
- case "Future<double>":
240
- responseParser = `(body) => Double.parse(body)`;
241
- break;
242
- case "Future<void>":
243
- responseParser = `(body) {}`;
244
- break;
245
- case "Future<bool>":
246
- responseParser = `(body) {
247
- switch(body) {
248
- case "true":
249
- case "1":
250
- return true;
251
- case "false":
252
- case "0":
253
- default:
254
- return false;
255
- }
256
- }`;
257
- break;
258
- default:
259
- responseParser = `(body) => ${returnTypeName}.fromJson(
260
- json.decode(body),
261
- )`;
262
- break;
263
- }
264
- if (def.isEventStream) {
265
- const hookParts = [
266
- `SseHookOnData<${returnTypeName}>? onData`,
267
- `SseHookOnError<${returnTypeName}>? onError`,
268
- `SseHookOnConnectionError<${returnTypeName}>? onConnectionError`,
269
- `SseHookOnOpen<${returnTypeName}>? onOpen`,
270
- `SseHookOnClose<${returnTypeName}>? onClose`,
271
- `String? lastEventId`
272
- ];
273
- return `${getAnnotations({ description: def.description, isDeprecated: def.isDeprecated })}${returnType} ${key}(${paramsInput.length ? `${paramsInput}, ` : ""}{${hookParts.join(", ")},}) {
274
- return parsedArriSseRequest<${returnTypeName}>(
275
- "$_baseUrl${def.path}",
276
- httpClient: _httpClient,
277
- method: HttpMethod.${def.method},
278
- headers: _headers,
279
- params: ${paramsInput.length ? `params.toJson()` : "null"},
280
- parser: ${responseParser},
281
- onData: onData,
282
- onError: onError,
283
- onConnectionError: onConnectionError,
284
- onOpen: onOpen,
285
- onClose: onClose,
286
- lastEventId: lastEventId,
287
- clientVersion: _clientVersion,
288
- );
289
- }`;
143
+ if (canUseClassName(className) || modelPrefix.length) {
144
+ return className;
290
145
  }
291
- return `${getAnnotations({ description: def.description, isDeprecated: def.isDeprecated })}${returnType} ${key}(${paramsInput}) {
292
- return parsedArriRequest(
293
- "$_baseUrl${def.path}",
294
- httpClient: _httpClient,
295
- method: HttpMethod.${def.method},
296
- headers: _headers,
297
- params: ${paramsInput.length ? `params.toJson()` : "null"},
298
- parser: ${responseParser},
299
- clientVersion: _clientVersion,
300
- );
301
- }`;
302
- }
303
- function dartWsRpcFromSchema(key, def, _opts) {
304
- const serverMsg = def.response ? pascalCase(def.response, { normalize: true }) : "Null";
305
- const clientMsg = def.params ? pascalCase(def.params, { normalize: true }) : "Null";
306
- const returnType = `Future<ArriWebsocketController<${serverMsg}, ${clientMsg}>>`;
307
- const parser = def.response ? `(body) => ${serverMsg}.fromJson(json.decode(body))` : `(_) => null`;
308
- const serializer = def.params ? `(body) => json.encode(body.toJson())` : `(_) => ""`;
309
- return `${getAnnotations({ description: def.description, isDeprecated: def.isDeprecated })}${returnType} ${key}() {
310
- return arriWebsocketRequest<${serverMsg}, ${clientMsg}>(
311
- "$_baseUrl${def.path}",
312
- headers: _headers,
313
- parser: ${parser},
314
- serializer: ${serializer},
315
- clientVersion: _clientVersion,
316
- );
317
- }`;
146
+ return `Class${className}`;
318
147
  }
319
- function dartTypeFromJtdSchema(nodePath, def, additionalOptions) {
320
- if (isSchemaFormType(def)) {
321
- return dartScalarFromJtdScalar(nodePath, def, additionalOptions);
322
- }
323
- if (isSchemaFormProperties(def)) {
324
- return dartClassFromJtdSchema(nodePath, def, additionalOptions);
325
- }
326
- if (isSchemaFormElements(def)) {
327
- return dartArrayFromJtdSchema(nodePath, def, additionalOptions);
328
- }
329
- if (isSchemaFormEnum(def)) {
330
- return dartEnumFromJtdSchema(nodePath, def, additionalOptions);
331
- }
332
- if (isSchemaFormValues(def)) {
333
- return dartMapFromJtdSchema(nodePath, def, additionalOptions);
334
- }
335
- if (isSchemaFormDiscriminator(def)) {
336
- return dartSealedClassFromJtdSchema(nodePath, def, additionalOptions);
148
+ function getDartClassName(schema, context) {
149
+ if (schema.metadata?.id) {
150
+ return validDartClassName(schema.metadata.id, context.modelPrefix);
337
151
  }
338
- if (isSchemaFormRef(def)) {
339
- return dartRefFromJtdSchema(nodePath, def, additionalOptions);
152
+ if (context.discriminatorParentId) {
153
+ return validDartClassName(
154
+ `${context.discriminatorParentId}_${context.discriminatorValue}`,
155
+ context.modelPrefix
156
+ );
340
157
  }
341
- return dartDynamicFromAny(nodePath, a.any(), additionalOptions);
158
+ return validDartClassName(
159
+ context.instancePath.split("/").join("_"),
160
+ context.modelPrefix
161
+ );
342
162
  }
343
- function dartClassFromJtdSchema(nodePath, def, additionalOptions) {
344
- const isException = additionalOptions?.isException ?? false;
345
- const discOptions = additionalOptions?.discriminatorOptions;
346
- const isDiscriminatorChild = (discOptions?.discriminatorKey.length ?? 0) > 0;
347
- const jsonKey = nodePath.split(".").pop() ?? "";
348
- const key = camelCaseWrapper(jsonKey);
349
- let className = def.metadata?.id ? pascalCase(def.metadata.id) : void 0;
350
- if (!className) {
351
- const relativePath = nodePath.split(".").pop();
352
- if (additionalOptions.parentId && relativePath) {
353
- className = pascalCase(
354
- `${additionalOptions.parentId}_${relativePath}`
355
- );
356
- } else {
357
- className = pascalCase(nodePath.split(".").join("_"));
358
- }
359
- }
360
- const properties = [];
361
- const optionalProperties = [];
362
- const subContentParts = [];
363
- if (!def.properties) {
364
- return {
365
- typeName: "",
366
- fieldTemplate: "",
367
- constructorTemplate: "",
368
- fromJsonTemplate: () => "",
369
- toJsonTemplate: () => "",
370
- content: ""
371
- };
372
- }
373
- for (const key2 of Object.keys(def.properties ?? {})) {
374
- const keyPath = `${nodePath}.${key2}`;
375
- const prop = def.properties[key2];
376
- const mappedProp = dartTypeFromJtdSchema(keyPath, prop, {
377
- parentId: className,
378
- isOptional: false,
379
- existingClassNames: additionalOptions.existingClassNames
380
- });
381
- properties.push({
382
- key: key2,
383
- templates: mappedProp
384
- });
385
- if (mappedProp?.content) {
386
- subContentParts.push(mappedProp.content);
387
- }
388
- }
389
- if (def.optionalProperties) {
390
- for (const key2 of Object.keys(def.optionalProperties ?? {})) {
391
- const keyPath = `${nodePath}.${key2}`;
392
- const prop = def.optionalProperties[key2];
393
- const mappedProp = dartTypeFromJtdSchema(keyPath, prop, {
394
- parentId: className,
395
- isOptional: true,
396
- existingClassNames: additionalOptions.existingClassNames
397
- });
398
- optionalProperties.push({ key: key2, templates: mappedProp });
399
- if (mappedProp?.content) {
400
- subContentParts.push(mappedProp.content);
163
+
164
+ function dartAnyFromSchema(schema, context) {
165
+ const typeName = `dynamic`;
166
+ const isNullable = outputIsNullable(schema, context);
167
+ const defaultValue = `null`;
168
+ return {
169
+ typeName,
170
+ isNullable,
171
+ defaultValue,
172
+ fromJson(input) {
173
+ return `${input}`;
174
+ },
175
+ toJson(input) {
176
+ return input;
177
+ },
178
+ toQueryString() {
179
+ return `print("[WARNING] any's cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
180
+ },
181
+ content: ""
182
+ };
183
+ }
184
+
185
+ function dartListFromSchema(schema, context) {
186
+ const isNullable = outputIsNullable(schema, context);
187
+ const innerType = dartTypeFromSchema(schema.elements, {
188
+ clientName: context.clientName,
189
+ modelPrefix: context.modelPrefix,
190
+ generatedTypes: context.generatedTypes,
191
+ instancePath: `${context.instancePath}/[Element]`,
192
+ schemaPath: `${context.schemaPath}/elements`
193
+ });
194
+ const typeName = isNullable ? `List<${innerType.typeName}>?` : `List<${innerType.typeName}>`;
195
+ const defaultValue = isNullable ? "null" : "[]";
196
+ return {
197
+ typeName,
198
+ isNullable,
199
+ defaultValue,
200
+ fromJson(input) {
201
+ if (isNullable) {
202
+ return `${input} is List
203
+ ? (${input} as List)
204
+ .map((_el_) => ${innerType.fromJson(`_el_`)})
205
+ .toList()
206
+ : null`;
401
207
  }
402
- }
208
+ return `${input} is List
209
+ ? (${input} as List)
210
+ .map((_el_) => ${innerType.fromJson(`_el_`)})
211
+ .toList()
212
+ : <${innerType.typeName}>[]`;
213
+ },
214
+ toJson(input) {
215
+ if (context.isOptional) {
216
+ return `${input}!.map((_el_) => ${innerType.toJson("_el_", "", "")}).toList()`;
217
+ }
218
+ if (schema.nullable) {
219
+ return `${input}?.map((_el_) => ${innerType.toJson("_el_", "", "")}).toList()`;
220
+ }
221
+ return `${input}.map((_el_) => ${innerType.toJson("_el_", "", "")}).toList()`;
222
+ },
223
+ toQueryString() {
224
+ return `print(
225
+ "[WARNING] arrays cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
226
+ },
227
+ content: innerType.content
228
+ };
229
+ }
230
+
231
+ function dartClassFromSchema(schema, context) {
232
+ const isNullable = outputIsNullable(schema, context);
233
+ const className = getDartClassName(schema, context);
234
+ const finalClassName = `${context.modelPrefix}${className}`;
235
+ const typeName = isNullable ? `${finalClassName}?` : finalClassName;
236
+ const defaultValue = isNullable ? "null" : `${finalClassName}.empty()`;
237
+ const result = {
238
+ typeName,
239
+ isNullable,
240
+ defaultValue,
241
+ fromJson(input) {
242
+ if (isNullable) {
243
+ return `${input} is Map<String, dynamic>
244
+ ? ${finalClassName}.fromJson(${input})
245
+ : null`;
246
+ }
247
+ return `${input} is Map<String, dynamic>
248
+ ? ${finalClassName}.fromJson(${input})
249
+ : ${finalClassName}.empty()`;
250
+ },
251
+ toJson(input) {
252
+ if (context.isOptional) {
253
+ return `${input}!.toJson()`;
254
+ }
255
+ if (schema.nullable) {
256
+ return `${input}?.toJson()`;
257
+ }
258
+ return `${input}.toJson()`;
259
+ },
260
+ toQueryString() {
261
+ return `print(
262
+ "[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
263
+ },
264
+ content: ""
265
+ };
266
+ if (context.generatedTypes.includes(className)) {
267
+ return result;
403
268
  }
269
+ const propNames = [];
404
270
  const fieldParts = [];
405
271
  const constructorParts = [];
272
+ const defaultParts = [];
406
273
  const fromJsonParts = [];
274
+ const toJsonRequiredParts = [];
275
+ if (context.discriminatorKey && context.discriminatorValue) {
276
+ toJsonRequiredParts.push(
277
+ ` "${context.discriminatorKey}": ${validDartIdentifier(context.discriminatorKey)},`
278
+ );
279
+ }
280
+ const toJsonOptionalParts = [];
281
+ const toUrlQueryParts = [];
282
+ if (context.discriminatorKey && context.discriminatorValue) {
283
+ toUrlQueryParts.push(
284
+ `_queryParts_.add("${context.discriminatorKey}=$${validDartIdentifier(context.discriminatorKey)}");`
285
+ );
286
+ }
407
287
  const copyWithParamParts = [];
408
- const copyWithInitParts = [];
409
- if (discOptions) {
410
- fieldParts.push(`@override
411
- final String ${camelCaseWrapper(discOptions.discriminatorKey)} = "${discOptions.discriminatorValue}"`);
412
- }
413
- for (const prop of properties) {
414
- fieldParts.push(prop.templates.fieldTemplate);
415
- constructorParts.push(prop.templates.constructorTemplate);
416
- const subJsonKey = prop.key;
417
- const subKey = camelCaseWrapper(prop.key);
288
+ const copyWithReturnParts = [];
289
+ const subContentParts = [];
290
+ for (const key of Object.keys(schema.properties)) {
291
+ const innerSchema = schema.properties[key];
292
+ const typeResult = dartTypeFromSchema(innerSchema, {
293
+ clientName: context.clientName,
294
+ modelPrefix: context.modelPrefix,
295
+ generatedTypes: context.generatedTypes,
296
+ instancePath: `/${className}/${key}`,
297
+ schemaPath: `${context.schemaPath}/properties/${key}`
298
+ });
299
+ const propName = validDartIdentifier(key);
300
+ propNames.push(propName);
301
+ fieldParts.push(` final ${typeResult.typeName} ${propName};`);
302
+ constructorParts.push(` required this.${propName},`);
303
+ defaultParts.push(` ${propName}: ${typeResult.defaultValue},`);
418
304
  fromJsonParts.push(
419
- `${subKey}: ${prop.templates.fromJsonTemplate(
420
- `json["${subJsonKey}"]`
421
- )}`
305
+ ` final ${propName} = ${typeResult.fromJson(`_input_["${key}"]`, key)};`
422
306
  );
423
- if (prop.templates.typeName === "dynamic") {
424
- copyWithParamParts.push(`dynamic ${subKey}`);
425
- copyWithInitParts.push(`${subKey}: ${subKey} ?? this.${subKey}`);
307
+ toJsonRequiredParts.push(
308
+ ` "${key}": ${typeResult.toJson(propName, "", key)},`
309
+ );
310
+ toUrlQueryParts.push(
311
+ ` ${typeResult.toQueryString(propName, "_queryParts_", key)};`
312
+ );
313
+ if (typeResult.isNullable) {
314
+ copyWithParamParts.push(
315
+ ` ${typeResult.typeName} Function()? ${propName},`
316
+ );
317
+ copyWithReturnParts.push(
318
+ ` ${propName}: ${propName} != null ? ${propName}() : this.${propName},`
319
+ );
426
320
  } else {
427
- if (prop.templates.typeName.endsWith("?")) {
428
- copyWithParamParts.push(
429
- `ArriBox<${prop.templates.typeName}>? ${subKey}`
430
- );
431
- copyWithInitParts.push(
432
- `${subKey}: ${subKey} != null ? ${subKey}.value : this.${subKey}`
433
- );
434
- } else {
435
- copyWithParamParts.push(
436
- `${prop.templates.typeName}? ${subKey}`
437
- );
438
- copyWithInitParts.push(
439
- `${subKey}: ${subKey} ?? this.${subKey}`
440
- );
441
- }
321
+ copyWithParamParts.push(
322
+ ` ${typeResult.typeName}${typeResult.typeName !== "dynamic" ? "?" : ""} ${propName},`
323
+ );
324
+ copyWithReturnParts.push(
325
+ ` ${propName}: ${propName} ?? this.${propName},`
326
+ );
327
+ }
328
+ if (typeResult.content) {
329
+ subContentParts.push(typeResult.content);
442
330
  }
443
331
  }
444
- for (const prop of optionalProperties) {
445
- fieldParts.push(prop.templates.fieldTemplate);
446
- constructorParts.push(prop.templates.constructorTemplate);
447
- const subKey = camelCaseWrapper(prop.key);
448
- const subJsonKey = prop.key;
332
+ for (const key of Object.keys(schema.optionalProperties ?? {})) {
333
+ const innerSchema = schema.optionalProperties[key];
334
+ const typeResult = dartTypeFromSchema(innerSchema, {
335
+ clientName: context.clientName,
336
+ modelPrefix: context.modelPrefix,
337
+ generatedTypes: context.generatedTypes,
338
+ instancePath: `/${className}/${key}`,
339
+ schemaPath: `${context.schemaPath}/optionalProperties/${key}`,
340
+ isOptional: true
341
+ });
342
+ const propName = validDartIdentifier(key);
343
+ propNames.push(propName);
344
+ fieldParts.push(` final ${typeResult.typeName} ${propName};`);
345
+ constructorParts.push(` this.${propName},`);
449
346
  fromJsonParts.push(
450
- `${subKey}: ${prop.templates.fromJsonTemplate(
451
- `json["${subJsonKey}"]`
452
- )}`
347
+ ` final ${propName} = ${typeResult.fromJson(`_input_["${key}"]`, key)};`
453
348
  );
454
- if (prop.templates.typeName === "dynamic") {
455
- copyWithParamParts.push(`dynamic ${subKey}`);
456
- copyWithInitParts.push(`${subKey}: ${subKey} ?? this.${subKey}`);
457
- } else {
458
- if (prop.templates.typeName.endsWith("?")) {
459
- copyWithParamParts.push(
460
- `ArriBox<${prop.templates.typeName}>? ${subKey}`
461
- );
462
- copyWithInitParts.push(
463
- `${subKey}: ${subKey} != null ? ${subKey}.value : this.${subKey}`
464
- );
465
- } else {
466
- copyWithParamParts.push(
467
- `${prop.templates.typeName}? ${subKey}`
468
- );
469
- copyWithInitParts.push(
470
- `${subKey}: ${subKey} ?? this.${subKey}`
471
- );
472
- }
349
+ toJsonOptionalParts.push(
350
+ ` if (${propName} != null) _output_["${key}"] = ${typeResult.toJson(propName, "_output_", key)};`
351
+ );
352
+ toUrlQueryParts.push(
353
+ ` ${typeResult.toQueryString(propName, "_queryParts_", key)};`
354
+ );
355
+ copyWithParamParts.push(
356
+ ` ${typeResult.typeName} Function()? ${propName},`
357
+ );
358
+ copyWithReturnParts.push(
359
+ ` ${propName}: ${propName} != null ? ${propName}() : this.${propName},`
360
+ );
361
+ if (typeResult.content) {
362
+ subContentParts.push(typeResult.content);
473
363
  }
474
364
  }
475
- let classNamePart = `class ${className}`;
476
- if (isDiscriminatorChild) {
477
- classNamePart += ` implements ${discOptions?.discriminatorParentClassName}`;
478
- } else if (isException) {
479
- classNamePart += ` implements Exception`;
365
+ let discriminatorPart = "";
366
+ if (context.discriminatorKey && context.discriminatorValue) {
367
+ discriminatorPart = `
368
+ @override
369
+ String get ${validDartIdentifier(context.discriminatorKey)} => "${context.discriminatorValue}";
370
+ `;
480
371
  }
481
- let content = `${getAnnotations(def.metadata)}${classNamePart} {
482
- ${fieldParts.join(";\n ")};
483
- const ${className}({
484
- ${constructorParts.join(",\n ")},
372
+ result.content = `class ${finalClassName} implements ${context.discriminatorParentId ?? "ArriModel"} {
373
+ ${fieldParts.join("\n")}
374
+ const ${finalClassName}({
375
+ ${constructorParts.join("\n")}
485
376
  });
486
- factory ${className}.fromJson(Map<String, dynamic> json) {
487
- return ${className}(
488
- ${fromJsonParts.join(",\n ")},
377
+ ${discriminatorPart}
378
+ factory ${finalClassName}.empty() {
379
+ return ${finalClassName}(
380
+ ${defaultParts.join("\n")}
489
381
  );
490
382
  }
491
- ${isDiscriminatorChild ? `@override` : ""}
383
+
384
+ factory ${finalClassName}.fromJson(Map<String, dynamic> _input_) {
385
+ ${fromJsonParts.join("\n")}
386
+ return ${finalClassName}(
387
+ ${propNames.map((prop) => ` ${prop}: ${prop},`).join("\n")}
388
+ );
389
+ }
390
+
391
+ factory ${finalClassName}.fromJsonString(String input) {
392
+ return ${finalClassName}.fromJson(json.decode(input));
393
+ }
394
+
395
+ @override
492
396
  Map<String, dynamic> toJson() {
493
- final __result = <String, dynamic>{${isDiscriminatorChild ? `
494
- "${discOptions?.discriminatorKey}": ${camelCaseWrapper(
495
- discOptions?.discriminatorKey ?? ""
496
- )},` : ""}
497
- ${properties.map(
498
- (prop) => `"${prop.key}": ${prop.templates.toJsonTemplate(
499
- camelCaseWrapper(prop.key)
500
- )}`
501
- ).join(",\n ")}${properties.length ? "," : ""}
397
+ final _output_ = <String, dynamic>{
398
+ ${toJsonRequiredParts.join("\n")}
502
399
  };
503
- ${optionalProperties.map(
504
- (prop) => `if (${camelCaseWrapper(prop.key)} != null) {
505
- __result["${prop.key}"] = ${prop.templates.toJsonTemplate(
506
- camelCaseWrapper(prop.key)
507
- )};
508
- }`
509
- ).join("\n")}
510
- return __result;
400
+ ${toJsonOptionalParts.join("\n")}
401
+ return _output_;
402
+ }
403
+
404
+ @override
405
+ String toJsonString() {
406
+ return json.encode(toJson());
511
407
  }
512
- ${className} copyWith({
513
- ${copyWithParamParts.join(",\n ")},
408
+
409
+ @override
410
+ String toUrlQueryParams() {
411
+ final _queryParts_ = <String>[];
412
+ ${toUrlQueryParts.join("\n")}
413
+ return _queryParts_.join("&");
414
+ }
415
+
416
+ @override
417
+ ${finalClassName} copyWith({
418
+ ${copyWithParamParts.join("\n")}
514
419
  }) {
515
- return ${className}(
516
- ${copyWithInitParts.join(",\n ")},
420
+ return ${finalClassName}(
421
+ ${copyWithReturnParts.join("\n")}
517
422
  );
518
423
  }
424
+
425
+ @override
426
+ List<Object?> get props => [
427
+ ${propNames.map((prop) => ` ${prop},`).join("\n")}
428
+ ];
429
+
430
+ @override
431
+ bool operator ==(Object other) {
432
+ return other is ${finalClassName} &&
433
+ listsAreEqual(props, other.props);
434
+ }
435
+
436
+ @override
437
+ int get hashCode => listToHashCode(props);
438
+
439
+ @override
440
+ String toString() {
441
+ return "${finalClassName} \${toJsonString()}";
442
+ }
443
+ }
444
+
445
+ ${subContentParts.join("\n\n")}`;
446
+ context.generatedTypes.push(className);
447
+ return result;
519
448
  }
520
- ${subContentParts.join("\n")}
521
449
 
522
- `;
523
- if (additionalOptions.existingClassNames.includes(className)) {
524
- content = "";
525
- } else {
526
- additionalOptions.existingClassNames.push(className);
450
+ function dartSealedClassFromSchema(schema, context) {
451
+ const isNullable = outputIsNullable(schema, context);
452
+ const className = getDartClassName(schema, context);
453
+ const finalClassName = `${context.modelPrefix}${className}`;
454
+ const typeName = isNullable ? `${finalClassName}?` : finalClassName;
455
+ const defaultValue = isNullable ? `null` : `${finalClassName}.empty()`;
456
+ const discriminatorKey = schema.discriminator;
457
+ const subTypeParts = [];
458
+ const subContentParts = [];
459
+ for (const key of Object.keys(schema.mapping)) {
460
+ const subSchema = schema.mapping[key];
461
+ const discriminatorValue = key;
462
+ const subTypeResult = dartClassFromSchema(subSchema, {
463
+ clientName: context.clientName,
464
+ modelPrefix: context.modelPrefix,
465
+ generatedTypes: context.generatedTypes,
466
+ instancePath: context.instancePath,
467
+ schemaPath: `${context.schemaPath}/mapping/${key}`,
468
+ discriminatorKey,
469
+ discriminatorValue,
470
+ discriminatorParentId: className
471
+ });
472
+ subTypeParts.push({
473
+ name: subTypeResult.typeName,
474
+ value: discriminatorValue
475
+ });
476
+ if (subTypeResult.content) {
477
+ subContentParts.push(subTypeResult.content);
478
+ }
527
479
  }
528
- const isNullable = def.nullable ?? additionalOptions?.isOptional;
529
- const typeName = isNullable ? `${className}?` : className;
530
- return {
480
+ const result = {
531
481
  typeName,
532
- fieldTemplate: fieldTemplateString(
533
- typeName,
534
- key,
535
- def.metadata?.description,
536
- def.metadata?.isDeprecated
537
- ),
538
- constructorTemplate: additionalOptions.isOptional ? `this.${key}` : `required this.${key}`,
539
- fromJsonTemplate: (input) => isNullable ? `${input} is Map<String, dynamic> ? ${className}.fromJson(${input}) : null` : `${className}.fromJson(${input})`,
540
- toJsonTemplate: (input) => `${input}${isNullable ? "?" : ""}.toJson()`,
541
- content
542
- };
543
- }
544
- function dartDynamicFromAny(nodePath, def, additionalOptions) {
545
- const jsonKey = nodePath.split(".").pop() ?? "";
546
- const key = camelCaseWrapper(jsonKey);
547
- return {
548
- typeName: "dynamic",
549
- fieldTemplate: fieldTemplateString(
550
- "dynamic",
551
- key,
552
- def.metadata?.description,
553
- def.metadata?.isDeprecated
554
- ),
555
- constructorTemplate: additionalOptions.isOptional ? `this.${key}` : `required this.${key}`,
556
- fromJsonTemplate: (input) => `${input}`,
557
- toJsonTemplate: (input) => input,
482
+ isNullable,
483
+ defaultValue,
484
+ fromJson(input) {
485
+ if (isNullable) {
486
+ return `${input} is Map<String, dynamic>
487
+ ? ${finalClassName}.fromJson(${input})
488
+ : null`;
489
+ }
490
+ return `${input} is Map<String, dynamic>
491
+ ? ${finalClassName}.fromJson(${input})
492
+ : ${finalClassName}.empty()`;
493
+ },
494
+ toJson(input) {
495
+ if (context.isOptional) {
496
+ return `${input}!.toJson()`;
497
+ }
498
+ if (schema.nullable) {
499
+ return `${input}?.toJson()`;
500
+ }
501
+ return `${input}.toJson()`;
502
+ },
503
+ toQueryString() {
504
+ return `print(
505
+ "[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
506
+ },
558
507
  content: ""
559
508
  };
560
- }
561
- function fieldTemplateString(typeName, key, description, deprecated) {
562
- let result = "";
563
- if (deprecated) {
564
- result += `@deprecated
565
- `;
509
+ if (context.generatedTypes.includes(className)) {
510
+ return result;
566
511
  }
567
- if (description) {
568
- const parts = description.split("\n");
569
- for (const part of parts) {
570
- result += `/// ${part}
571
- `;
512
+ const discriminatorProp = validDartIdentifier(schema.discriminator);
513
+ result.content = `sealed class ${finalClassName} implements ArriModel {
514
+ String get ${discriminatorProp};
515
+ const ${finalClassName}();
516
+
517
+ factory ${finalClassName}.empty() {
518
+ return ${subTypeParts[0]?.name}.empty();
572
519
  }
573
- }
574
- result += `final ${typeName} ${key}`;
520
+
521
+ factory ${finalClassName}.fromJson(Map<String, dynamic> _input_) {
522
+ final ${discriminatorProp} = typeFromDynamic<String>(_input_["${schema.discriminator}"], "");
523
+ switch (${discriminatorProp}) {
524
+ ${subTypeParts.map(
525
+ (type) => ` case "${type.value}":
526
+ return ${type.name}.fromJson(_input_);`
527
+ ).join("\n")}
528
+ default:
529
+ return ${finalClassName}.empty();
530
+ }
531
+ }
532
+
533
+ factory ${finalClassName}.fromJsonString(String input) {
534
+ return ${finalClassName}.fromJson(json.decode(input));
535
+ }
536
+ }
537
+
538
+ ${subContentParts.join("\n\n")}`;
539
+ context.generatedTypes.push(className);
575
540
  return result;
576
541
  }
577
- function dartArrayFromJtdSchema(nodePath, def, additionalOptions) {
578
- const isNullable = additionalOptions.isOptional || (def.nullable ?? false);
579
- const jsonKey = nodePath.split(".").pop() ?? "";
580
- const key = camelCaseWrapper(jsonKey);
581
- const subtype = dartTypeFromJtdSchema(`${nodePath}.Item`, def.elements, {
582
- existingClassNames: additionalOptions.existingClassNames,
583
- isOptional: false
584
- });
585
- const typeName = isNullable ? `List<${subtype.typeName}>?` : `List<${subtype.typeName}>`;
586
- return {
542
+
543
+ function dartEnumFromSchema(schema, context) {
544
+ const isNullable = outputIsNullable(schema, context);
545
+ const enumName = getDartClassName(schema, context);
546
+ const typeName = isNullable ? `${enumName}?` : enumName;
547
+ const enumValues = schema.enum.map((val) => ({
548
+ name: camelCase(val, { normalize: true }),
549
+ serialValue: val
550
+ }));
551
+ if (!enumValues.length) {
552
+ throw new Error(
553
+ `Enum schemas must have at least one enum value. At ${context.instancePath}.`
554
+ );
555
+ }
556
+ const defaultValue = isNullable ? "null" : `${context.modelPrefix}${enumName}.${enumValues[0]?.name}`;
557
+ const output = {
587
558
  typeName,
588
- fieldTemplate: fieldTemplateString(
589
- typeName,
590
- key,
591
- def.metadata?.description,
592
- def.metadata?.isDeprecated
593
- ),
594
- constructorTemplate: additionalOptions.isOptional ? `this.${key}` : `required this.${key}`,
595
- fromJsonTemplate: (input) => {
559
+ isNullable,
560
+ defaultValue,
561
+ fromJson(input, _key) {
596
562
  if (isNullable) {
597
- return `${input} is List ?
598
- // ignore: unnecessary_cast
599
- (${input} as List).map((item) => ${subtype.fromJsonTemplate(
600
- "item"
601
- )}).toList() as ${typeName} : null`;
602
- }
603
- return `${input} is List ?
604
- // ignore: unnecessary_cast
605
- (${input} as List).map((item) => ${subtype.fromJsonTemplate(
606
- "item"
607
- )}).toList() as ${typeName} : <${subtype.typeName}>[]`;
608
- },
609
- toJsonTemplate: (input) => {
610
- return `${input}${isNullable ? "?" : ""}.map((item) => ${subtype.toJsonTemplate("item")}).toList()`;
611
- },
612
- content: subtype.content
563
+ return `${input} is String ? ${context.modelPrefix}${enumName}.fromString(${input}) : null`;
564
+ }
565
+ return `${context.modelPrefix}${enumName}.fromString(typeFromDynamic<String>(${input}, ""))`;
566
+ },
567
+ toJson(input) {
568
+ if (context.isOptional) {
569
+ return `${input}!.serialValue`;
570
+ }
571
+ if (schema.nullable) {
572
+ return `${input}?.serialValue`;
573
+ }
574
+ return `${input}.serialValue`;
575
+ },
576
+ toQueryString(input, target, key) {
577
+ if (context.isOptional) {
578
+ return `if (${input} != null) ${target}.add("${key}=\${${input}!.serialValue}")`;
579
+ }
580
+ if (schema.nullable) {
581
+ return `${target}.add("${key}=\${${input}?.serialValue}")`;
582
+ }
583
+ return `${target}.add("${key}=\${${input}.serialValue}")`;
584
+ },
585
+ content: ""
613
586
  };
614
- }
615
- function dartScalarFromJtdScalar(nodePath, def, additionalOptions) {
616
- const isNullable = additionalOptions.isOptional || (def.nullable ?? false);
617
- const jsonKey = nodePath.split(".").pop() ?? "";
618
- const key = camelCaseWrapper(jsonKey);
619
- const defaultInitializationTemplate = additionalOptions.isOptional ? `this.${key}` : `required this.${key}`;
620
- const { description } = def.metadata ?? {};
621
- const defaultToJsonTemplate = (input) => input;
622
- switch (def.type) {
623
- case "boolean":
624
- if (isNullable) {
625
- return {
626
- typeName: "bool?",
627
- fieldTemplate: fieldTemplateString(
628
- "bool?",
629
- key,
630
- description,
631
- def.metadata?.isDeprecated
632
- ),
633
- constructorTemplate: defaultInitializationTemplate,
634
- fromJsonTemplate: (input) => `nullableTypeFromDynamic<bool>(${input})`,
635
- toJsonTemplate: defaultToJsonTemplate,
636
- content: ""
637
- };
638
- }
639
- return {
640
- typeName: "bool",
641
- fieldTemplate: fieldTemplateString(
642
- "bool",
643
- key,
644
- description,
645
- def.metadata?.isDeprecated
646
- ),
647
- constructorTemplate: defaultInitializationTemplate,
648
- fromJsonTemplate: (input) => `typeFromDynamic<bool>(${input}, false)`,
649
- toJsonTemplate: defaultToJsonTemplate,
650
- content: ""
651
- };
652
- case "float32":
653
- case "float64":
654
- if (isNullable) {
655
- return {
656
- typeName: "double?",
657
- fieldTemplate: fieldTemplateString(
658
- "double?",
659
- key,
660
- description,
661
- def.metadata?.isDeprecated
662
- ),
663
- constructorTemplate: defaultInitializationTemplate,
664
- fromJsonTemplate: (input) => `nullableDoubleFromDynamic(${input})`,
665
- toJsonTemplate: defaultToJsonTemplate,
666
- content: ""
667
- };
668
- }
669
- return {
670
- typeName: "double",
671
- fieldTemplate: fieldTemplateString(
672
- "double",
673
- key,
674
- description,
675
- def.metadata?.isDeprecated
676
- ),
677
- constructorTemplate: defaultInitializationTemplate,
678
- fromJsonTemplate: (input) => `doubleFromDynamic(${input}, 0)`,
679
- toJsonTemplate: defaultToJsonTemplate,
680
- content: ""
681
- };
682
- case "int16":
683
- case "int32":
684
- case "int8":
685
- case "uint16":
686
- case "uint32":
687
- case "uint8":
688
- if (isNullable) {
689
- return {
690
- typeName: "int?",
691
- fieldTemplate: fieldTemplateString(
692
- "int?",
693
- key,
694
- description,
695
- def.metadata?.isDeprecated
696
- ),
697
- constructorTemplate: defaultInitializationTemplate,
698
- fromJsonTemplate: (input) => `nullableIntFromDynamic(${input})`,
699
- toJsonTemplate: defaultToJsonTemplate,
700
- content: ""
701
- };
702
- }
703
- return {
704
- typeName: "int",
705
- fieldTemplate: fieldTemplateString(
706
- `int`,
707
- key,
708
- description,
709
- def.metadata?.isDeprecated
710
- ),
711
- constructorTemplate: defaultInitializationTemplate,
712
- fromJsonTemplate: (input) => `intFromDynamic(${input}, 0)`,
713
- toJsonTemplate: defaultToJsonTemplate,
714
- content: ""
715
- };
716
- case "int64":
717
- case "uint64":
718
- if (isNullable) {
719
- return {
720
- typeName: "BigInt?",
721
- fieldTemplate: fieldTemplateString(
722
- `BigInt?`,
723
- key,
724
- description,
725
- def.metadata?.isDeprecated
726
- ),
727
- constructorTemplate: defaultInitializationTemplate,
728
- fromJsonTemplate: (input) => `nullableBigIntFromDynamic(${input})`,
729
- toJsonTemplate: (input) => `${input}?.toString()`,
730
- content: ""
731
- };
732
- }
733
- return {
734
- typeName: "BigInt",
735
- fieldTemplate: fieldTemplateString(
736
- `BigInt`,
737
- key,
738
- description,
739
- def.metadata?.isDeprecated
740
- ),
741
- constructorTemplate: defaultInitializationTemplate,
742
- fromJsonTemplate: (input) => `bigIntFromDynamic(${input}, BigInt.zero)`,
743
- toJsonTemplate: (input) => `${input}.toString()`,
744
- content: ""
745
- };
746
- case "timestamp":
747
- if (isNullable) {
748
- return {
749
- typeName: "DateTime?",
750
- fieldTemplate: fieldTemplateString(
751
- "DateTime?",
752
- key,
753
- description,
754
- def.metadata?.isDeprecated
755
- ),
756
- constructorTemplate: defaultInitializationTemplate,
757
- fromJsonTemplate: (input) => `nullableDateTimeFromDynamic(${input})`,
758
- toJsonTemplate: (input) => `${input}?.toUtc().toIso8601String()`,
759
- content: ""
760
- };
761
- }
762
- return {
763
- typeName: "DateTime",
764
- fieldTemplate: fieldTemplateString(
765
- "DateTime",
766
- key,
767
- description,
768
- def.metadata?.isDeprecated
769
- ),
770
- constructorTemplate: defaultInitializationTemplate,
771
- fromJsonTemplate: (input) => `dateTimeFromDynamic(
772
- ${input},
773
- DateTime.fromMillisecondsSinceEpoch(0),
774
- )`,
775
- toJsonTemplate: (input) => `${input}.toUtc().toIso8601String()`,
776
- content: ""
777
- };
778
- case "string":
779
- if (isNullable) {
780
- return {
781
- typeName: "String?",
782
- fieldTemplate: fieldTemplateString(
783
- "String?",
784
- key,
785
- description,
786
- def.metadata?.isDeprecated
787
- ),
788
- constructorTemplate: defaultInitializationTemplate,
789
- fromJsonTemplate: (input) => `nullableTypeFromDynamic<String>(${input})`,
790
- toJsonTemplate: defaultToJsonTemplate,
791
- content: ""
792
- };
793
- }
794
- return {
795
- typeName: "String",
796
- fieldTemplate: fieldTemplateString(
797
- "String",
798
- key,
799
- description,
800
- def.metadata?.isDeprecated
801
- ),
802
- constructorTemplate: defaultInitializationTemplate,
803
- fromJsonTemplate: (input) => `typeFromDynamic<String>(${input}, "")`,
804
- toJsonTemplate: defaultToJsonTemplate,
805
- content: ""
806
- };
807
- default:
808
- return {
809
- typeName: "dynamic",
810
- fieldTemplate: fieldTemplateString(
811
- "dynamic",
812
- key,
813
- description,
814
- def.metadata?.isDeprecated
815
- ),
816
- constructorTemplate: defaultInitializationTemplate,
817
- fromJsonTemplate: (input) => input,
818
- toJsonTemplate: defaultToJsonTemplate,
819
- content: ""
820
- };
821
- }
822
- }
823
- function dartEnumFromJtdSchema(nodePath, def, additionalOptions) {
824
- const isNullable = additionalOptions.isOptional || (def.nullable ?? false);
825
- const jsonKey = nodePath.split(".").pop() ?? "";
826
- const key = camelCaseWrapper(jsonKey);
827
- let className = def.metadata?.id ? pascalCase(def.metadata.id) : void 0;
828
- if (!className) {
829
- className = pascalCase(nodePath.split(".").join("_"));
830
- }
831
- const valNames = [];
832
- const fieldParts = [];
833
- for (const val of def.enum) {
834
- valNames.push(`${camelCaseWrapper(val)}`);
835
- fieldParts.push(`${camelCaseWrapper(val)}("${val}")`);
587
+ if (context.generatedTypes.includes(enumName)) {
588
+ return output;
836
589
  }
837
- let content = `${getAnnotations(def.metadata)}enum ${className} implements Comparable<${className}> {
838
- ${fieldParts.join(",\n ")};
839
- const ${className}(this.value);
840
- final String value;
590
+ output.content = `enum ${context.modelPrefix}${enumName} implements Comparable<${context.modelPrefix}${enumName}> {
591
+ ${enumValues.map((val) => ` ${val.name}("${val.serialValue}")`).join(",\n")};
592
+
593
+ const ${context.modelPrefix}${enumName}(this.serialValue);
594
+ final String serialValue;
841
595
 
842
- factory ${className}.fromJson(dynamic json) {
843
- for(final v in values) {
844
- if(v.value == json) {
845
- return v;
596
+ factory ${context.modelPrefix}${enumName}.fromString(String input) {
597
+ for (final val in values) {
598
+ if (val.serialValue == input) {
599
+ return val;
846
600
  }
847
601
  }
848
- return ${valNames[0]};
602
+ return ${enumValues[0].name};
849
603
  }
850
604
 
851
605
  @override
852
- compareTo(${className} other) => name.compareTo(other.name);
606
+ int compareTo(${enumName} other) => name.compareTo(other.name);
853
607
  }`;
854
- if (additionalOptions.existingClassNames.includes(className)) {
855
- content = "";
856
- } else {
857
- additionalOptions.existingClassNames.push(className);
858
- }
608
+ context.generatedTypes.push(enumName);
609
+ return output;
610
+ }
611
+
612
+ function dartStringFromSchema(schema, context) {
613
+ const isNullable = outputIsNullable(schema, context);
614
+ const typeName = isNullable ? `String?` : "String";
615
+ const defaultValue = isNullable ? "null" : '""';
859
616
  return {
860
- typeName: className,
861
- fieldTemplate: fieldTemplateString(
862
- isNullable ? `${className}?` : className,
863
- key,
864
- def.metadata?.description,
865
- def.metadata?.isDeprecated
866
- ),
867
- constructorTemplate: additionalOptions.isOptional ? `this.${key}` : `required this.${key}`,
868
- fromJsonTemplate: (input) => {
617
+ typeName,
618
+ isNullable,
619
+ defaultValue,
620
+ fromJson(input, _key) {
869
621
  if (isNullable) {
870
- return `${input} is String ? ${className}.fromJson(${input}) : null`;
622
+ return `nullableTypeFromDynamic<String>(${input})`;
871
623
  }
872
- return `${className}.fromJson(${input})`;
624
+ return `typeFromDynamic<String>(${input}, "")`;
625
+ },
626
+ toJson(input, _target, _key) {
627
+ return input;
873
628
  },
874
- toJsonTemplate: (input) => `${input}${isNullable ? "?" : ""}.value`,
875
- content
629
+ toQueryString(input, target, key) {
630
+ if (context.isOptional) {
631
+ return `if (${input} != null) ${target}.add("${key}=$${input}")`;
632
+ }
633
+ return `${target}.add("${key}=$${input}")`;
634
+ },
635
+ content: ""
876
636
  };
877
637
  }
878
- function dartMapFromJtdSchema(nodePath, def, additionalOptions) {
879
- const isNullable = additionalOptions.isOptional || (def.nullable ?? false);
880
- const jsonKey = nodePath.split(".").pop() ?? "";
881
- const key = camelCaseWrapper(jsonKey);
882
- const innerType = dartTypeFromJtdSchema(`${nodePath}.Value`, def.values, {
883
- existingClassNames: additionalOptions.existingClassNames,
884
- isOptional: false
885
- });
886
- const typeName = `Map<String, ${innerType.typeName}>${isNullable ? "?" : ""}`;
638
+ function dartBoolFromSchema(schema, context) {
639
+ const isNullable = outputIsNullable(schema, context);
640
+ const typeName = isNullable ? "bool?" : "bool";
641
+ const defaultValue = isNullable ? "null" : "false";
887
642
  return {
888
- typeName: isNullable ? `Map<String, ${innerType.typeName}>?` : `Map<String, ${innerType.typeName}>`,
889
- fieldTemplate: fieldTemplateString(
890
- typeName,
891
- key,
892
- def.metadata?.description,
893
- def.metadata?.isDeprecated
894
- ),
895
- constructorTemplate: additionalOptions.isOptional ? `this.${key}` : `required this.${key}`,
896
- fromJsonTemplate: (input) => `${input} is Map<String, dynamic>
897
- ? (${input} as Map<String, dynamic>).map(
898
- (key, value) => MapEntry(key, ${innerType.fromJsonTemplate(
899
- "value"
900
- )}))
901
- : <String, ${innerType.typeName}>{}`,
902
- toJsonTemplate: (input) => `${input}${isNullable ? "?" : ""}.map((key, value) => MapEntry(key, ${innerType.toJsonTemplate(
903
- "value"
904
- )}))`,
905
- content: innerType.content
643
+ typeName,
644
+ isNullable,
645
+ defaultValue,
646
+ fromJson(input, _key) {
647
+ if (isNullable) {
648
+ return `nullableTypeFromDynamic<bool>(${input})`;
649
+ }
650
+ return `typeFromDynamic<bool>(${input}, false)`;
651
+ },
652
+ toJson(input, _target, _key) {
653
+ return input;
654
+ },
655
+ toQueryString(input, target, key) {
656
+ if (context.isOptional) {
657
+ return `if (${input} != null) ${target}.add("${key}=$${input}")`;
658
+ }
659
+ return `${target}.add("${key}=$${input}")`;
660
+ },
661
+ content: ""
906
662
  };
907
663
  }
908
- function dartSealedClassFromJtdSchema(nodePath, def, additionalOptions) {
909
- const className = def.metadata?.id ? pascalCase(def.metadata?.id) : pascalCase(nodePath.split(".").join("_"));
910
- const isNullable = additionalOptions.isOptional || (def.nullable ?? false);
911
- const jsonKey = nodePath.split(".").pop() ?? "";
912
- const key = camelCaseWrapper(jsonKey);
913
- const discriminatorJsonKey = def.discriminator;
914
- const discriminatorKey = camelCaseWrapper(def.discriminator);
915
- const fromJsonCaseParts = [];
916
- const childContentParts = [];
917
- Object.keys(def.mapping).forEach((discKeyValue) => {
918
- const childDef = def.mapping[discKeyValue];
919
- if (!isSchemaFormProperties(childDef)) {
920
- return;
921
- }
922
- const child = dartClassFromJtdSchema(
923
- `${nodePath}.${camelCaseWrapper(discKeyValue.toLowerCase())}`,
924
- childDef,
925
- {
926
- parentId: className,
927
- isOptional: false,
928
- existingClassNames: additionalOptions.existingClassNames,
929
- discriminatorOptions: {
930
- discriminatorKey,
931
- discriminatorValue: discKeyValue,
932
- discriminatorParentClassName: className
933
- }
664
+ function dartDateTimeFromSchema(schema, context) {
665
+ const isNullable = outputIsNullable(schema, context);
666
+ const typeName = isNullable ? "DateTime?" : "DateTime";
667
+ const defaultValue = isNullable ? "null" : "DateTime.now()";
668
+ return {
669
+ typeName,
670
+ isNullable,
671
+ defaultValue,
672
+ fromJson(input, _key) {
673
+ if (isNullable) {
674
+ return `nullableDateTimeFromDynamic(${input})`;
934
675
  }
935
- );
936
- fromJsonCaseParts.push(`case "${discKeyValue}":
937
- return ${child.typeName}.fromJson(json);`);
938
- childContentParts.push(child.content);
939
- });
940
- let content = "";
941
- if (!additionalOptions.existingClassNames.includes(className)) {
942
- content = `${getAnnotations(def.metadata)}sealed class ${className} {
943
- final String ${discriminatorKey};
944
- const ${className}({
945
- required this.${discriminatorKey},
946
- });
947
- factory ${className}.fromJson(Map<String, dynamic> json) {
948
- if(json["${discriminatorJsonKey}"] is! String) {
949
- throw Exception(
950
- "Unable to decode ${className}. Expected String from \\"${discriminatorJsonKey}\\". Received \${json["${discriminatorJsonKey}"]}}",
676
+ return `dateTimeFromDynamic(${input}, DateTime.now())`;
677
+ },
678
+ toJson(input, _target, _key) {
679
+ if (context.isOptional) {
680
+ return `${input}!.toUtc().toIso8601String()`;
681
+ }
682
+ if (schema.nullable) {
683
+ return `${input}?.toUtc().toIso8601String()`;
684
+ }
685
+ return `${input}.toUtc().toIso8601String()`;
686
+ },
687
+ toQueryString(input, target, key) {
688
+ if (context.isOptional) {
689
+ return `if (${input} != null) ${target}.add("${key}=\${${input}!.toUtc().toIso8601String()}")`;
690
+ }
691
+ if (schema.nullable) {
692
+ return `${target}.add("${key}=\${${input}?.toUtc().toIso8601String()}")`;
693
+ }
694
+ return `${target}.add("${key}=\${${input}.toUtc().toIso8601String()}")`;
695
+ },
696
+ content: ""
697
+ };
698
+ }
699
+ function dartDoubleFromSchema(schema, context) {
700
+ const isNullable = outputIsNullable(schema, context);
701
+ const typeName = isNullable ? "double?" : "double";
702
+ const defaultValue = isNullable ? "null" : "0.0";
703
+ return {
704
+ typeName,
705
+ isNullable,
706
+ defaultValue,
707
+ fromJson(input, _key) {
708
+ if (isNullable) {
709
+ return `nullableDoubleFromDynamic(${input})`;
710
+ }
711
+ return `doubleFromDynamic(${input}, 0.0)`;
712
+ },
713
+ toJson(input, _, __) {
714
+ return input;
715
+ },
716
+ toQueryString(input, target, key) {
717
+ if (context.isOptional) {
718
+ return `if (${input} != null) ${target}.add("${key}=$${input}")`;
719
+ }
720
+ return `${target}.add("${key}=$${input}")`;
721
+ },
722
+ content: ""
723
+ };
724
+ }
725
+ function dartIntFromSchema(schema, context) {
726
+ const isNullable = outputIsNullable(schema, context);
727
+ const typeName = isNullable ? "int?" : "int";
728
+ const defaultValue = isNullable ? "null" : "0";
729
+ return {
730
+ typeName,
731
+ isNullable,
732
+ defaultValue,
733
+ fromJson(input, _key) {
734
+ if (isNullable) {
735
+ return `nullableIntFromDynamic(${input})`;
736
+ }
737
+ return `intFromDynamic(${input}, 0)`;
738
+ },
739
+ toJson(input) {
740
+ return input;
741
+ },
742
+ toQueryString(input, target, key) {
743
+ if (context.isOptional) {
744
+ return `if (${input} != null) ${target}.add("${key}=$${input}")`;
745
+ }
746
+ return `${target}.add("${key}=$${input}")`;
747
+ },
748
+ content: ""
749
+ };
750
+ }
751
+ function dartBigIntFromSchema(schema, context) {
752
+ const isNullable = outputIsNullable(schema, context);
753
+ const typeName = isNullable ? "BigInt?" : "BigInt";
754
+ const defaultValue = isNullable ? "null" : "BigInt.zero";
755
+ return {
756
+ typeName,
757
+ isNullable,
758
+ defaultValue,
759
+ fromJson(input, _key) {
760
+ if (isNullable) {
761
+ return `nullableBigIntFromDynamic(${input})`;
762
+ }
763
+ return `bigIntFromDynamic(${input}, BigInt.zero)`;
764
+ },
765
+ toJson(input, _target, _key) {
766
+ if (context.isOptional) {
767
+ return `${input}!.toString()`;
768
+ }
769
+ if (schema.nullable) {
770
+ return `${input}?.toString()`;
771
+ }
772
+ return `${input}.toString()`;
773
+ },
774
+ toQueryString(input, target, key) {
775
+ if (context.isOptional) {
776
+ return `if (${input} != null) ${target}.add("${key}=$${input}")`;
777
+ }
778
+ return `${target}.add("${key}=$${input}")`;
779
+ },
780
+ content: ""
781
+ };
782
+ }
783
+
784
+ function dartRpcFromSchema(schema, context) {
785
+ switch (schema.transport) {
786
+ case "http":
787
+ return dartHttpRpcFromSchema(schema, context);
788
+ case "ws":
789
+ return dartWsRpcFromSchema(schema, context);
790
+ default:
791
+ console.warn(
792
+ `[WARNING] unsupported transport "${schema.transport}". Skipping ${context.instancePath}.`
951
793
  );
794
+ return "";
795
+ }
796
+ }
797
+ function dartHttpRpcFromSchema(schema, context) {
798
+ const functionName = getFunctionName(context.instancePath);
799
+ let responseType = "void";
800
+ let paramsType = "";
801
+ if (schema.response) {
802
+ responseType = `${context.modelPrefix}${validDartClassName(schema.response, context.modelPrefix)}`;
803
+ }
804
+ if (schema.params) {
805
+ paramsType = `${context.modelPrefix}${validDartClassName(schema.params, context.modelPrefix)}`;
806
+ }
807
+ if (schema.isEventStream) {
808
+ return `EventSource<${responseType}> ${functionName}(
809
+ ${paramsType ? `${paramsType} params, ` : ""} {
810
+ void Function(${responseType} data, EventSource<${responseType}> connection)? onMessage,
811
+ void Function(http.StreamedResponse response, EventSource<${responseType}> connection)? onOpen,
812
+ void Function(EventSource<${responseType}> connection)? onClose,
813
+ void Function(ArriError error, EventSource<${responseType}> connection)? onError,
814
+ Duration? retryDelay,
815
+ int? maxRetryCount,
816
+ String? lastEventId,
817
+ }) {
818
+ return parsedArriSseRequest(
819
+ "$_baseUrl${schema.path}",
820
+ method: HttpMethod.${schema.method.toLowerCase()},
821
+ httpClient: _httpClient,
822
+ headers: _headers,
823
+ clientVersion: _clientVersion,
824
+ retryDelay: retryDelay,
825
+ maxRetryCount: maxRetryCount,
826
+ lastEventId: lastEventId,
827
+ ${paramsType ? "params: params.toJson()," : ""}
828
+ parser: (body) ${schema.response ? `=> ${responseType}.fromJsonString(body)` : `{}`},
829
+ onMessage: onMessage,
830
+ onOpen: onOpen,
831
+ onClose: onClose,
832
+ onError: onError,
833
+ );
834
+ }`;
835
+ }
836
+ return `Future<${responseType}> ${functionName}(${paramsType ? `${paramsType} params` : ""}) async {
837
+ return parsedArriRequest(
838
+ "$_baseUrl${schema.path}",
839
+ method: HttpMethod.${schema.method.toLowerCase()},
840
+ httpClient: _httpClient,
841
+ headers: _headers,
842
+ clientVersion: _clientVersion,
843
+ ${paramsType ? "params: params.toJson()," : ""}
844
+ parser: (body) ${schema.response ? `=> ${responseType}.fromJsonString(body)` : "{}"},
845
+ );
846
+ }`;
847
+ }
848
+ function getFunctionName(instancePath) {
849
+ const parts = instancePath.split(".");
850
+ return parts.pop() ?? "";
851
+ }
852
+ function dartWsRpcFromSchema(schema, context) {
853
+ const functionName = getFunctionName(context.instancePath);
854
+ let responseType;
855
+ let paramsType;
856
+ if (schema.response) {
857
+ responseType = `${context.modelPrefix}${validDartClassName(schema.response, context.modelPrefix)}`;
858
+ }
859
+ if (schema.params) {
860
+ paramsType = `${context.modelPrefix}${validDartClassName(schema.params, context.modelPrefix)}`;
861
+ }
862
+ return `Future<ArriWebsocketController<${responseType ?? "void"}, ${paramsType ?? "void"}>> ${functionName}() {
863
+ return arriWebsocketRequest(
864
+ "$_baseUrl${schema.path}",
865
+ headers: _headers,
866
+ clientVersion: _clientVersion,
867
+ parser: (msg) ${responseType ? `=> ${responseType}.fromJsonString(msg)` : "{}"},
868
+ serializer: (msg) ${paramsType ? "=> msg.toJsonString()" : '=> ""'},
869
+ );
870
+ }`;
871
+ }
872
+ function dartServiceFromSchema(schema, context) {
873
+ const rpcParts = [];
874
+ const subServices = [];
875
+ const subServiceParts = [];
876
+ const serviceName = getServiceName(
877
+ context.instancePath,
878
+ context.clientName
879
+ );
880
+ for (const key of Object.keys(schema)) {
881
+ const subSchema = schema[key];
882
+ if (isServiceDefinition(subSchema)) {
883
+ const subSchemaResult = dartServiceFromSchema(subSchema, {
884
+ clientName: context.clientName,
885
+ modelPrefix: context.modelPrefix,
886
+ generatedTypes: context.generatedTypes,
887
+ instancePath: `${context.instancePath}.${key}`,
888
+ schemaPath: `${context.schemaPath}.${key}`
889
+ });
890
+ if (subSchemaResult) {
891
+ subServiceParts.push(subSchemaResult);
892
+ subServices.push({
893
+ key: validDartIdentifier(key),
894
+ name: getServiceName(
895
+ `${context.instancePath}.${key}`,
896
+ context.clientName
897
+ )
898
+ });
899
+ }
900
+ continue;
952
901
  }
953
- switch (json["${discriminatorJsonKey}"]) {
954
- ${fromJsonCaseParts.join("\n ")}
902
+ if (isRpcDefinition(subSchema)) {
903
+ const subSchemaResult = dartRpcFromSchema(subSchema, {
904
+ clientName: context.clientName,
905
+ modelPrefix: context.modelPrefix,
906
+ generatedTypes: context.generatedTypes,
907
+ instancePath: `${context.instancePath}.${key}`,
908
+ schemaPath: `${context.schemaPath}.${key}`
909
+ });
910
+ if (subSchemaResult) {
911
+ rpcParts.push(subSchemaResult);
912
+ }
913
+ continue;
955
914
  }
956
- throw Exception(
957
- "Unable to decode ${className}. \\"\${json["${discriminatorJsonKey}"]}\\" doesn't match any of the accepted discriminator values.",
915
+ console.warn(
916
+ `Unknown schema in procedures at "${context.instancePath}".`
958
917
  );
959
918
  }
960
- Map<String, dynamic> toJson();
919
+ return `class ${serviceName}{
920
+ final http.Client? _httpClient;
921
+ final String _baseUrl;
922
+ final String _clientVersion = "${context.clientVersion ?? ""}";
923
+ late final FutureOr<Map<String, String>> Function()? _headers;
924
+ ${serviceName}({
925
+ http.Client? httpClient,
926
+ required String baseUrl,
927
+ FutureOr<Map<String, String>> Function()? headers,
928
+ }) : _httpClient = httpClient,
929
+ _baseUrl = baseUrl,
930
+ _headers = headers;
931
+
932
+ ${rpcParts.join("\n\n")}
933
+
934
+ ${subServices.map(
935
+ (service) => ` ${service.name} get ${service.key} => ${service.name}(
936
+ baseUrl: _baseUrl,
937
+ headers: _headers,
938
+ httpClient: _httpClient,
939
+ );`
940
+ ).join("\n\n")}
961
941
  }
962
- ${childContentParts.join("\n")}`;
963
- additionalOptions.existingClassNames.push(className);
964
- }
965
- const typeName = `${className}${isNullable ? "?" : ""}`;
942
+ ${subServiceParts.join("\n\n")}`;
943
+ }
944
+ function getServiceName(instancePath, clientName) {
945
+ return validDartClassName(
946
+ `${clientName}_${instancePath.split(".").join("_")}_Service`,
947
+ ""
948
+ );
949
+ }
950
+
951
+ function dartMapFromSchema(schema, context) {
952
+ const isNullable = outputIsNullable(schema, context);
953
+ const innerType = dartTypeFromSchema(schema.values, {
954
+ clientName: context.clientName,
955
+ modelPrefix: context.modelPrefix,
956
+ generatedTypes: context.generatedTypes,
957
+ instancePath: `${context.instancePath}/[entry]`,
958
+ schemaPath: `${context.schemaPath}/values`
959
+ });
960
+ const typeName = isNullable ? `Map<String, ${innerType.typeName}>?` : `Map<String, ${innerType.typeName}>`;
961
+ const defaultValue = isNullable ? "null" : "{}";
966
962
  return {
967
963
  typeName,
968
- fieldTemplate: fieldTemplateString(
969
- typeName,
970
- key,
971
- def.metadata?.description,
972
- def.metadata?.isDeprecated
973
- ),
974
- constructorTemplate: additionalOptions.isOptional ? `this.${key}` : `required this.${key}`,
975
- fromJsonTemplate: (input) => {
964
+ isNullable,
965
+ defaultValue,
966
+ fromJson(input) {
976
967
  if (isNullable) {
977
- return `${input} is Map<String, dynamic> ? ${className}.fromJson(${input}) : null`;
968
+ return `${input} is Map<String, dynamic>
969
+ ? (${input} as Map<String, dynamic>).map(
970
+ (_key_, _val_) => MapEntry(
971
+ _key_,
972
+ ${innerType.fromJson("_val_")},
973
+ ),
974
+ )
975
+ : null`;
978
976
  }
979
- return `${className}.fromJson(${input})`;
977
+ return `${input} is Map<String, dynamic>
978
+ ? (${input} as Map<String, dynamic>).map(
979
+ (_key_, _val_) => MapEntry(
980
+ _key_,
981
+ ${innerType.fromJson("_val_")},
982
+ ),
983
+ )
984
+ : <String, ${innerType.typeName}>{}`;
980
985
  },
981
- toJsonTemplate: (input) => {
982
- if (isNullable) {
983
- return `${input}?.toJson()`;
986
+ toJson(input) {
987
+ if (context.isOptional) {
988
+ return `${input}!.map((_key_, _val_) => MapEntry(_key_, ${innerType.toJson("_val_", "", "")},),)`;
984
989
  }
985
- return `${input}.toJson()`;
990
+ if (schema.nullable) {
991
+ return `${input}?.map((_key_, _val_) => MapEntry(_key_, ${innerType.toJson("_val_", "", "")},),)`;
992
+ }
993
+ return `${input}.map((_key_, _val_) => MapEntry(_key_, ${innerType.toJson("_val_", "", "")},),)`;
994
+ },
995
+ toQueryString() {
996
+ return `print(
997
+ "[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
986
998
  },
987
- content
999
+ content: innerType.content
988
1000
  };
989
1001
  }
990
- function dartRefFromJtdSchema(nodePath, def, additionalOptions) {
991
- const jsonKey = nodePath.split(".").pop() ?? "";
992
- const key = camelCaseWrapper(jsonKey);
993
- const className = pascalCase(def.ref, { normalize: true });
994
- const isNullable = additionalOptions.isOptional || (def.nullable ?? false);
995
- const typeName = `${className}${isNullable ? "?" : ""}`;
1002
+
1003
+ function dartRefFromSchema(schema, context) {
1004
+ const isNullable = outputIsNullable(schema, context);
1005
+ const className = sanitizeIdentifier(
1006
+ pascalCase(schema.ref, { normalize: true })
1007
+ );
1008
+ const finalClassName = `${context.modelPrefix}${className}`;
1009
+ const typeName = isNullable ? `${finalClassName}?` : finalClassName;
1010
+ const defaultValue = isNullable ? `null` : `${finalClassName}.empty()`;
996
1011
  return {
997
1012
  typeName,
998
- fieldTemplate: fieldTemplateString(
999
- typeName,
1000
- key,
1001
- def.metadata?.description,
1002
- def.metadata?.isDeprecated
1003
- ),
1004
- constructorTemplate: additionalOptions.isOptional ? `this.${key}` : `required this.${key}`,
1005
- fromJsonTemplate(input) {
1013
+ isNullable,
1014
+ defaultValue,
1015
+ fromJson(input) {
1006
1016
  if (isNullable) {
1007
- return `${input} is Map<String, dynamic> ? ${className}.fromJson(${input}) : null`;
1017
+ return `${input} is Map<String, dynamic>
1018
+ ? ${finalClassName}.fromJson(${input})
1019
+ : null`;
1008
1020
  }
1009
- return `${className}.fromJson(${input})`;
1021
+ return `${input} is Map<String, dynamic>
1022
+ ? ${finalClassName}.fromJson(${input})
1023
+ : ${finalClassName}.empty()`;
1010
1024
  },
1011
- toJsonTemplate(input) {
1012
- if (isNullable) {
1025
+ toJson(input) {
1026
+ if (context.isOptional) {
1027
+ return `${input}!.toJson()`;
1028
+ }
1029
+ if (schema.nullable) {
1013
1030
  return `${input}?.toJson()`;
1014
1031
  }
1015
1032
  return `${input}.toJson()`;
1016
1033
  },
1034
+ toQueryString() {
1035
+ return `print(
1036
+ "[WARNING] nested objects cannot be serialized to query params. Skipping field at ${context.instancePath}.")`;
1037
+ },
1017
1038
  content: ""
1018
1039
  };
1019
1040
  }
1020
1041
 
1021
- export { DartClientGenerator, createDartClient, dartClassFromJtdSchema, dartClientGenerator, dartHttpRpcFromSchema, dartRpcFromDefinition, dartServiceFromDefinition, dartTypeFromJtdSchema, dartWsRpcFromSchema, getAnnotations };
1042
+ const dartClientGenerator = defineGeneratorPlugin(
1043
+ (options) => {
1044
+ return {
1045
+ async generator(def) {
1046
+ if (!options.outputFile) {
1047
+ throw new Error(
1048
+ 'Missing "outputFile" cannot generate dart code'
1049
+ );
1050
+ }
1051
+ try {
1052
+ const result = createDartClient(def, options);
1053
+ const destination = path.resolve(options.outputFile);
1054
+ await fs.writeFile(destination, result);
1055
+ if (options.format !== false) {
1056
+ execSync(`dart format ${destination}`, {
1057
+ stdio: "inherit"
1058
+ });
1059
+ }
1060
+ } catch (err) {
1061
+ console.error(err);
1062
+ }
1063
+ },
1064
+ options
1065
+ };
1066
+ }
1067
+ );
1068
+ function createDartClient(def, options) {
1069
+ const typeParts = [];
1070
+ const context = {
1071
+ clientName: options.clientName ?? "Client",
1072
+ modelPrefix: options.modelPrefix ?? "",
1073
+ generatedTypes: [],
1074
+ instancePath: "",
1075
+ schemaPath: "",
1076
+ clientVersion: def.info?.version
1077
+ };
1078
+ const services = unflattenProcedures(def.procedures);
1079
+ const subServices = [];
1080
+ const subServiceParts = [];
1081
+ const rpcParts = [];
1082
+ for (const key of Object.keys(services)) {
1083
+ const subSchema = services[key];
1084
+ if (isServiceDefinition(subSchema)) {
1085
+ const service = dartServiceFromSchema(subSchema, {
1086
+ clientName: context.clientName,
1087
+ modelPrefix: context.modelPrefix,
1088
+ generatedTypes: context.generatedTypes,
1089
+ instancePath: key,
1090
+ schemaPath: `procedures.${key}`,
1091
+ clientVersion: context.clientVersion
1092
+ });
1093
+ if (service) {
1094
+ subServiceParts.push(service);
1095
+ subServices.push({
1096
+ key: validDartIdentifier(key),
1097
+ name: getServiceName(key, context.clientName)
1098
+ });
1099
+ }
1100
+ continue;
1101
+ }
1102
+ if (isRpcDefinition(subSchema)) {
1103
+ const rpc = dartRpcFromSchema(subSchema, {
1104
+ clientName: context.clientName,
1105
+ modelPrefix: context.modelPrefix,
1106
+ generatedTypes: context.generatedTypes,
1107
+ instancePath: key,
1108
+ schemaPath: `procedures.${key}`
1109
+ });
1110
+ if (rpc) {
1111
+ rpcParts.push(rpc);
1112
+ }
1113
+ continue;
1114
+ }
1115
+ console.warn(`Unknown schema in procedures at "${key}"`);
1116
+ }
1117
+ for (const key of Object.keys(def.definitions)) {
1118
+ const subDef = def.definitions[key];
1119
+ const result = dartTypeFromSchema(subDef, {
1120
+ clientName: context.clientName,
1121
+ modelPrefix: context.modelPrefix,
1122
+ generatedTypes: context.generatedTypes,
1123
+ instancePath: `/${key}`,
1124
+ schemaPath: `/${key}`
1125
+ });
1126
+ if (result.content) {
1127
+ typeParts.push(result.content);
1128
+ }
1129
+ }
1130
+ if (rpcParts.length === 0 && subServiceParts.length === 0) {
1131
+ const heading = `// this file was autogenerated by arri
1132
+ // ignore_for_file: type=lint, unused_field, unnecessary_cast
1133
+ import 'dart:convert';
1134
+ import 'package:arri_client/arri_client.dart';`;
1135
+ return `${heading}
1136
+
1137
+ ${typeParts.join("\n\n")}`;
1138
+ }
1139
+ const clientName = validDartClassName(context.clientName, "");
1140
+ return `// this file was autogenerated by arri
1141
+ // ignore_for_file: type=lint, unused_field, unnecessary_cast
1142
+ import 'dart:async';
1143
+ import 'dart:convert';
1144
+ import 'package:arri_client/arri_client.dart';
1145
+ import 'package:http/http.dart' as http;
1146
+
1147
+ class ${clientName} {
1148
+ final http.Client? _httpClient;
1149
+ final String _baseUrl;
1150
+ final String _clientVersion = "${context.clientVersion ?? ""}";
1151
+ late final FutureOr<Map<String, String>> Function()? _headers;
1152
+ ${clientName}({
1153
+ http.Client? httpClient,
1154
+ required String baseUrl,
1155
+ FutureOr<Map<String, String>> Function()? headers,
1156
+ }) : _httpClient = httpClient,
1157
+ _baseUrl = baseUrl,
1158
+ _headers = headers;
1159
+
1160
+ ${rpcParts.join("\n\n")}
1161
+
1162
+ ${subServices.map(
1163
+ (service) => ` ${service.name} get ${service.key} => ${service.name}(
1164
+ baseUrl: _baseUrl,
1165
+ headers: _headers,
1166
+ httpClient: _httpClient,
1167
+ );`
1168
+ ).join("\n\n")}
1169
+ }
1170
+
1171
+ ${subServiceParts.join("\n\n")}
1172
+
1173
+ ${typeParts.join("\n\n")}`;
1174
+ }
1175
+ function dartTypeFromSchema(schema, context) {
1176
+ if (isSchemaFormType(schema)) {
1177
+ switch (schema.type) {
1178
+ case "string":
1179
+ return dartStringFromSchema(schema, context);
1180
+ case "boolean":
1181
+ return dartBoolFromSchema(schema, context);
1182
+ case "timestamp":
1183
+ return dartDateTimeFromSchema(schema, context);
1184
+ case "float32":
1185
+ case "float64":
1186
+ return dartDoubleFromSchema(schema, context);
1187
+ case "int8":
1188
+ case "uint8":
1189
+ case "int16":
1190
+ case "uint16":
1191
+ case "int32":
1192
+ case "uint32":
1193
+ return dartIntFromSchema(schema, context);
1194
+ case "int64":
1195
+ case "uint64":
1196
+ return dartBigIntFromSchema(schema, context);
1197
+ default:
1198
+ schema.type;
1199
+ throw new Error(`Unhandled schema.type ${schema.type}`);
1200
+ }
1201
+ }
1202
+ if (isSchemaFormEnum(schema)) {
1203
+ return dartEnumFromSchema(schema, context);
1204
+ }
1205
+ if (isSchemaFormProperties(schema)) {
1206
+ return dartClassFromSchema(schema, context);
1207
+ }
1208
+ if (isSchemaFormElements(schema)) {
1209
+ return dartListFromSchema(schema, context);
1210
+ }
1211
+ if (isSchemaFormValues(schema)) {
1212
+ return dartMapFromSchema(schema, context);
1213
+ }
1214
+ if (isSchemaFormDiscriminator(schema)) {
1215
+ return dartSealedClassFromSchema(schema, context);
1216
+ }
1217
+ if (isSchemaFormRef(schema)) {
1218
+ return dartRefFromSchema(schema, context);
1219
+ }
1220
+ return dartAnyFromSchema(schema, context);
1221
+ }
1222
+
1223
+ export { createDartClient, dartClientGenerator, dartTypeFromSchema };