@arrirpc/codegen-dart 0.49.1 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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, defineClientGeneratorPlugin, 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
- }
134
+ if (!canUseIdentifier(finalIdentifier)) {
135
+ return `k_${finalIdentifier}`;
114
136
  }
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")}`;
121
- }
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 ")}
143
- }
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
- `;
137
+ return finalIdentifier;
202
138
  }
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 = defineClientGeneratorPlugin(
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 };