@arrirpc/codegen-ts 0.54.0 → 0.57.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.cjs CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const codegenUtils = require('@arrirpc/codegen-utils');
4
- const schema = require('@arrirpc/schema');
5
4
  const fs = require('fs');
6
5
  const prettier = require('prettier');
7
6
 
@@ -9,314 +8,28 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
9
8
 
10
9
  const prettier__default = /*#__PURE__*/_interopDefaultCompat(prettier);
11
10
 
12
- const typescriptClientGenerator = codegenUtils.defineGeneratorPlugin(
13
- (options) => ({
14
- generator: async (def) => {
15
- if (!options.clientName) {
16
- throw new Error("Name is requires");
17
- }
18
- if (!options.outputFile) {
19
- throw new Error("No output file specified");
20
- }
21
- if (Object.keys(def.procedures).length <= 0) {
22
- console.warn(
23
- `No procedures defined in AppDefinition. Only data models will be outputted.`
24
- );
25
- }
26
- const result = await createTypescriptClient(def, options);
27
- fs.writeFileSync(options.outputFile, result);
28
- },
29
- options
30
- })
31
- );
32
- async function createTypescriptClient(def, options) {
33
- const clientName = codegenUtils.pascalCase(options.clientName);
34
- const services = codegenUtils.unflattenProcedures(def.procedures);
35
- const serviceFieldParts = [];
36
- const serviceInitializationParts = [];
37
- const procedureParts = [];
38
- const subContentParts = [];
39
- const existingTypeNames = [];
40
- const rpcOptions = {
41
- ...options,
42
- versionNumber: def.info?.version ?? "",
43
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
44
- typesNeedingParser: Object.keys(def.definitions),
45
- hasSseProcedures: false,
46
- hasWsProcedures: false
47
- };
48
- Object.keys(services).forEach((key) => {
49
- const item = services[key];
50
- if (codegenUtils.isRpcDefinition(item)) {
51
- const rpc = tsRpcFromDefinition(key, item, rpcOptions);
52
- if (rpc) {
53
- procedureParts.push(rpc);
54
- }
55
- return;
56
- }
57
- if (codegenUtils.isServiceDefinition(item)) {
58
- const serviceName = codegenUtils.pascalCase(`${clientName}_${key}`);
59
- const service = tsServiceFromDefinition(
60
- serviceName,
61
- item,
62
- rpcOptions
63
- );
64
- serviceFieldParts.push(`${key}: ${serviceName}Service;`);
65
- serviceInitializationParts.push(
66
- `this.${key} = new ${serviceName}Service(options);`
67
- );
68
- subContentParts.push(service);
69
- }
70
- });
71
- for (const key of rpcOptions.typesNeedingParser) {
72
- const schema = def.definitions[key];
73
- if (codegenUtils.isSchemaFormProperties(schema) || codegenUtils.isSchemaFormDiscriminator(schema)) {
74
- const type = tsTypeFromJtdSchema(key, schema, rpcOptions, {
75
- isOptional: false,
76
- existingTypeNames
77
- });
78
- subContentParts.push(type.content);
79
- }
80
- }
81
- const importParts = ["arriRequest"];
82
- if (rpcOptions.hasSseProcedures) {
83
- importParts.push("arriSseRequest");
84
- importParts.push("type SseOptions");
85
- importParts.push("type EventSourceController");
86
- }
87
- if (rpcOptions.hasWsProcedures) {
88
- importParts.push("arriWsRequest");
89
- importParts.push("type WsOptions");
90
- }
91
- if (procedureParts.length === 0 && serviceFieldParts.length === 0) {
92
- const result2 = `/* eslint-disable */
93
- // @ts-nocheck
94
- // this file was autogenerated by arri-codegen-ts
95
-
96
- ${subContentParts.join("\n")}`;
97
- return prettier__default.format(result2, {
98
- parser: "typescript",
99
- ...options.prettierOptions
100
- });
101
- }
102
- const result = `/* eslint-disable */
103
- // @ts-nocheck
104
- // this file was autogenerated by arri-codegen-ts
105
- import { ${importParts.join(", ")} } from '@arrirpc/client';
106
-
107
- interface ${clientName}Options {
108
- baseUrl?: string;
109
- headers?: Record<string, string | undefined> | (() => Record<string, string | undefined> | Promise<Record<string, string | undefined>>);
110
- }
111
-
112
- export class ${clientName} {
113
- private readonly baseUrl: string;
114
- private readonly headers: Record<string, string | undefined> | (() => Record<string, string | undefined> | Promise<Record<string, string | undefined>>)
115
- private readonly clientVersion = '${rpcOptions.versionNumber}';
116
- ${serviceFieldParts.join("\n ")}
117
-
118
- constructor(options: ${clientName}Options = {}) {
119
- this.baseUrl = options.baseUrl ?? "";
120
- this.headers = options.headers ?? {};
121
- ${serviceInitializationParts.join(";\n ")}
122
- }
123
- ${procedureParts.join("\n ")}
124
- }
125
-
126
- ${subContentParts.join("\n")}
127
- `;
128
- return await prettier__default.format(result, {
129
- parser: "typescript",
130
- ...options.prettierOptions
131
- });
132
- }
133
- function tsRpcFromDefinition(key, schema, options) {
134
- if (schema.transport === "http") {
135
- return tsHttpRpcFromDefinition(key, schema, options);
136
- }
137
- if (schema.transport === "ws") {
138
- return tsWsRpcFromDefinition(key, schema, options);
139
- }
140
- console.warn(
141
- `[codegen-ts] Unsupported transport "${schema.transport}". Ignoring rpc.`
142
- );
143
- return "";
144
- }
145
- function tsHttpRpcFromDefinition(key, schema, options) {
146
- const paramName = codegenUtils.pascalCase(schema.params ?? "");
147
- const responseName = codegenUtils.pascalCase(schema.response ?? "");
148
- const paramsInput = schema.params ? `params: ${paramName}` : "";
149
- const hasInput = paramName.length > 0;
150
- const hasOutput = responseName.length > 0;
151
- let serializerPart = `(_) => {}`;
152
- let parserPart = `(_) => {}`;
153
- if (hasInput) {
154
- serializerPart = `$$${paramName}.serialize`;
155
- }
156
- if (hasOutput) {
157
- parserPart = `$$${responseName}.parse`;
158
- }
159
- const paramsOutput = hasInput ? `params` : `params: undefined`;
160
- if (schema.isEventStream) {
161
- options.hasSseProcedures = true;
162
- return `${getJsDocComment({ isDeprecated: schema.isDeprecated, description: schema.description })}${key}(${paramsInput.length ? `${paramsInput}, ` : ""}options: SseOptions<${schema.response ?? "undefined"}>): EventSourceController {
163
- return arriSseRequest<${schema.response ?? "undefined"}, ${schema.params ?? "undefined"}>({
164
- url: \`\${this.baseUrl}${schema.path}\`,
165
- method: "${schema.method}",
166
- headers: this.headers,
167
- ${paramsOutput},
168
- parser: ${parserPart},
169
- serializer: ${serializerPart},
170
- clientVersion: this.clientVersion,
171
- }, options);
172
- }`;
173
- }
174
- return `${getJsDocComment({ isDeprecated: schema.isDeprecated, description: schema.description })}${key}(${paramsInput}) {
175
- return arriRequest<${schema.response ?? "undefined"}, ${schema.params ?? "undefined"}>({
176
- url: \`\${this.baseUrl}${schema.path}\`,
177
- method: "${schema.method}",
178
- headers: this.headers,
179
- ${paramsOutput},
180
- parser: ${parserPart},
181
- serializer: ${serializerPart},
182
- clientVersion: this.clientVersion,
183
- });
184
- }`;
185
- }
186
- function tsWsRpcFromDefinition(key, schema, options) {
187
- options.hasWsProcedures = true;
188
- const paramName = schema.params ? codegenUtils.pascalCase(schema.params) : "undefined";
189
- const responseName = schema.response ? codegenUtils.pascalCase(schema.response) : "undefined";
190
- const hasInput = paramName.length > 0 && paramName !== "undefined";
191
- const hasOutput = responseName.length > 0 && paramName !== "undefined";
192
- let serializerPart = `(_) => {}`;
193
- let parserPart = `(_) => {}`;
194
- if (hasInput) {
195
- serializerPart = `$$${paramName}.serialize`;
196
- }
197
- if (hasOutput) {
198
- parserPart = `$$${responseName}.parse`;
199
- }
200
- return `${getJsDocComment(schema)}${key}(options: WsOptions<${responseName}> = {}) {
201
- return arriWsRequest<${paramName}, ${responseName}>({
202
- url: \`\${this.baseUrl}${schema.path}\`,
203
- headers: this.headers,
204
- parser: ${parserPart},
205
- serializer: ${serializerPart},
206
- onOpen: options.onOpen,
207
- onClose: options.onClose,
208
- onError: options.onError,
209
- onConnectionError: options.onConnectionError,
210
- onMessage: options.onMessage,
211
- clientVersion: this.clientVersion,
212
- })
213
- }`;
214
- }
215
- function tsServiceFromDefinition(name, schema, options) {
216
- const serviceFieldParts = [];
217
- const serviceConstructorParts = [];
218
- const subServiceContent = [];
219
- const rpcContent = [];
220
- Object.keys(schema).forEach((key) => {
221
- const def = schema[key];
222
- if (codegenUtils.isRpcDefinition(def)) {
223
- const rpc = tsRpcFromDefinition(key, def, options);
224
- if (rpc) {
225
- rpcContent.push(rpc);
226
- }
227
- return;
228
- }
229
- if (codegenUtils.isServiceDefinition(def)) {
230
- const serviceName = codegenUtils.pascalCase(`${name}_${key}`);
231
- const service = tsServiceFromDefinition(serviceName, def, options);
232
- serviceFieldParts.push(`${key}: ${serviceName}Service;`);
233
- serviceConstructorParts.push(
234
- `this.${key} = new ${serviceName}Service(options);`
235
- );
236
- subServiceContent.push(service);
237
- }
238
- });
239
- return `export class ${name}Service {
240
- private readonly baseUrl: string;
241
- private readonly headers: Record<string, string | undefined> | (() => Record<string, string | undefined> | Promise<Record<string, string | undefined>>);
242
- private readonly clientVersion = '${options.versionNumber}';
243
- ${serviceFieldParts.join("\n ")}
244
-
245
- constructor(options: ${codegenUtils.pascalCase(
246
- `${options.clientName}_Options`
247
- )} = {}) {
248
- this.baseUrl = options.baseUrl ?? '';
249
- this.headers = options.headers ?? {};
250
- ${serviceConstructorParts.join("\n ")}
251
- }
252
- ${rpcContent.join("\n ")}
253
- }
254
- ${subServiceContent.join("\n")}`;
255
- }
256
- function tsTypeFromJtdSchema(nodePath, def, options, additionalOptions) {
257
- if (additionalOptions.logDef) {
258
- console.log(def);
259
- }
260
- if (codegenUtils.isSchemaFormType(def)) {
261
- return tsScalarFromJtdSchema(nodePath, def, options, additionalOptions);
262
- }
263
- if (codegenUtils.isSchemaFormProperties(def)) {
264
- return tsObjectFromJtdSchema(nodePath, def, options, additionalOptions);
265
- }
266
- if (codegenUtils.isSchemaFormEnum(def)) {
267
- return tsEnumFromJtdSchema(nodePath, def, options, additionalOptions);
268
- }
269
- if (codegenUtils.isSchemaFormElements(def)) {
270
- return tsArrayFromJtdSchema(nodePath, def, options, additionalOptions);
271
- }
272
- if (codegenUtils.isSchemaFormDiscriminator(def)) {
273
- return tsDiscriminatedUnionFromJtdSchema(
274
- nodePath,
275
- def,
276
- options,
277
- additionalOptions
278
- );
279
- }
280
- if (codegenUtils.isSchemaFormValues(def)) {
281
- return tsRecordFromJtdSchema(nodePath, def, options, additionalOptions);
282
- }
283
- if (codegenUtils.isSchemaFormRef(def)) {
284
- return tsRefFromJtdSchema(nodePath, def, options, additionalOptions);
285
- }
286
- return tsAnyFromJtdSchema(nodePath, def, options, additionalOptions);
287
- }
288
- function maybeOptionalKey(keyName, isOptional = false) {
289
- if (isOptional) {
290
- return `${keyName}?`;
291
- }
292
- return keyName;
293
- }
294
- function maybeNullType(typeName, isNullable = false) {
295
- if (isNullable) {
296
- return `${typeName} | null`;
297
- }
298
- return typeName;
299
- }
300
- function tsAnyFromJtdSchema(nodePath, def, options, additionalOptions) {
301
- const key = nodePath.split(".").pop() ?? "";
302
- const isNullable = def.nullable ?? false;
303
- const isOptional = additionalOptions.isOptional;
11
+ function tsAnyFromSchema(schema, context) {
12
+ const typeName = "any";
13
+ const defaultValue = schema.nullable ? "null" : "undefined";
304
14
  return {
305
- tsType: "any",
306
- schema: def,
307
- fieldTemplate: `${maybeOptionalKey(key, isOptional)}: ${maybeNullType(
308
- "any",
309
- isNullable
310
- )}`,
311
- fromJsonTemplate(input) {
312
- return input;
313
- },
314
- toJsonTemplate(input) {
315
- return `JSON.stringify(${input})`;
15
+ typeName,
16
+ defaultValue,
17
+ validationTemplate(_) {
18
+ return "true";
19
+ },
20
+ fromJsonTemplate(input, target) {
21
+ return `${target} = ${input}`;
22
+ },
23
+ toJsonTemplate(input, target) {
24
+ return `${target} += JSON.stringify(${input})`;
25
+ },
26
+ toQueryStringTemplate(_input, _target, _key) {
27
+ return `console.warn("[WARNING] Cannot serialize any's to query string. Skipping property at ${context.instancePath}.")`;
316
28
  },
317
29
  content: ""
318
30
  };
319
31
  }
32
+
320
33
  function getJsDocComment(metadata) {
321
34
  const descriptionParts = [];
322
35
  if (metadata?.description?.length) {
@@ -336,524 +49,1171 @@ ${descriptionParts.join("\n")}
336
49
  */
337
50
  `;
338
51
  }
339
- function tsScalarFromJtdSchema(nodePath, def, options, additionalOptions) {
340
- const key = nodePath.split(".").pop() ?? "";
341
- const isNullable = def.nullable ?? false;
342
- const isOptional = additionalOptions.isOptional;
343
- switch (def.type) {
344
- case "boolean":
345
- return {
346
- tsType: "boolean",
347
- schema: def,
348
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
349
- key,
350
- isOptional
351
- )}: ${maybeNullType("boolean", isNullable)}`,
352
- fromJsonTemplate(input) {
353
- if (isOptional) {
354
- return `typeof ${input} === 'boolean' ? ${input} : undefined`;
355
- }
356
- if (isNullable) {
357
- return `typeof ${input} === 'boolean' ? ${input} : null`;
358
- }
359
- return `typeof ${input} === 'boolean' ? ${input} : false`;
360
- },
361
- toJsonTemplate(input) {
362
- if (isNullable) {
363
- return `typeof ${input} === 'boolean' ? ${input} : null`;
364
- }
365
- return `typeof ${input} === 'boolean' ? ${input} : false`;
366
- },
367
- content: ""
368
- };
369
- case "string":
370
- return {
371
- tsType: "string",
372
- schema: def,
373
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
374
- key,
375
- isOptional
376
- )}: ${maybeNullType("string", isNullable)}`,
377
- fromJsonTemplate(input) {
378
- if (isOptional) {
379
- return `typeof ${input} === 'string' ? ${input} : undefined`;
380
- }
381
- if (isNullable) {
382
- return `typeof ${input} === 'string' ? ${input} : null`;
383
- }
384
- return `typeof ${input} === 'string' ? ${input} : ''`;
385
- },
386
- toJsonTemplate(input) {
387
- if (isNullable) {
388
- return `typeof ${input} === 'string' ? \`"${input}"\` : null`;
389
- }
390
- return `typeof ${input} === 'string' ? \`"${input}"\` : ""`;
391
- },
392
- content: ""
393
- };
394
- case "timestamp":
395
- return {
396
- tsType: "Date",
397
- schema: def,
398
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
399
- key,
400
- isOptional
401
- )}: ${maybeNullType("Date", isNullable)}`,
402
- fromJsonTemplate(input) {
403
- if (isOptional) {
404
- return `typeof ${input} === 'string' ? new Date(${input}) : undefined`;
405
- }
406
- if (isNullable) {
407
- return `typeof ${input} === 'string' ? new Date(${input}) : null`;
408
- }
409
- return `typeof ${input} === 'string' ? new Date(${input}) : new Date(0)`;
410
- },
411
- toJsonTemplate(input) {
412
- if (isOptional || isNullable) {
413
- return `typeof ${input} === 'object' && ${input} instanceof Date ? "${input}.toISOString()" : null`;
414
- }
415
- return `"${input}.toISOString()"`;
416
- },
417
- content: ""
418
- };
419
- case "float32":
420
- case "float64":
421
- case "int8":
422
- case "int16":
423
- case "int32":
424
- case "uint8":
425
- case "uint16":
426
- case "uint32":
427
- return {
428
- tsType: "number",
429
- schema: def,
430
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
431
- key,
432
- isOptional
433
- )}: ${maybeNullType("number", isNullable)}`,
434
- fromJsonTemplate(input) {
435
- if (isOptional) {
436
- return `typeof ${input} === 'number' ? ${input} : undefined`;
437
- }
438
- if (isNullable) {
439
- return `typeof ${input} === 'number' ? ${input} : null`;
440
- }
441
- return `typeof ${input} === 'number' ? ${input} : 0`;
442
- },
443
- toJsonTemplate(input) {
444
- return `JSON.stringify(${input})`;
445
- },
446
- content: ""
447
- };
448
- case "int64":
449
- case "uint64":
450
- return {
451
- tsType: "bigint",
452
- schema: def,
453
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
454
- key,
455
- isOptional
456
- )}: ${maybeNullType("bigint", isNullable)}`,
457
- fromJsonTemplate(input) {
458
- if (isOptional) {
459
- return `typeof ${input} === 'string' ? BigInt(${input}) : undefined`;
460
- }
461
- if (isNullable) {
462
- return `typeof ${input} === 'string' ? BigInt(${input}) : null`;
463
- }
464
- return `typeof ${input} === 'string' ? BigInt(${input}) : BigInt("0")`;
465
- },
466
- toJsonTemplate(input) {
467
- if (isOptional || isNullable) {
468
- return `${input}?.toString()`;
469
- }
470
- return `${input}.toString()`;
471
- },
472
- content: ""
473
- };
474
- default:
475
- def.type;
476
- throw new Error(`Invalid type in SchemaFormType`);
52
+ const illegalChars = "!#^&*()-+=?/][{}|\\~`'\"";
53
+ function validVarName(name) {
54
+ if (codegenUtils.stringStartsWithNumber(name)) {
55
+ return `_${codegenUtils.removeDisallowedChars(name, illegalChars)}`;
477
56
  }
57
+ return codegenUtils.removeDisallowedChars(name, illegalChars);
478
58
  }
479
- function getTypeName(nodePath, def) {
480
- if (def.metadata?.id) {
481
- return codegenUtils.pascalCase(def.metadata.id);
59
+ function getTsTypeName(schema, context) {
60
+ if (schema.metadata?.id) {
61
+ const name2 = codegenUtils.pascalCase(schema.metadata.id, { normalize: true });
62
+ return validVarName(name2);
482
63
  }
483
- return codegenUtils.pascalCase(nodePath.split(".").join("_"));
64
+ if (context.discriminatorParent && context.discriminatorValue) {
65
+ const name2 = codegenUtils.pascalCase(
66
+ `${context.discriminatorParent}_${context.discriminatorValue}`,
67
+ { normalize: true }
68
+ );
69
+ return validVarName(name2);
70
+ }
71
+ const name = codegenUtils.pascalCase(context.instancePath.split("/").join("_"), {
72
+ normalize: true
73
+ });
74
+ return validVarName(name);
75
+ }
76
+
77
+ function tsArrayFromSchema(schema, context) {
78
+ const innerType = tsTypeFromSchema(schema.elements, {
79
+ clientName: context.clientName,
80
+ typePrefix: context.typePrefix,
81
+ generatedTypes: context.generatedTypes,
82
+ instancePath: `${context.instancePath}/[element]`,
83
+ schemaPath: `${context.schemaPath}/elements`,
84
+ discriminatorParent: "",
85
+ discriminatorKey: "",
86
+ discriminatorValue: "",
87
+ versionNumber: context.versionNumber,
88
+ usedFeatures: context.usedFeatures
89
+ });
90
+ const typeName = `(${innerType.typeName})[]`;
91
+ const defaultValue = schema.nullable ? "null" : "[]";
92
+ return {
93
+ typeName: schema.nullable ? `${typeName} | null` : typeName,
94
+ defaultValue,
95
+ validationTemplate(input) {
96
+ const mainPart = `Array.isArray(${input})
97
+ && ${input}.every(
98
+ (_element) => ${innerType.validationTemplate("_element")}
99
+ )`;
100
+ if (schema.nullable) {
101
+ return `((${mainPart}) ||
102
+ ${input} === null)`;
103
+ }
104
+ return mainPart;
105
+ },
106
+ fromJsonTemplate(input, target) {
107
+ return `if (Array.isArray(${input})) {
108
+ ${target} = [];
109
+ for (const ${target}El of ${input}) {
110
+ let ${target}ElValue: ${innerType.typeName};
111
+ ${innerType.fromJsonTemplate(`${target}El`, `${target}ElValue`)}
112
+ ${target}.push(${target}ElValue);
113
+ }
114
+ } else {
115
+ ${target} = ${defaultValue};
116
+ }`;
117
+ },
118
+ toJsonTemplate(input, target) {
119
+ const elVar = `_${codegenUtils.camelCase(validVarName(input.split(".").join("_")), { normalize: true })}El`;
120
+ const elKeyVar = `${elVar}Key`;
121
+ if (schema.nullable) {
122
+ return `if (${input} !== null) {
123
+ ${target} += '[';
124
+ for (let i = 0; i < ${input}.length; i++) {
125
+ if (i !== 0) ${target} += ',';
126
+ const ${elVar} = ${input}[i];
127
+ ${innerType.toJsonTemplate(elVar, target, elKeyVar)}
128
+ }
129
+ ${target} += ']';
130
+ } else {
131
+ ${target} += 'null';
132
+ }`;
133
+ }
134
+ return `${target} += '[';
135
+ for (let i = 0; i < ${input}.length; i++) {
136
+ if (i !== 0) ${target} += ',';
137
+ const ${elVar} = ${input}[i];
138
+ ${innerType.toJsonTemplate(elVar, target, elKeyVar)}
139
+ }
140
+ ${target} += ']';`;
141
+ },
142
+ toQueryStringTemplate(_input, _target, _key) {
143
+ return `console.warn('[WARNING] Cannot serialize arrays to query string. Skipping property at ${context.instancePath}.')`;
144
+ },
145
+ content: innerType.content
146
+ };
484
147
  }
485
- function tsObjectFromJtdSchema(nodePath, def, options, additionalOptions) {
486
- const typeName = getTypeName(nodePath, def);
487
- const key = nodePath.split(".").pop() ?? "";
488
- const fieldNames = [];
148
+
149
+ function tsObjectFromSchema(schema, context) {
150
+ const typeName = getTsTypeName(schema, context);
151
+ const prefixedTypeName = `${context.typePrefix}${typeName}`;
152
+ const defaultValue = schema.nullable ? "null" : `$$${prefixedTypeName}.new()`;
153
+ const result = {
154
+ typeName: schema.nullable ? `${prefixedTypeName} | null` : prefixedTypeName,
155
+ defaultValue,
156
+ validationTemplate(input) {
157
+ if (schema.nullable) {
158
+ return `($$${prefixedTypeName}.validate(${input}) || ${input} === null)`;
159
+ }
160
+ return `$$${prefixedTypeName}.validate(${input})`;
161
+ },
162
+ fromJsonTemplate(input, target) {
163
+ return `if (isObject(${input})) {
164
+ ${target} = $$${prefixedTypeName}.fromJson(${input});
165
+ } else {
166
+ ${target} = ${defaultValue};
167
+ }`;
168
+ },
169
+ toJsonTemplate(input, target) {
170
+ if (schema.nullable) {
171
+ return `if (${input} !== null) {
172
+ ${target} += $$${prefixedTypeName}.toJsonString(${input});
173
+ } else {
174
+ ${target} += 'null';
175
+ }`;
176
+ }
177
+ return `${target} += $$${prefixedTypeName}.toJsonString(${input});`;
178
+ },
179
+ toQueryStringTemplate(_input, _target) {
180
+ return `console.warn("[WARNING] Cannot serialize nested objects to query string. Skipping property at ${context.instancePath}.")`;
181
+ },
182
+ content: ""
183
+ };
184
+ if (context.generatedTypes.includes(typeName)) {
185
+ return result;
186
+ }
187
+ const newParts = [];
489
188
  const fieldParts = [];
189
+ const fromJsonParts = [];
190
+ const constructionParts = [];
191
+ const toJsonParts = [];
192
+ const toQueryParts = [];
193
+ const validationParts = ["isObject(input)"];
490
194
  const subContentParts = [];
491
- const parserParts = [];
492
- if (additionalOptions.discriminatorKey && additionalOptions.discriminatorKeyValue) {
493
- parserParts.push(
494
- `${additionalOptions.discriminatorKey}: '${additionalOptions.discriminatorKeyValue}'`
195
+ let hasKey = false;
196
+ if (context.discriminatorParent && context.discriminatorValue && context.discriminatorKey) {
197
+ hasKey = true;
198
+ const key = validVarName(
199
+ codegenUtils.camelCase(context.discriminatorKey, { normalize: true })
495
200
  );
496
- fieldParts.push(
497
- `${additionalOptions.discriminatorKey}: "${additionalOptions.discriminatorKeyValue}"`
201
+ fieldParts.push(`${key}: "${context.discriminatorValue}",`);
202
+ fromJsonParts.push(`const _${key} = "${context.discriminatorValue}"`);
203
+ toJsonParts.push(`json += '"${key}":"${context.discriminatorValue}"'`);
204
+ toQueryParts.push(
205
+ `queryParts.push('${context.discriminatorKey}=${context.discriminatorValue}');`
498
206
  );
499
- fieldNames.push(additionalOptions.discriminatorKey);
207
+ validationParts.push(
208
+ `input.${key} === '${context.discriminatorValue}'`
209
+ );
210
+ newParts.push(`${key}: "${context.discriminatorValue}",`);
211
+ constructionParts.push(`${key}: _${key},`);
500
212
  }
501
- if (def.properties) {
502
- for (const propKey of Object.keys(def.properties)) {
503
- const propSchema = def.properties[propKey];
504
- const type = tsTypeFromJtdSchema(
505
- `${nodePath}.${propKey}`,
506
- propSchema,
507
- options,
508
- {
509
- isOptional: false,
510
- existingTypeNames: additionalOptions.existingTypeNames
511
- }
512
- );
513
- parserParts.push(
514
- `${propKey}: ${type.fromJsonTemplate(`input.${propKey}`)}`
515
- );
516
- fieldParts.push(type.fieldTemplate);
517
- fieldNames.push(propKey);
518
- if (type.content) {
519
- subContentParts.push(type.content);
520
- }
213
+ for (const key of Object.keys(schema.properties)) {
214
+ const subSchema = schema.properties[key];
215
+ const prop = tsTypeFromSchema(subSchema, {
216
+ clientName: context.clientName,
217
+ typePrefix: context.typePrefix,
218
+ generatedTypes: context.generatedTypes,
219
+ instancePath: `/${typeName}/${key}`,
220
+ schemaPath: `/${typeName}/properties/${key}`,
221
+ discriminatorParent: "",
222
+ discriminatorKey: "",
223
+ discriminatorValue: "",
224
+ versionNumber: context.versionNumber,
225
+ usedFeatures: context.usedFeatures
226
+ });
227
+ if (prop.content)
228
+ subContentParts.push(prop.content);
229
+ const fieldName = validVarName(codegenUtils.camelCase(key));
230
+ fieldParts.push(`${fieldName}: ${prop.typeName},`);
231
+ newParts.push(`${fieldName}: ${prop.defaultValue},`);
232
+ const tempKey = `_${validVarName(key)}`;
233
+ fromJsonParts.push(`let ${tempKey}: ${prop.typeName};`);
234
+ fromJsonParts.push(prop.fromJsonTemplate(`input.${key}`, tempKey));
235
+ if (hasKey) {
236
+ toJsonParts.push(`json += ',"${key}":';`);
237
+ } else {
238
+ toJsonParts.push(`json += '"${key}":';`);
521
239
  }
240
+ toJsonParts.push(
241
+ prop.toJsonTemplate(`input.${fieldName}`, "json", key)
242
+ );
243
+ toQueryParts.push(
244
+ prop.toQueryStringTemplate(`input.${fieldName}`, "queryParts", key)
245
+ );
246
+ const validationPart = prop.validationTemplate(`input.${fieldName}`);
247
+ validationParts.push(validationPart);
248
+ constructionParts.push(`${fieldName}: ${tempKey},`);
249
+ hasKey = true;
522
250
  }
523
- if (def.optionalProperties) {
524
- for (const propKey of Object.keys(def.optionalProperties)) {
525
- const propSchema = def.optionalProperties[propKey];
526
- const type = tsTypeFromJtdSchema(
527
- `${nodePath}.${propKey}`,
528
- propSchema,
529
- options,
530
- {
531
- isOptional: true,
532
- existingTypeNames: additionalOptions.existingTypeNames
533
- }
534
- );
535
- parserParts.push(
536
- `${propKey}: ${type.fromJsonTemplate(`input.${propKey}`)}`
537
- );
538
- fieldParts.push(type.fieldTemplate);
539
- fieldNames.push(propKey);
540
- if (type.content) {
541
- subContentParts.push(type.content);
542
- }
251
+ if (!hasKey) {
252
+ toJsonParts.push(`let _hasKey = false;`);
253
+ }
254
+ for (const key of Object.keys(schema.optionalProperties ?? {})) {
255
+ const subSchema = schema.optionalProperties[key];
256
+ const prop = tsTypeFromSchema(subSchema, {
257
+ clientName: context.clientName,
258
+ typePrefix: context.typePrefix,
259
+ generatedTypes: context.generatedTypes,
260
+ instancePath: `/${typeName}/${key}`,
261
+ schemaPath: `/${typeName}/optionalProperties/${key}`,
262
+ discriminatorParent: "",
263
+ discriminatorKey: "",
264
+ discriminatorValue: "",
265
+ versionNumber: context.versionNumber,
266
+ usedFeatures: context.usedFeatures
267
+ });
268
+ if (prop.content)
269
+ subContentParts.push(prop.content);
270
+ const fieldName = validVarName(codegenUtils.camelCase(key));
271
+ fieldParts.push(`${fieldName}?: ${prop.typeName},`);
272
+ const tempKey = `_${validVarName(key)}`;
273
+ fromJsonParts.push(`let ${tempKey}: ${prop.typeName} | undefined;`);
274
+ fromJsonParts.push(`if (typeof input.${key} !== 'undefined') {
275
+ ${prop.fromJsonTemplate(`input.${key}`, tempKey)}
276
+ }`);
277
+ if (hasKey) {
278
+ toJsonParts.push(`if (typeof input.${key} !== 'undefined') {
279
+ json += \`,"${key}":\`;
280
+ ${prop.toJsonTemplate(`input.${key}`, "json", key)}
281
+ }`);
282
+ } else {
283
+ toJsonParts.push(`if (typeof input.${key} !== 'undefined') {
284
+ if (_hasKey) json += ',';
285
+ json += '"${key}":';
286
+ ${prop.toJsonTemplate(`input.${key}`, "json", key)}
287
+ _hasKey = true;
288
+ }`);
543
289
  }
290
+ toQueryParts.push(`if (typeof input.${fieldName} !== 'undefined') {
291
+ ${prop.toQueryStringTemplate(`input.${fieldName}`, "queryParts", key)}
292
+ }`);
293
+ const validationPart = prop.validationTemplate(`input.${fieldName}`);
294
+ validationParts.push(
295
+ `((${validationPart}) || typeof input.${fieldName} === 'undefined')`
296
+ );
297
+ constructionParts.push(`${fieldName}: ${tempKey},`);
544
298
  }
545
- let content = "";
546
- const modifiedDef = {
547
- ...def,
548
- metadata: {
549
- id: def.metadata?.id ?? typeName,
550
- description: def.metadata?.description
551
- },
552
- nullable: false
299
+ result.content = `${getJsDocComment(schema.metadata)}export interface ${prefixedTypeName} {
300
+ ${fieldParts.map((part) => ` ${part}`).join("\n")}
301
+ }
302
+ ${context.discriminatorParent && context.discriminatorValue ? "" : "export "}const $$${prefixedTypeName}: ArriModelValidator<${prefixedTypeName}> = {
303
+ new(): ${prefixedTypeName} {
304
+ return {
305
+ ${newParts.map((part) => ` ${part}`).join("\n")}
306
+ };
307
+ },
308
+ validate(input): input is ${prefixedTypeName} {
309
+ return (
310
+ ${validationParts.map((part) => ` ${part}`).join("&& \n")}
311
+ )
312
+ },
313
+ fromJson(input): ${prefixedTypeName} {
314
+ ${fromJsonParts.map((part) => ` ${part}`).join("\n")}
315
+ return {
316
+ ${constructionParts.map((part) => ` ${part}`).join("\n")}
317
+ }
318
+ },
319
+ fromJsonString(input): ${prefixedTypeName} {
320
+ return $$${prefixedTypeName}.fromJson(JSON.parse(input));
321
+ },
322
+ toJsonString(input): string {
323
+ let json = "{";
324
+ ${toJsonParts.map((part) => ` ${part}`).join("\n")}
325
+ json += "}";
326
+ return json;
327
+ },
328
+ toUrlQueryString(input): string {
329
+ const queryParts: string[] = [];
330
+ ${toQueryParts.map((part) => ` ${part}`).join("\n")}
331
+ return queryParts.join("&");
332
+ }
333
+ }
334
+
335
+ ${subContentParts.join("\n")}`;
336
+ context.generatedTypes.push(typeName);
337
+ return result;
338
+ }
339
+
340
+ function tsTaggedUnionFromSchema(schema, context) {
341
+ const typeName = getTsTypeName(schema, context);
342
+ const prefixedTypeName = `${context.typePrefix}${typeName}`;
343
+ const defaultValue = schema.nullable ? "null" : `$$${prefixedTypeName}.new()`;
344
+ const result = {
345
+ typeName: schema.nullable ? `${prefixedTypeName} | null` : prefixedTypeName,
346
+ defaultValue,
347
+ validationTemplate(input) {
348
+ const mainPart = `$$${prefixedTypeName}.validate(${input})`;
349
+ if (schema.nullable) {
350
+ return `(${mainPart} || ${input} === null)`;
351
+ }
352
+ return mainPart;
353
+ },
354
+ fromJsonTemplate(input, target) {
355
+ return `if (isObject(${input})) {
356
+ ${target} = $$${prefixedTypeName}.fromJson(${input});
357
+ } else {
358
+ ${target} = ${defaultValue};
359
+ }`;
360
+ },
361
+ toJsonTemplate(input, target, _key) {
362
+ if (schema.nullable) {
363
+ return `if (${input} != null) {
364
+ ${target} += $$${prefixedTypeName}.toJsonString(${input});
365
+ } else {
366
+ ${target} += 'null';
367
+ }`;
368
+ }
369
+ return `${target} += $$${prefixedTypeName}.toJsonString(${input});`;
370
+ },
371
+ toQueryStringTemplate(_, __, ___) {
372
+ return `console.warn("[WARNING] Cannot serialize nested objects to query string. Skipping property at ${context.instancePath}.");`;
373
+ },
374
+ content: ""
553
375
  };
554
- let validatorPart = "";
555
- if (options.typesNeedingParser.includes(typeName)) {
556
- const parsingCode = schema.getSchemaParsingCode("input", modifiedDef);
557
- const serializationCode = schema.getSchemaSerializationCode(
558
- "input",
559
- modifiedDef
560
- );
561
- validatorPart = `export const $$${typeName} = {
562
- parse(input: Record<any, any>): ${typeName} {
563
- ${parsingCode}
564
- },
565
- serialize(input: ${typeName}): string {
566
- ${serializationCode}
567
- }
568
- }`;
376
+ if (context.generatedTypes.includes(typeName))
377
+ return result;
378
+ const subTypes = [];
379
+ const discriminatorKey = schema.discriminator;
380
+ for (const key of Object.keys(schema.mapping)) {
381
+ const subSchema = schema.mapping[key];
382
+ const subType = tsObjectFromSchema(subSchema, {
383
+ clientName: context.clientName,
384
+ typePrefix: context.typePrefix,
385
+ generatedTypes: context.generatedTypes,
386
+ instancePath: context.instancePath,
387
+ schemaPath: `${context.schemaPath}/mapping/${key}`,
388
+ discriminatorParent: typeName,
389
+ discriminatorKey,
390
+ discriminatorValue: key,
391
+ versionNumber: context.versionNumber,
392
+ usedFeatures: context.usedFeatures
393
+ });
394
+ subTypes.push({ value: key, data: subType });
569
395
  }
570
- if (!additionalOptions.existingTypeNames.includes(typeName)) {
571
- content = `${getJsDocComment(def.metadata)}export interface ${typeName} {
572
- ${fieldParts.join(";\n ")};
396
+ result.content = `export type ${prefixedTypeName} = ${subTypes.map((type) => type.data.typeName).join(" |")};
397
+ export const $$${prefixedTypeName}: ArriModelValidator<${prefixedTypeName}> = {
398
+ new(): ${prefixedTypeName} {
399
+ return $$${subTypes[0].data.typeName}.new();
400
+ },
401
+ validate(input): input is ${prefixedTypeName} {
402
+ if (!isObject(input)) {
403
+ return false;
404
+ }
405
+ if (typeof input.${discriminatorKey} !== 'string') {
406
+ return false;
407
+ }
408
+ switch (input.${discriminatorKey}) {
409
+ ${subTypes.map(
410
+ (type) => ` case "${type.value}":
411
+ return $$${type.data.typeName}.validate(input);`
412
+ ).join("\n")}
413
+ default:
414
+ return false;
415
+ }
416
+ },
417
+ fromJson(input): ${prefixedTypeName} {
418
+ switch (input.${discriminatorKey}) {
419
+ ${subTypes.map(
420
+ (type) => ` case "${type.value}":
421
+ return $$${type.data.typeName}.fromJson(input);`
422
+ ).join("\n")}
423
+ default:
424
+ return $$${subTypes[0].data.typeName}.new();
425
+ }
426
+ },
427
+ fromJsonString(input): ${prefixedTypeName} {
428
+ return $$${prefixedTypeName}.fromJson(JSON.parse(input));
429
+ },
430
+ toJsonString(input): string {
431
+ switch (input.${discriminatorKey}) {
432
+ ${subTypes.map(
433
+ (type) => ` case "${type.value}":
434
+ return $$${type.data.typeName}.toJsonString(input);`
435
+ ).join("\n")}
436
+ default:
437
+ throw new Error(\`Unhandled case "\${(input as any).${discriminatorKey}}"\`);
438
+ }
439
+ },
440
+ toUrlQueryString(input): string {
441
+ switch (input.${discriminatorKey}) {
442
+ ${subTypes.map(
443
+ (type) => ` case "${type.value}":
444
+ return $$${type.data.typeName}.toUrlQueryString(input);`
445
+ ).join("\n")}
446
+ default:
447
+ throw new Error('Unhandled case');
448
+ }
573
449
  }
574
- ${validatorPart}
575
- ${subContentParts.join("\n")}`;
576
- additionalOptions.existingTypeNames.push(typeName);
450
+ }
451
+ ${subTypes.map((type) => type.data.content).join("\n")}
452
+ `;
453
+ context.generatedTypes.push(typeName);
454
+ return result;
455
+ }
456
+
457
+ function tsEnumFromSchema(schema, context) {
458
+ if (schema.enum.length === 0)
459
+ throw new Error(
460
+ `Error at ${context.schemaPath}. Enum schemas must have at least one enum value.`
461
+ );
462
+ const enumName = getTsTypeName(schema, context);
463
+ const prefixedEnumName = `${context.typePrefix}${enumName}`;
464
+ const typeName = schema.nullable ? `${prefixedEnumName} | null` : prefixedEnumName;
465
+ const defaultValue = schema.nullable ? "null" : `$$${prefixedEnumName}.new()`;
466
+ const result = {
467
+ typeName,
468
+ defaultValue,
469
+ validationTemplate(input) {
470
+ if (schema.nullable) {
471
+ return `($$${prefixedEnumName}.validate(${input}) || ${input} === null)`;
472
+ }
473
+ return `$$${prefixedEnumName}.validate(${input})`;
474
+ },
475
+ fromJsonTemplate(input, target) {
476
+ return `if (typeof ${input} === 'string') {
477
+ ${target} = $$${prefixedEnumName}.fromSerialValue(${input});
478
+ } else {
479
+ ${target} = ${defaultValue};
480
+ }`;
481
+ },
482
+ toJsonTemplate(input, target) {
483
+ if (schema.nullable) {
484
+ return `if (typeof ${input} === 'string') {
485
+ ${target} += \`"\${${input}}"\`;
486
+ } else {
487
+ ${target} += 'null';
488
+ }`;
489
+ }
490
+ return `${target} += \`"\${${input}}"\``;
491
+ },
492
+ toQueryStringTemplate(input, target, key) {
493
+ return `${target}.push(\`${key}=\${${input}}\`)`;
494
+ },
495
+ content: ""
496
+ };
497
+ if (context.generatedTypes.includes(enumName)) {
498
+ return result;
577
499
  }
500
+ const name = `${context.typePrefix}${enumName}`;
501
+ const valuesName = `$$${name}Values`;
502
+ result.content = `${getJsDocComment(schema.metadata)}export type ${name} = (typeof ${valuesName})[number];
503
+ const ${valuesName} = [${schema.enum.map((val) => `"${val}"`).join(", ")}] as const;
504
+ export const $$${name}: ArriEnumValidator<${name}> = {
505
+ new(): ${name} {
506
+ return ${valuesName}[0];
507
+ },
508
+ validate(input): input is ${enumName} {
509
+ return (
510
+ typeof input === 'string' &&
511
+ ${valuesName}.includes(input as any)
512
+ );
513
+ },
514
+ values: ${valuesName},
515
+ fromSerialValue(input): ${name} {
516
+ if (${valuesName}.includes(input as any)) {
517
+ return input as ${name};
518
+ }
519
+ if (${valuesName}.includes(input.toLowerCase() as any)) {
520
+ return input.toLowerCase() as ${name};
521
+ }
522
+ if (${valuesName}.includes(input.toUpperCase() as any)) {
523
+ return input.toUpperCase() as ${name};
524
+ }
525
+ return "${schema.enum[0]}";
526
+ }
527
+ }
528
+ `;
529
+ context.generatedTypes.push(enumName);
530
+ return result;
531
+ }
532
+
533
+ function tsStringFromSchema(schema, _context) {
534
+ const typeName = schema.nullable ? "string | null" : "string";
535
+ const defaultValue = schema.nullable ? "null" : '""';
578
536
  return {
579
- tsType: typeName,
580
- schema: def,
581
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
582
- key,
583
- additionalOptions.isOptional
584
- )}: ${maybeNullType(typeName, def.nullable ?? false)}`,
585
- fromJsonTemplate(input) {
586
- if (additionalOptions.isOptional) {
587
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : undefined`;
537
+ typeName,
538
+ defaultValue,
539
+ validationTemplate(input) {
540
+ if (schema.nullable) {
541
+ return `(typeof ${input} === 'string' || ${input} === null)`;
588
542
  }
589
- if (def.nullable) {
590
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : null`;
543
+ return `typeof ${input} === 'string'`;
544
+ },
545
+ fromJsonTemplate(input, target) {
546
+ return `if (typeof ${input} === 'string') {
547
+ ${target} = ${input};
548
+ } else {
549
+ ${target} = ${defaultValue};
550
+ }`;
551
+ },
552
+ toJsonTemplate(input, target) {
553
+ if (schema.nullable) {
554
+ return `if (typeof ${input} === 'string') {
555
+ ${target} += serializeString(${input});
556
+ } else {
557
+ ${target} += 'null';
558
+ }`;
591
559
  }
592
- return `$$${typeName}.parse(${input})`;
560
+ return `${target} += serializeString(${input});`;
593
561
  },
594
- toJsonTemplate(input) {
595
- return `JSON.stringify(${input})`;
562
+ toQueryStringTemplate(input, target, key) {
563
+ return `${target}.push(\`${key}=\${${input}}\`);`;
596
564
  },
597
- content
565
+ content: ""
598
566
  };
599
567
  }
600
- function tsEnumFromJtdSchema(nodePath, def, options, additionalOptions) {
601
- const keyName = nodePath.split(".").pop() ?? "";
602
- const typeName = getTypeName(nodePath, def);
603
- let content = `${getJsDocComment(def.metadata)}export type ${typeName} = ${def.enum.map((val) => `"${val}"`).join(" | ")}`;
604
- if (additionalOptions.existingTypeNames.includes(typeName)) {
605
- content = "";
606
- } else {
607
- additionalOptions.existingTypeNames.push(typeName);
608
- }
568
+ function tsBooleanFromSchema(schema, _context) {
569
+ const typeName = schema.nullable ? `boolean | null` : "boolean";
570
+ const defaultValue = schema.nullable ? `null` : "false";
609
571
  return {
610
- tsType: typeName,
611
- schema: def,
612
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
613
- keyName,
614
- additionalOptions.isOptional
615
- )}: ${maybeNullType(typeName, def.nullable ?? false)}`,
616
- fromJsonTemplate(input) {
617
- if (additionalOptions.isOptional) {
618
- return `typeof ${input} === 'string' ? $$${typeName}.parse(${input}) : undefined`;
619
- }
620
- if (def.nullable) {
621
- return `typeof ${input} === 'string' ? $$${typeName}.parse(${input}) : null`;
572
+ typeName,
573
+ defaultValue,
574
+ validationTemplate(input) {
575
+ if (schema.nullable) {
576
+ return `(typeof ${input} === 'boolean' || ${input} === null)`;
622
577
  }
623
- return `$$${typeName}.parse(${input})`;
578
+ return `typeof ${input} === 'boolean'`;
624
579
  },
625
- toJsonTemplate(input) {
626
- return `JSON.stringify(${input})`;
580
+ fromJsonTemplate(input, target) {
581
+ return `if (typeof ${input} === 'boolean') {
582
+ ${target} = ${input};
583
+ } else {
584
+ ${target} = ${defaultValue};
585
+ }`;
627
586
  },
628
- content
587
+ toJsonTemplate(input, target) {
588
+ return `${target} += \`\${${input}}\`;`;
589
+ },
590
+ toQueryStringTemplate(input, target, key) {
591
+ return `${target}.push(\`${key}=\${${input}}\`);`;
592
+ },
593
+ content: ""
629
594
  };
630
595
  }
631
- function tsArrayFromJtdSchema(nodePath, def, options, additionalOptions) {
632
- const key = nodePath.split(".").pop() ?? "";
633
- const subType = tsTypeFromJtdSchema(
634
- `${nodePath}.item`,
635
- def.elements,
636
- options,
637
- {
638
- isOptional: false,
639
- existingTypeNames: additionalOptions.existingTypeNames
640
- }
641
- );
642
- const tsType = `Array<${maybeNullType(
643
- subType.tsType,
644
- def.elements.nullable
645
- )}>`;
596
+ function tsDateFromSchema(schema, _context) {
597
+ const typeName = schema.nullable ? `Date | null` : "Date";
598
+ const defaultValue = schema.nullable ? `null` : "new Date()";
646
599
  return {
647
- tsType,
648
- schema: def,
649
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
650
- key,
651
- additionalOptions.isOptional ?? false
652
- )}: ${maybeNullType(tsType, def.nullable ?? false)}`,
653
- fromJsonTemplate(input) {
654
- if (additionalOptions.isOptional) {
655
- return `Array.isArray(${input}) ? ${input}.map((item) => ${subType.fromJsonTemplate(
656
- "item"
657
- )}) : undefined`;
600
+ typeName,
601
+ defaultValue,
602
+ validationTemplate(input) {
603
+ if (schema.nullable) {
604
+ return `(${input} instanceof Date || ${input} === null)`;
658
605
  }
659
- if (def.nullable) {
660
- return `Array.isArray(${input}) ? ${input}.map((item) => ${subType.fromJsonTemplate(
661
- "item"
662
- )}) : null`;
606
+ return `${input} instanceof Date`;
607
+ },
608
+ fromJsonTemplate(input, target) {
609
+ return `if (typeof ${input} === 'string') {
610
+ ${target} = new Date(${input});
611
+ } else if (${input} instanceof Date) {
612
+ ${target} = ${input};
613
+ } else {
614
+ ${target} = ${defaultValue}
615
+ }`;
616
+ },
617
+ toJsonTemplate(input, target) {
618
+ if (schema.nullable) {
619
+ return `if (${input} instanceof Date) {
620
+ ${target} += \`"\${${input}.toISOString()}"\`
621
+ } else {
622
+ ${target} += 'null';
623
+ }`;
663
624
  }
664
- return `Array.isArray(${input}) ? ${input}.map((item) => ${subType.fromJsonTemplate(
665
- "item"
666
- )}) : []`;
625
+ return `${target} += \`"\${${input}.toISOString()}"\``;
667
626
  },
668
- toJsonTemplate(input) {
669
- return `JSON.stringify(${input})`;
627
+ toQueryStringTemplate(input, target, key) {
628
+ if (schema.nullable) {
629
+ return `${target}.push(\`${key}=\${${input}?.toISOString()}\`)`;
630
+ }
631
+ return `${target}.push(\`${key}=\${${input}.toISOString()}\`)`;
670
632
  },
671
- content: subType.content
633
+ content: ""
672
634
  };
673
635
  }
674
- function tsDiscriminatedUnionFromJtdSchema(nodePath, def, options, additionalOptions) {
675
- const key = nodePath.split(".").pop() ?? "";
676
- const typeName = getTypeName(nodePath, def);
677
- const subTypeNames = [];
678
- const subContentParts = [];
679
- const parserParts = [];
680
- for (const val of Object.keys(def.mapping)) {
681
- const optionSchema = def.mapping[val];
682
- if (!codegenUtils.isSchemaFormProperties(optionSchema)) {
683
- continue;
684
- }
685
- const optionType = tsObjectFromJtdSchema(
686
- `${nodePath}.${codegenUtils.camelCase(val.toLowerCase())}`,
687
- optionSchema,
688
- options,
689
- {
690
- discriminatorKey: def.discriminator,
691
- discriminatorKeyValue: val,
692
- isOptional: false,
693
- existingTypeNames: additionalOptions.existingTypeNames
636
+ function tsFloatFromSchema(schema, _context) {
637
+ const typeName = schema.nullable ? "number | null" : "number";
638
+ const defaultValue = schema.nullable ? "null" : "0";
639
+ return {
640
+ typeName,
641
+ defaultValue,
642
+ validationTemplate(input) {
643
+ if (schema.nullable) {
644
+ return `(typeof ${input} === 'number' || ${input} === null)`;
694
645
  }
695
- );
696
- parserParts.push(`case '${val}':
697
- return $$${optionType.tsType}.parse(input);`);
698
- subTypeNames.push(optionType.tsType);
699
- subContentParts.push(optionType.content);
700
- }
701
- let validatorPart = "";
702
- if (options.typesNeedingParser.includes(typeName)) {
703
- const modifiedDef = { ...def, nullable: false };
704
- const parsingCode = schema.getSchemaParsingCode(
705
- "input",
706
- modifiedDef
707
- );
708
- const serializationCode = schema.getSchemaSerializationCode(
709
- "input",
710
- modifiedDef
711
- );
712
- validatorPart = `export const $$${typeName} = {
713
- parse(input: Record<any, any>): ${typeName} {
714
- ${parsingCode}
646
+ return `typeof ${input} === 'number'`;
715
647
  },
716
- serialize(input: ${typeName}): string {
717
- ${serializationCode}
718
- }
719
- }`;
720
- }
721
- let content = `${getJsDocComment(def.metadata)}export type ${typeName} = ${subTypeNames.join(" | ")};
722
- ${validatorPart}
723
- ${subContentParts.join("\n")}`;
724
- if (additionalOptions.existingTypeNames.includes(typeName)) {
725
- content = "";
726
- } else {
727
- additionalOptions.existingTypeNames.push(typeName);
648
+ fromJsonTemplate(input, target) {
649
+ return `if (typeof ${input} === 'number') {
650
+ ${target} = ${input};
651
+ } else {
652
+ ${target} = ${defaultValue};
653
+ }`;
654
+ },
655
+ toJsonTemplate(input, target) {
656
+ return `${target} += \`\${${input}}\``;
657
+ },
658
+ toQueryStringTemplate(input, target, key) {
659
+ return `${target}.push(\`${key}=\${${input}}\`)`;
660
+ },
661
+ content: ""
662
+ };
663
+ }
664
+ function tsIntFromSchema(schema, intType, _context) {
665
+ const typeName = schema.nullable ? "number | null" : "number";
666
+ const defaultValue = schema.nullable ? "null" : "0";
667
+ let min;
668
+ let max;
669
+ switch (intType) {
670
+ case "int8":
671
+ min = "INT8_MIN";
672
+ max = "INT8_MAX";
673
+ break;
674
+ case "uint8":
675
+ min = "0";
676
+ max = "UINT8_MAX";
677
+ break;
678
+ case "int16":
679
+ min = "INT16_MIN";
680
+ max = "INT16_MAX";
681
+ break;
682
+ case "uint16":
683
+ min = "0";
684
+ max = "UINT16_MAX";
685
+ break;
686
+ case "int32":
687
+ min = "INT32_MIN";
688
+ max = "INT32_MAX";
689
+ break;
690
+ case "uint32":
691
+ min = "0";
692
+ max = "UINT32_MAX";
693
+ break;
728
694
  }
729
695
  return {
730
- tsType: typeName,
731
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
732
- key,
733
- additionalOptions.isOptional
734
- )}: ${maybeNullType(typeName, def.nullable)}`,
735
- schema: def,
736
- fromJsonTemplate(input) {
737
- if (additionalOptions.isOptional) {
738
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : undefined`;
696
+ typeName,
697
+ defaultValue,
698
+ validationTemplate(input) {
699
+ const mainPart = `typeof ${input} === 'number' && Number.isInteger(${input}) && ${input} >= ${min} && ${input} <= ${max}`;
700
+ if (schema.nullable) {
701
+ return `((${mainPart}) || ${input} === null)`;
739
702
  }
740
- if (def.nullable) {
741
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : null`;
742
- }
743
- return `$$${typeName}.parse(${input})`;
703
+ return mainPart;
704
+ },
705
+ fromJsonTemplate(input, target) {
706
+ return `if (
707
+ typeof ${input} === 'number' &&
708
+ Number.isInteger(${input}) &&
709
+ ${input} >= ${min} &&
710
+ ${input} <= ${max}
711
+ ) {
712
+ ${target} = ${input};
713
+ } else {
714
+ ${target} = ${defaultValue};
715
+ }`;
716
+ },
717
+ toJsonTemplate(input, target) {
718
+ return `${target} += \`\${${input}}\``;
744
719
  },
745
- toJsonTemplate(input) {
746
- return `JSON.stringify(${input})`;
720
+ toQueryStringTemplate(input, target, key) {
721
+ return `${target}.push(\`${key}=\${${input}}\`)`;
747
722
  },
748
- content
723
+ content: ""
749
724
  };
750
725
  }
751
- function tsRecordFromJtdSchema(nodePath, def, options, additionalOptions) {
752
- const key = nodePath.split(".").pop() ?? "";
753
- const typeName = getTypeName(nodePath, def);
754
- const subType = tsTypeFromJtdSchema(
755
- `${nodePath}.value`,
756
- def.values,
757
- options,
758
- {
759
- isOptional: false,
760
- existingTypeNames: additionalOptions.existingTypeNames
761
- }
762
- );
763
- let content = "";
764
- if (!additionalOptions.existingTypeNames.includes(typeName)) {
765
- let validatorPart = "";
766
- if (options.typesNeedingParser.includes(typeName)) {
767
- const modifiedDef = { ...def, nullable: false };
768
- const parsingCode = schema.getSchemaParsingCode(
769
- "input",
770
- modifiedDef
771
- );
772
- const serializationCode = schema.getSchemaSerializationCode(
773
- "input",
774
- modifiedDef
775
- );
776
- validatorPart = `export const $$${typeName} = {
777
- parse(input: Record<any, any>): ${typeName} {
778
- ${parsingCode}
779
- },
780
- serialize(input: ${typeName}): string {
781
- ${serializationCode}
782
- }
783
- }`;
784
- }
785
- content = `${getJsDocComment(def.metadata)}export type ${typeName} = Record<string, ${subType.tsType}>;
786
- ${validatorPart}
787
- ${subType.content}`;
788
- }
726
+ function tsBigIntFromSchema(schema, isUnsigned, _context) {
727
+ const typeName = schema.nullable ? `bigint | null` : `bigint`;
728
+ const defaultValue = schema.nullable ? `null` : "BigInt(0)";
789
729
  return {
790
- tsType: typeName,
791
- schema: def,
792
- fieldTemplate: `${getJsDocComment(def.metadata)}${maybeOptionalKey(
793
- key,
794
- additionalOptions.isOptional
795
- )}: ${maybeNullType(typeName, def.nullable)}`,
796
- fromJsonTemplate(input) {
797
- if (additionalOptions.isOptional) {
798
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : undefined`;
730
+ typeName,
731
+ defaultValue,
732
+ validationTemplate(input) {
733
+ const mainPart = isUnsigned ? `typeof ${input} === 'bigint' && ${input} >= BigInt(0) && ${input} <= UINT64_MAX` : `typeof ${input} === 'bigint' && ${input} >= INT64_MIN && ${input} <= INT64_MAX`;
734
+ if (schema.nullable) {
735
+ return `((${mainPart}) || ${input} === null)`;
799
736
  }
800
- if (def.nullable) {
801
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : null`;
737
+ return mainPart;
738
+ },
739
+ fromJsonTemplate(input, target) {
740
+ if (isUnsigned) {
741
+ return `if (typeof ${input} === 'string' && BigInt(${input}) >= BigInt(0)) {
742
+ ${target} = BigInt(${input});
743
+ } else if (typeof ${input} === 'bigint' && ${input} >= BigInt(0)) {
744
+ ${target} = ${input};
745
+ } else {
746
+ ${target} = ${defaultValue};
747
+ }`;
802
748
  }
803
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : {}`;
749
+ return `if (typeof ${input} === 'string') {
750
+ ${target} = BigInt(${input});
751
+ } else if (typeof ${input} === 'bigint') {
752
+ ${target} = ${input};
753
+ } else {
754
+ ${target} = ${defaultValue};
755
+ }`;
804
756
  },
805
- toJsonTemplate(input) {
806
- return `JSON.stringify(${input})`;
757
+ toJsonTemplate(input, target) {
758
+ if (schema.nullable) {
759
+ return `if (typeof ${input} === 'bigint') {
760
+ ${target} += \`"\${${input}}"\`
761
+ } else {
762
+ ${target} += 'null';
763
+ }`;
764
+ }
765
+ return `${target} += \`"\${${input}}"\``;
807
766
  },
808
- content
767
+ toQueryStringTemplate(input, target, key) {
768
+ return `${target}.push(\`${key}=\${${input}}\`)`;
769
+ },
770
+ content: ""
809
771
  };
810
772
  }
811
- function tsRefFromJtdSchema(nodePath, def, options, additionalOptions) {
812
- const key = nodePath.split(".").pop() ?? "";
813
- const typeName = codegenUtils.pascalCase(def.ref, {
814
- normalize: true
773
+
774
+ function tsRecordFromSchema(schema, context) {
775
+ const innerType = tsTypeFromSchema(schema.values, {
776
+ clientName: context.clientName,
777
+ typePrefix: context.typePrefix,
778
+ generatedTypes: context.generatedTypes,
779
+ instancePath: `${context.instancePath}/[value]`,
780
+ schemaPath: `${context.schemaPath}/values`,
781
+ discriminatorParent: "",
782
+ discriminatorKey: "",
783
+ discriminatorValue: "",
784
+ versionNumber: context.versionNumber,
785
+ usedFeatures: context.usedFeatures
815
786
  });
787
+ const typeName = `Record<string, ${innerType.typeName}>`;
788
+ const defaultValue = schema.nullable ? "null" : "{}";
816
789
  return {
817
- tsType: typeName,
818
- schema: def,
819
- fieldTemplate: `${maybeOptionalKey(key, additionalOptions.isOptional)}: ${maybeNullType(typeName, def.nullable)}`,
820
- fromJsonTemplate(input) {
821
- if (additionalOptions.isOptional) {
822
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : undefined`;
790
+ typeName: schema.nullable ? `${typeName} | null` : typeName,
791
+ defaultValue,
792
+ validationTemplate(input) {
793
+ const mainPart = `isObject(${input}) && Object.values(${input}).every(
794
+ (_value) => ${innerType.validationTemplate("_value")},
795
+ )`;
796
+ if (schema.nullable) {
797
+ return `((${mainPart}) || ${input} === null)`;
823
798
  }
824
- if (def.nullable) {
825
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : null`;
799
+ return mainPart;
800
+ },
801
+ fromJsonTemplate(input, target) {
802
+ return `if (isObject(${input})) {
803
+ ${target} = {};
804
+ for (const [_key, _value] of Object.entries(${input})) {
805
+ let ${target}Value: ${innerType.typeName};
806
+ if (typeof _value === 'boolean') {
807
+ ${target}Value = _value;
808
+ } else {
809
+ ${target}Value = false;
810
+ }
811
+ ${target}[_key] = ${target}Value;
812
+ }
813
+ } else {
814
+ ${target} = ${defaultValue};
815
+ }`;
816
+ },
817
+ toJsonTemplate(input, target, key) {
818
+ const countVal = `_${validVarName(key)}PropertyCount`;
819
+ if (schema.nullable) {
820
+ return `if (${input} !== null) {
821
+ ${target} += '{';
822
+ let ${countVal} = 0;
823
+ for (const [_key, _value] of Object.entries(${input})) {
824
+ if (${countVal} !== 0) {
825
+ ${target} += ',';
826
+ }
827
+ ${target} += \`"\${_key}":\`;
828
+ ${innerType.toJsonTemplate("_value", target, "_key")}
829
+ ${countVal}++;
830
+ }
831
+ ${target} += '}';
832
+ } else {
833
+ ${target} += 'null';
834
+ }`;
826
835
  }
827
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.parse(${input}) : {}`;
836
+ return `${target} += '{'
837
+ let ${countVal} = 0;
838
+ for (const [_key, _value] of Object.entries(${input})) {
839
+ if (${countVal} !== 0) {
840
+ ${target} += ',';
841
+ }
842
+ ${target} += \`"\${_key}":\`;
843
+ ${innerType.toJsonTemplate("_value", target, "_key")};
844
+ ${countVal}++;
845
+ }
846
+ ${target} += '}';
847
+ `;
848
+ },
849
+ toQueryStringTemplate(_, __, ___) {
850
+ return `console.warn('[WARNING] Cannot serialize nested objects to query string. Skipping property at ${context.instancePath}.')`;
828
851
  },
829
- toJsonTemplate(input) {
830
- if (additionalOptions.isOptional) {
831
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.serialize(${input}) : 'undefined'`;
852
+ content: innerType.content
853
+ };
854
+ }
855
+
856
+ function tsRefFromSchema(schema, context) {
857
+ const typeName = codegenUtils.pascalCase(validVarName(schema.ref), { normalize: true });
858
+ const prefixedTypeName = `${context.typePrefix}${typeName}`;
859
+ const defaultValue = schema.nullable ? "null" : `${prefixedTypeName}.new()`;
860
+ return {
861
+ typeName: schema.nullable ? `${prefixedTypeName} | null` : prefixedTypeName,
862
+ defaultValue,
863
+ validationTemplate(input) {
864
+ if (schema.nullable) {
865
+ return `($$${prefixedTypeName}.validate(${input}) || ${input} === null)`;
832
866
  }
833
- if (def.nullable) {
834
- return `typeof ${input} === 'object' && ${input} !== null ? $$${typeName}.serialize(${input}) : 'null'`;
867
+ return `$$${prefixedTypeName}.validate(${input})`;
868
+ },
869
+ fromJsonTemplate(input, target) {
870
+ return `if (isObject(${input})) {
871
+ ${target} = $$${prefixedTypeName}.fromJson(${input});
872
+ } else {
873
+ ${target} = ${defaultValue};
874
+ }`;
875
+ },
876
+ toJsonTemplate(input, target, _key) {
877
+ if (schema.nullable) {
878
+ return `if (${input} !== null) {
879
+ ${target} += $$${prefixedTypeName}.toJsonString(${input});
880
+ } else {
881
+ ${target} += 'null';
882
+ }`;
835
883
  }
836
- return `$$${typeName}.serialize(${input})`;
884
+ return `${target} += $$${prefixedTypeName}.toJsonString(${input});`;
885
+ },
886
+ toQueryStringTemplate(_, __, ___) {
887
+ return `console.warn("[WARNING] Nested objects cannot be serialized to query string. Ignoring property at ${context.instancePath}.");`;
837
888
  },
838
889
  content: ""
839
890
  };
840
891
  }
841
892
 
893
+ function tsRpcFromDefinition(def, context) {
894
+ switch (def.transport) {
895
+ case "http":
896
+ return httpRpcFromDefinition(def, context);
897
+ case "ws":
898
+ return wsRpcFromDefinition(def, context);
899
+ default:
900
+ console.warn(
901
+ `[ts-codegen] Warning: unsupported transport "${def.transport}". Ignoring ${context.instancePath}.`
902
+ );
903
+ return "";
904
+ }
905
+ }
906
+ function httpRpcFromDefinition(def, context) {
907
+ const key = getRpcKey(context);
908
+ const params = def.params ? `${context.typePrefix}${codegenUtils.pascalCase(validVarName(def.params))}` : void 0;
909
+ const response = def.response ? `${context.typePrefix}${codegenUtils.pascalCase(validVarName(def.response), { normalize: true })}` : void 0;
910
+ const serializerMethod = def.method === "get" ? "toUrlQueryString" : "toJsonString";
911
+ if (def.isEventStream) {
912
+ context.usedFeatures.sse = true;
913
+ return ` ${key}(${params ? `params: ${params},` : ""} options: SseOptions<${response ?? "undefined"}> = {}): EventSourceController {
914
+ return arriSseRequest<${response ?? "undefined"}, ${params ?? "undefined"}>(
915
+ {
916
+ url: \`\${this._baseUrl}${def.path}\`,
917
+ method: "${def.method.toLowerCase()}",
918
+ headers: this._headers,
919
+ ${params ? "params: params," : ""}
920
+ responseFromJson: ${response ? `$$${response}.fromJson` : "() => {}"},
921
+ responseFromString: ${response ? `$$${response}.fromJsonString` : "() => {}"},
922
+ serializer: ${params ? `$$${params}.${serializerMethod}` : "() => {}"},
923
+ clientVersion: "${context.versionNumber}",
924
+ },
925
+ options,
926
+ )
927
+ }`;
928
+ }
929
+ return ` async ${key}(${params ? `params: ${params}` : ""}): Promise<${response ?? "undefined"}> {
930
+ return arriRequest<${response ?? "undefined"}, ${params ?? "undefined"}>({
931
+ url: \`\${this._baseUrl}${def.path}\`,
932
+ method: "${def.method.toLowerCase()}",
933
+ headers: this._headers,
934
+ ${params ? "params: params," : ""}
935
+ responseFromJson: ${response ? `$$${response}.fromJson` : "() => {}"},
936
+ responseFromString: ${response ? `$$${response}.fromJsonString` : "() => {}"},
937
+ serializer: ${params ? `$$${params}.${serializerMethod}` : "() => {}"},
938
+ clientVersion: "${context.versionNumber}",
939
+ });
940
+ }`;
941
+ }
942
+ function wsRpcFromDefinition(def, context) {
943
+ context.usedFeatures.ws = true;
944
+ const key = getRpcKey(context);
945
+ const params = def.params ? `${context.typePrefix}${codegenUtils.pascalCase(validVarName(def.params))}` : void 0;
946
+ const response = def.response ? `${context.typePrefix}${codegenUtils.pascalCase(validVarName(def.response), { normalize: true })}` : void 0;
947
+ return ` async ${key}(options: WsOptions<${response ?? "undefined"}> = {}): Promise<WsController<${params ?? "undefined"},${response ?? "undefined"}>> {
948
+ return arriWsRequest<${params ?? "undefined"}, ${response ?? "undefined"}>({
949
+ url: \`\${this._baseUrl}${def.path}\`,
950
+ headers: this._headers,
951
+ responseFromJson: ${response ? `$$${response}.fromJson` : "() => {}"},
952
+ responseFromString: ${response ? `$$${response}.fromJsonString` : "() => {}"},
953
+ serializer: ${params ? `$$${params}.toJsonString` : "() => {}"},
954
+ onOpen: options.onOpen,
955
+ onClose: options.onClose,
956
+ onError: options.onError,
957
+ onConnectionError: options.onConnectionError,
958
+ onMessage: options.onMessage,
959
+ clientVersion: "${context.versionNumber}",
960
+ });
961
+ }`;
962
+ }
963
+ function getRpcKey(context) {
964
+ const name = context.instancePath.split(".").pop() ?? "";
965
+ return validVarName(name);
966
+ }
967
+
968
+ function tsServiceFromDefinition(def, context) {
969
+ const key = getServiceKey(context);
970
+ const serviceName = getServiceName(context);
971
+ const rpcParts = [];
972
+ const subServices = [];
973
+ for (const key2 of Object.keys(def)) {
974
+ const subDef = def[key2];
975
+ if (codegenUtils.isServiceDefinition(subDef)) {
976
+ const subService = tsServiceFromDefinition(subDef, {
977
+ clientName: context.clientName,
978
+ typePrefix: context.typePrefix,
979
+ generatedTypes: context.generatedTypes,
980
+ instancePath: `${context.instancePath}.${key2}`,
981
+ schemaPath: `${context.instancePath}.${key2}`,
982
+ discriminatorParent: "",
983
+ discriminatorKey: "",
984
+ discriminatorValue: "",
985
+ versionNumber: context.versionNumber,
986
+ usedFeatures: context.usedFeatures
987
+ });
988
+ if (subService.content) {
989
+ subServices.push(subService);
990
+ }
991
+ continue;
992
+ }
993
+ if (codegenUtils.isRpcDefinition(subDef)) {
994
+ const rpc = tsRpcFromDefinition(subDef, {
995
+ clientName: context.clientName,
996
+ typePrefix: context.typePrefix,
997
+ generatedTypes: context.generatedTypes,
998
+ instancePath: `${context.instancePath}.${key2}`,
999
+ schemaPath: `${context.schemaPath}.${key2}`,
1000
+ discriminatorParent: "",
1001
+ discriminatorKey: "",
1002
+ discriminatorValue: "",
1003
+ versionNumber: context.versionNumber,
1004
+ usedFeatures: context.usedFeatures
1005
+ });
1006
+ if (rpc) {
1007
+ rpcParts.push(rpc);
1008
+ }
1009
+ continue;
1010
+ }
1011
+ console.warn(
1012
+ `Invalid definition found at procedures.${context.schemaPath}`
1013
+ );
1014
+ }
1015
+ if (subServices.length === 0 && rpcParts.length === 0) {
1016
+ return {
1017
+ key,
1018
+ name: serviceName,
1019
+ content: ""
1020
+ };
1021
+ }
1022
+ return {
1023
+ key,
1024
+ name: serviceName,
1025
+ content: `export class ${serviceName} {
1026
+ private readonly _baseUrl: string;
1027
+ private readonly _headers: HeaderMap | (() => HeaderMap | Promise<HeaderMap>);
1028
+ ${subServices.map((service) => ` ${service.key}: ${service.name};`).join("\n")}
1029
+ constructor(
1030
+ options: {
1031
+ baseUrl?: string;
1032
+ headers?: HeaderMap | (() => HeaderMap | Promise<HeaderMap>);
1033
+ } = {},
1034
+ ) {
1035
+ this._baseUrl = options.baseUrl ?? "";
1036
+ this._headers = options.headers ?? {};
1037
+ ${subServices.map((service) => ` this.${service.key} = new ${service.name}(options);`).join("\n")}
1038
+ }
1039
+ ${rpcParts.map((rpc) => ` ${rpc}`).join("\n")}
1040
+ }
1041
+
1042
+ ${subServices.map((service) => service.content).join("\n")}`
1043
+ };
1044
+ }
1045
+ function getServiceKey(context) {
1046
+ const name = context.instancePath.split(".").pop() ?? "";
1047
+ return validVarName(name);
1048
+ }
1049
+ function getServiceName(context) {
1050
+ if (context.instancePath.length === 0) {
1051
+ return context.clientName;
1052
+ }
1053
+ const serviceName = codegenUtils.pascalCase(context.instancePath.split(".").join("_"), {
1054
+ normalize: true
1055
+ });
1056
+ return `${context.clientName}${serviceName}Service`;
1057
+ }
1058
+
1059
+ const typescriptClientGenerator = codegenUtils.defineGeneratorPlugin(
1060
+ (options) => ({
1061
+ generator: async (def) => {
1062
+ if (!options.clientName) {
1063
+ throw new Error("Name is requires");
1064
+ }
1065
+ if (!options.outputFile) {
1066
+ throw new Error("No output file specified");
1067
+ }
1068
+ if (Object.keys(def.procedures).length <= 0) {
1069
+ console.warn(
1070
+ `No procedures defined in AppDefinition. Only data models will be outputted.`
1071
+ );
1072
+ }
1073
+ const result = await createTypescriptClient(def, options);
1074
+ fs.writeFileSync(options.outputFile, result);
1075
+ },
1076
+ options
1077
+ })
1078
+ );
1079
+ async function createTypescriptClient(def, options) {
1080
+ const types = [];
1081
+ const context = {
1082
+ clientName: options.clientName,
1083
+ typePrefix: options.typePrefix ?? "",
1084
+ generatedTypes: [],
1085
+ instancePath: "",
1086
+ schemaPath: "",
1087
+ discriminatorParent: "",
1088
+ discriminatorKey: "",
1089
+ discriminatorValue: "",
1090
+ versionNumber: def.info?.version ?? "",
1091
+ usedFeatures: {
1092
+ sse: false,
1093
+ ws: false
1094
+ }
1095
+ };
1096
+ const serviceDefinitions = codegenUtils.unflattenProcedures(def.procedures);
1097
+ const mainService = tsServiceFromDefinition(serviceDefinitions, context);
1098
+ for (const key of Object.keys(def.definitions)) {
1099
+ const typeDef = def.definitions[key];
1100
+ const result2 = tsTypeFromSchema(typeDef, {
1101
+ clientName: context.clientName,
1102
+ typePrefix: context.typePrefix,
1103
+ generatedTypes: context.generatedTypes,
1104
+ instancePath: `/${key}`,
1105
+ schemaPath: `/${key}`,
1106
+ discriminatorParent: "",
1107
+ discriminatorKey: "",
1108
+ discriminatorValue: "",
1109
+ versionNumber: context.versionNumber,
1110
+ usedFeatures: context.usedFeatures
1111
+ });
1112
+ if (result2.content) {
1113
+ types.push(result2.content);
1114
+ }
1115
+ }
1116
+ const imports = `// This file was autogenerated by @arrirpc/codegen-ts. Do not modify directly.
1117
+ // For more information visit https://github.com/modiimedia/arri
1118
+
1119
+ /* eslint-disable */
1120
+ // @ts-nocheck
1121
+ import {
1122
+ ArriEnumValidator,
1123
+ ArriModelValidator,
1124
+ arriRequest,
1125
+ ${context.usedFeatures.sse ? "arriSseRequest," : ""}
1126
+ ${context.usedFeatures.ws ? "arriWsRequest," : ""}
1127
+ ${context.usedFeatures.sse ? "type EventSourceController," : ""}
1128
+ INT8_MAX,
1129
+ INT8_MIN,
1130
+ INT16_MAX,
1131
+ INT16_MIN,
1132
+ INT32_MAX,
1133
+ INT32_MIN,
1134
+ INT64_MAX,
1135
+ INT64_MIN,
1136
+ isObject,
1137
+ serializeString,
1138
+ ${context.usedFeatures.sse ? "type SseOptions," : ""}
1139
+ UINT8_MAX,
1140
+ UINT16_MAX,
1141
+ UINT32_MAX,
1142
+ UINT64_MAX,
1143
+ ${context.usedFeatures.ws ? "type WsController," : ""}
1144
+ ${context.usedFeatures.ws ? "type WsOptions," : ""}
1145
+ } from "@arrirpc/client";
1146
+
1147
+ type HeaderMap = Record<string, string | undefined>;`;
1148
+ if (!mainService.content) {
1149
+ const result2 = `${imports}
1150
+
1151
+ ${types.join("\n")}`;
1152
+ return await prettier__default.format(result2, {
1153
+ ...options.prettierOptions,
1154
+ parser: "typescript"
1155
+ });
1156
+ }
1157
+ const result = `${imports}
1158
+ ${mainService.content}
1159
+
1160
+ ${types.join("\n")}`;
1161
+ return await prettier__default.format(result, {
1162
+ ...options.prettierOptions,
1163
+ parser: "typescript"
1164
+ });
1165
+ }
1166
+ function tsTypeFromSchema(schema, context) {
1167
+ if (codegenUtils.isSchemaFormType(schema)) {
1168
+ switch (schema.type) {
1169
+ case "string":
1170
+ return tsStringFromSchema(schema);
1171
+ case "boolean":
1172
+ return tsBooleanFromSchema(schema);
1173
+ case "timestamp":
1174
+ return tsDateFromSchema(schema);
1175
+ case "float32":
1176
+ case "float64":
1177
+ return tsFloatFromSchema(schema);
1178
+ case "int8":
1179
+ return tsIntFromSchema(schema, "int8");
1180
+ case "uint8":
1181
+ return tsIntFromSchema(schema, "uint8");
1182
+ case "int16":
1183
+ return tsIntFromSchema(schema, "int16");
1184
+ case "uint16":
1185
+ return tsIntFromSchema(schema, "uint16");
1186
+ case "int32":
1187
+ return tsIntFromSchema(schema, "int32");
1188
+ case "uint32":
1189
+ return tsIntFromSchema(schema, "uint32");
1190
+ case "int64":
1191
+ return tsBigIntFromSchema(schema, false);
1192
+ case "uint64":
1193
+ return tsBigIntFromSchema(schema, true);
1194
+ }
1195
+ }
1196
+ if (codegenUtils.isSchemaFormEnum(schema)) {
1197
+ return tsEnumFromSchema(schema, context);
1198
+ }
1199
+ if (codegenUtils.isSchemaFormProperties(schema)) {
1200
+ return tsObjectFromSchema(schema, context);
1201
+ }
1202
+ if (codegenUtils.isSchemaFormElements(schema)) {
1203
+ return tsArrayFromSchema(schema, context);
1204
+ }
1205
+ if (codegenUtils.isSchemaFormValues(schema)) {
1206
+ return tsRecordFromSchema(schema, context);
1207
+ }
1208
+ if (codegenUtils.isSchemaFormDiscriminator(schema)) {
1209
+ return tsTaggedUnionFromSchema(schema, context);
1210
+ }
1211
+ if (codegenUtils.isSchemaFormRef(schema)) {
1212
+ return tsRefFromSchema(schema, context);
1213
+ }
1214
+ return tsAnyFromSchema(schema, context);
1215
+ }
1216
+
842
1217
  exports.createTypescriptClient = createTypescriptClient;
843
- exports.getJsDocComment = getJsDocComment;
844
- exports.maybeNullType = maybeNullType;
845
- exports.maybeOptionalKey = maybeOptionalKey;
846
- exports.tsAnyFromJtdSchema = tsAnyFromJtdSchema;
847
- exports.tsArrayFromJtdSchema = tsArrayFromJtdSchema;
848
- exports.tsDiscriminatedUnionFromJtdSchema = tsDiscriminatedUnionFromJtdSchema;
849
- exports.tsEnumFromJtdSchema = tsEnumFromJtdSchema;
850
- exports.tsHttpRpcFromDefinition = tsHttpRpcFromDefinition;
851
- exports.tsObjectFromJtdSchema = tsObjectFromJtdSchema;
852
- exports.tsRecordFromJtdSchema = tsRecordFromJtdSchema;
853
- exports.tsRefFromJtdSchema = tsRefFromJtdSchema;
854
- exports.tsRpcFromDefinition = tsRpcFromDefinition;
855
- exports.tsScalarFromJtdSchema = tsScalarFromJtdSchema;
856
- exports.tsServiceFromDefinition = tsServiceFromDefinition;
857
- exports.tsTypeFromJtdSchema = tsTypeFromJtdSchema;
858
- exports.tsWsRpcFromDefinition = tsWsRpcFromDefinition;
1218
+ exports.tsTypeFromSchema = tsTypeFromSchema;
859
1219
  exports.typescriptClientGenerator = typescriptClientGenerator;