@aws/nx-plugin 1.0.0-rc.30 → 1.0.0-rc.32

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.
Files changed (59) hide show
  1. package/LICENSE-THIRD-PARTY +235 -1336
  2. package/README.md +1 -1
  3. package/package.json +1 -2
  4. package/src/mcp-server/tools/create-workspace-command.js +2 -2
  5. package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
  6. package/src/open-api/ts-client/__snapshots__/generator.additional-properties.spec.ts.snap +147 -33
  7. package/src/open-api/ts-client/__snapshots__/generator.arrays.spec.ts.snap +308 -75
  8. package/src/open-api/ts-client/__snapshots__/generator.complex-types.spec.ts.snap +286 -62
  9. package/src/open-api/ts-client/__snapshots__/generator.composite-types.spec.ts.snap +674 -93
  10. package/src/open-api/ts-client/__snapshots__/generator.content-type.spec.ts.snap +211 -36
  11. package/src/open-api/ts-client/__snapshots__/generator.duplicate-types.spec.ts.snap +196 -44
  12. package/src/open-api/ts-client/__snapshots__/generator.edge-cases.spec.ts.snap +5507 -0
  13. package/src/open-api/ts-client/__snapshots__/generator.fast-api.spec.ts.snap +219 -60
  14. package/src/open-api/ts-client/__snapshots__/generator.multipart.spec.ts.snap +280 -0
  15. package/src/open-api/ts-client/__snapshots__/generator.primitive-types.spec.ts.snap +351 -80
  16. package/src/open-api/ts-client/__snapshots__/generator.request.spec.ts.snap +170 -49
  17. package/src/open-api/ts-client/__snapshots__/generator.reserved-keywords.spec.ts.snap +98 -22
  18. package/src/open-api/ts-client/__snapshots__/generator.response.spec.ts.snap +147 -33
  19. package/src/open-api/ts-client/__snapshots__/generator.streaming.spec.ts.snap +392 -88
  20. package/src/open-api/ts-client/__snapshots__/generator.tags.spec.ts.snap +196 -44
  21. package/src/open-api/ts-client/files/client.gen.ts.template +166 -19
  22. package/src/open-api/ts-client/files/types.gen.ts.template +4 -4
  23. package/src/open-api/ts-client/generator.js +1 -1
  24. package/src/open-api/ts-client/generator.js.map +1 -1
  25. package/src/open-api/ts-hooks/files/options-proxy.gen.ts.template +5 -0
  26. package/src/open-api/ts-hooks/generator.spec.tsx +70 -0
  27. package/src/open-api/utils/codegen-data/languages.d.ts +0 -6
  28. package/src/open-api/utils/codegen-data/languages.js +23 -18
  29. package/src/open-api/utils/codegen-data/languages.js.map +1 -1
  30. package/src/open-api/utils/codegen-data/types.d.ts +240 -10
  31. package/src/open-api/utils/codegen-data/types.js +24 -1
  32. package/src/open-api/utils/codegen-data/types.js.map +1 -1
  33. package/src/open-api/utils/codegen-data.d.ts +2 -2
  34. package/src/open-api/utils/codegen-data.js +357 -617
  35. package/src/open-api/utils/codegen-data.js.map +1 -1
  36. package/src/open-api/utils/normalise.js +167 -22
  37. package/src/open-api/utils/normalise.js.map +1 -1
  38. package/src/open-api/utils/parser.d.ts +55 -0
  39. package/src/open-api/utils/parser.js +754 -0
  40. package/src/open-api/utils/parser.js.map +1 -0
  41. package/src/open-api/utils/types.d.ts +18 -0
  42. package/src/open-api/utils/types.js.map +1 -1
  43. package/src/preset/__snapshots__/generator.spec.ts.snap +6 -6
  44. package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +414 -6
  45. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +276 -4
  46. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +414 -6
  47. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +0 -23
  48. package/src/ts/mcp-server/generator.js +10 -22
  49. package/src/ts/mcp-server/generator.js.map +1 -1
  50. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +69 -1
  51. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +69 -1
  52. package/src/utils/mcp.js +1 -1
  53. package/src/utils/mcp.js.map +1 -1
  54. package/src/utils/names.d.ts +6 -0
  55. package/src/utils/names.js +6 -1
  56. package/src/utils/names.js.map +1 -1
  57. package/src/utils/versions.d.ts +5 -5
  58. package/src/utils/versions.js +4 -4
  59. package/src/utils/versions.js.map +1 -1
@@ -16,7 +16,26 @@ const uniqStrings = (strings) => {
16
16
  return true;
17
17
  });
18
18
  };
19
- const returnTypeSet = new Set(allOperations.map(op => op.result.typescriptType));
19
+ const returnTypeSet = new Set(allOperations.flatMap(op => op.result ? [op.result.typescriptType] : []));
20
+ // Names of discriminator "base" models (an object schema whose subtypes allOf-
21
+ // compose it). A subtype composes its base's non-dispatching `$toJsonBase`/
22
+ // `$fromJsonBase` body, so composing the dispatching `toJson`/`fromJson` would
23
+ // recurse infinitely (base -> subtype -> base).
24
+ const discriminatorBaseNames = new Set(models.filter(m => m.discriminator && m.discriminator.isBase).map(m => m.name));
25
+ const modelByName = Object.fromEntries(models.map(m => [m.name, m]));
26
+ // The $IO marshaller call for a composed member: a subtype composes its base's
27
+ // non-dispatching body to avoid recursing back through the base's dispatch.
28
+ const composedMarshaller = (property, method) =>
29
+ property.export === 'reference' && discriminatorBaseNames.has(property.type)
30
+ ? `$IO.${property.typescriptType}.$${method}Base`
31
+ : `$IO.${property.typescriptType}.${method}`;
32
+ // The single wire media type chosen for an operation's request body, if any.
33
+ const bodyMediaTypeOf = (op) => {
34
+ if (!op.parametersBody || !op.parametersBody.mediaTypes) return undefined;
35
+ const mediaTypes = Array.isArray(op.parametersBody.mediaTypes) ? op.parametersBody.mediaTypes : [op.parametersBody.mediaTypes];
36
+ return mediaTypes.find(mt => mt === 'application/json' || mt.endsWith('+json')) || mediaTypes[0];
37
+ };
38
+ const hasMultipartBody = allOperations.some(op => bodyMediaTypeOf(op) === 'multipart/form-data');
20
39
  _%>
21
40
  <%_ if ((models.length + allOperations.filter(p => p.parameters.length > 0).length) > 0) { _%>
22
41
  import type {
@@ -44,14 +63,18 @@ const renderToJsonDateValue = (identifier, format) => {
44
63
  // Renders the appropriate nested function for .map() or $mapValues() for arrays and dictionaries for the given type
45
64
  const renderNestedToJsonValue = (type, argumentType = undefined, depth = 0) => {
46
65
  const itemIdentifier = `item${depth}`;
47
- const argType = `${argumentType ? `: ${argumentType}` : ''}`;
66
+ // Nested items (depth > 0) come from mapping over an `any`, so annotate them
67
+ // to satisfy noImplicitAny.
68
+ const argType = `${argumentType ? `: ${argumentType}` : (depth > 0 ? ': any' : '')}`;
48
69
  if (type.isPrimitive || type.typescriptType === 'unknown' || type.export === 'enum' || type.isEnum) {
49
70
  return `(${itemIdentifier}${argType}) => ${["date", "date-time"].includes(type.format) ? renderToJsonDateValue(itemIdentifier, type.format) : itemIdentifier}`;
50
71
  } else if (type.export === "array") {
51
72
  return `(${itemIdentifier}${argType}) => ${itemIdentifier}.map(${renderNestedToJsonValue(type.link, argumentType, depth + 1)})`;
52
73
  } else if (type.export === "dictionary") {
53
74
  return `(${itemIdentifier}${argType}) => $IO.$mapValues(${itemIdentifier}, ${renderNestedToJsonValue(type.link, argumentType, depth + 1)})`;
54
- } else if (type.type === 'unknown') {
75
+ } else if (type.type === 'unknown' && !["one-of", "any-of", "all-of"].includes(type.export)) {
76
+ // A bare `unknown` element passes through; a composite has type `unknown`
77
+ // but must still be marshalled via its own $IO entry.
55
78
  return `(${itemIdentifier}${argType}) => ${itemIdentifier}`;
56
79
  }
57
80
  return `$IO.${type.typescriptType || type.typescriptName}.toJson`;
@@ -83,14 +106,18 @@ const renderToJsonValue = (property, value, argumentType = undefined) => {
83
106
  // Renders the appropriate nested function for .map() or $mapValues() for arrays and dictionaries for the given type
84
107
  const renderNestedFromJsonValue = (type, argumentType = undefined, depth = 0) => {
85
108
  const itemIdentifier = `item${depth}`;
86
- const argType = `${argumentType ? `: ${argumentType}` : ''}`;
109
+ // Nested items (depth > 0) come from mapping over an `any`, so annotate them
110
+ // to satisfy noImplicitAny.
111
+ const argType = `${argumentType ? `: ${argumentType}` : (depth > 0 ? ': any' : '')}`;
87
112
  if (type.isPrimitive || type.typescriptType === 'unknown' || type.export === 'enum' || type.isEnum) {
88
113
  return `(${itemIdentifier}${argType}) => ${["date", "date-time"].includes(type.format) ? `new Date(${itemIdentifier})` : itemIdentifier}`;
89
114
  } else if (type.export === "array") {
90
115
  return `(${itemIdentifier}${argType}) => ${itemIdentifier}.map(${renderNestedFromJsonValue(type.link, argumentType, depth + 1)})`;
91
116
  } else if (type.export === "dictionary") {
92
117
  return `(${itemIdentifier}${argType}) => $IO.$mapValues(${itemIdentifier}, ${renderNestedFromJsonValue(type.link, argumentType, depth + 1)})`;
93
- } else if (type.type === 'unknown') {
118
+ } else if (type.type === 'unknown' && !["one-of", "any-of", "all-of"].includes(type.export)) {
119
+ // A bare `unknown` element passes through; a composite has type `unknown`
120
+ // but must still be marshalled via its own $IO entry.
94
121
  return `(${itemIdentifier}${argType}) => ${itemIdentifier}`;
95
122
  }
96
123
  return `$IO.${type.typescriptType || type.typescriptName}.fromJson`;
@@ -163,13 +190,44 @@ _%>
163
190
  * Utility for serialisation and deserialisation of API types.
164
191
  */
165
192
  export class $IO {
166
- protected static $mapValues = (data: any, fn: (item: any) => any) =>
193
+ public static $mapValues = (data: any, fn: (item: any) => any) =>
167
194
  Object.fromEntries(Object.entries(data).map(([k, v]) => [k, fn(v)]));
168
195
  <%_ models.filter(m => m.export !== 'enum').forEach((model) => { _%>
169
196
  <%_ const isComposite = model.export === "one-of" || model.export === "any-of" || model.export === "all-of"; _%>
197
+ <%_ /* A discriminator base emits its plain (own-fields) body under a $-prefixed
198
+ name that its subtypes compose, while its public toJson/fromJson dispatch
199
+ to the matching subtype — so composing a subtype never recurses back into
200
+ the dispatch. */ _%>
201
+ <%_ const isBase = model.discriminator && model.discriminator.isBase; _%>
202
+ <%_ const toJsonName = isBase ? '$toJsonBase' : 'toJson'; _%>
203
+ <%_ const fromJsonName = isBase ? '$fromJsonBase' : 'fromJson'; _%>
170
204
 
171
205
  public static <%- model.typescriptName %> = {
206
+ <%_ if (isBase) { _%>
172
207
  toJson: (model: <%- model.typescriptName %>): any => {
208
+ if (model === undefined || model === null) {
209
+ return model;
210
+ }
211
+ switch ((model as any).<%- model.discriminator.typescriptPropertyName %>) {
212
+ <%_ model.discriminator.mapping.filter(m => modelByName[m.modelName]).forEach(({ value, modelName }) => { _%>
213
+ case <%- JSON.stringify(value) %>: return $IO.<%- modelByName[modelName].typescriptName %>.toJson(model as <%- modelByName[modelName].typescriptName %>);
214
+ <%_ }); _%>
215
+ }
216
+ return $IO.<%- model.typescriptName %>.$toJsonBase(model);
217
+ },
218
+ fromJson: (json: any): <%- model.typescriptName %> => {
219
+ if (json === undefined || json === null) {
220
+ return json;
221
+ }
222
+ switch (json?.[<%- JSON.stringify(model.discriminator.propertyName) %>]) {
223
+ <%_ model.discriminator.mapping.filter(m => modelByName[m.modelName]).forEach(({ value, modelName }) => { _%>
224
+ case <%- JSON.stringify(value) %>: return $IO.<%- modelByName[modelName].typescriptName %>.fromJson(json);
225
+ <%_ }); _%>
226
+ }
227
+ return $IO.<%- model.typescriptName %>.$fromJsonBase(json);
228
+ },
229
+ <%_ } _%>
230
+ <%- toJsonName %>: (model: <%- model.typescriptName %>): any => {
173
231
  if (model === undefined || model === null) {
174
232
  return model;
175
233
  }
@@ -202,10 +260,25 @@ export class $IO {
202
260
  return <%- renderToJsonValue(arrayOfNonPrimitives, 'model') %>;
203
261
  }
204
262
  <%_ } _%>
263
+ <%_ /* A discriminated composite marshals directly to the matching branch,
264
+ rather than merging every branch (which would leak fields from the
265
+ non-matching branches). Unknown values fall through to the merge. */ _%>
266
+ <%_ if (model.discriminator) { _%>
267
+ <%_ const discToJsonByName = Object.fromEntries(model.composedModels.map(m => [m.name, m.typescriptName])); _%>
268
+ switch ((model as any).<%- model.discriminator.typescriptPropertyName %>) {
269
+ <%_ model.discriminator.mapping.filter(m => discToJsonByName[m.modelName]).forEach(({ value, modelName }) => { _%>
270
+ case <%- JSON.stringify(value) %>: return $IO.<%- discToJsonByName[modelName] %>.toJson(model as <%- discToJsonByName[modelName] %>);
271
+ <%_ }); _%>
272
+ }
273
+ <%_ } _%>
205
274
  <%_ if (model.composedModels.length > 0) { _%>
206
275
  return {
207
276
  <%_ model.properties.filter(p => !p.isPrimitive && p.export !== 'array').forEach((property, i) => { _%>
277
+ <%_ if (property.export === 'reference' && discriminatorBaseNames.has(property.type)) { _%>
278
+ ...<%- composedMarshaller(property, 'toJson') %>(model),
279
+ <%_ } else { _%>
208
280
  ...<%- renderToJsonValue(property, `model${(model.export !== 'all-of' && property.export !== 'dictionary') ? ` as ${property.typescriptType}` : ''}`) %>,
281
+ <%_ } _%>
209
282
  <%_ }); _%>
210
283
  };
211
284
  <%_ } else { _%>
@@ -238,7 +311,7 @@ export class $IO {
238
311
  };
239
312
  <%_ } _%>
240
313
  },
241
- fromJson: (json: any): <%- model.typescriptName %> => {
314
+ <%- fromJsonName %>: (json: any): <%- model.typescriptName %> => {
242
315
  if (json === undefined || json === null) {
243
316
  return json;
244
317
  }
@@ -270,10 +343,25 @@ export class $IO {
270
343
  return <%- renderFromJsonValue(arrayOfNonPrimitives, 'json') %>;
271
344
  }
272
345
  <%_ } _%>
346
+ <%_ /* A discriminated composite marshals directly to the matching branch;
347
+ the discriminator value comes off the raw json by its wire name.
348
+ Unknown values fall through to the merge. */ _%>
349
+ <%_ if (model.discriminator) { _%>
350
+ <%_ const discFromJsonByName = Object.fromEntries(model.composedModels.map(m => [m.name, m.typescriptName])); _%>
351
+ switch (json?.[<%- JSON.stringify(model.discriminator.propertyName) %>]) {
352
+ <%_ model.discriminator.mapping.filter(m => discFromJsonByName[m.modelName]).forEach(({ value, modelName }) => { _%>
353
+ case <%- JSON.stringify(value) %>: return $IO.<%- discFromJsonByName[modelName] %>.fromJson(json);
354
+ <%_ }); _%>
355
+ }
356
+ <%_ } _%>
273
357
  <%_ if (model.composedModels.length > 0) { _%>
274
358
  return {
275
359
  <%_ model.properties.filter(p => !p.isPrimitive && p.export !== 'array').forEach((composedType) => { _%>
360
+ <%_ if (composedType.export === 'reference' && discriminatorBaseNames.has(composedType.type)) { _%>
361
+ ...<%- composedMarshaller(composedType, 'fromJson') %>(json),
362
+ <%_ } else { _%>
276
363
  ...<%- renderFromJsonValue(composedType, 'json') %>,
364
+ <%_ } _%>
277
365
  <%_ }); _%>
278
366
  };
279
367
  <%_ } else { _%>
@@ -355,29 +443,70 @@ export class <%- className %> {
355
443
  <%_ }); _%>
356
444
  }
357
445
 
358
- private $url = (path: string, pathParameters: { [key: string]: any }, queryParameters: { [key: string]: any }, collectionFormats?: { [key: string]: 'multi' | 'csv' }): string => {
446
+ private $collectionDelimiters: { [format: string]: string } = { csv: ',', ssv: ' ', pipes: '|' };
447
+
448
+ private $deepObject = (key: string, value: any): string[] => {
449
+ if (value === undefined || value === null) {
450
+ return [];
451
+ }
452
+ if (typeof value !== 'object') {
453
+ return [`${key}=${encodeURIComponent(String(value))}`];
454
+ }
455
+ return Object.entries(value).flatMap(([prop, v]) =>
456
+ this.$deepObject(`${key}[${encodeURIComponent(prop)}]`, v),
457
+ );
458
+ };
459
+
460
+ private $url = (path: string, pathParameters: { [key: string]: any }, queryParameters: { [key: string]: any }, collectionFormats?: { [key: string]: 'multi' | 'csv' | 'ssv' | 'pipes' | 'deepObject' }): string => {
359
461
  const baseUrl = this.$config.url.endsWith('/') ? this.$config.url.slice(0, -1) : this.$config.url;
360
462
  const pathWithParameters = Object.entries(pathParameters).reduce((withParams, [key, value]) =>
361
463
  withParams.replace(`{${key}}`, encodeURIComponent(`${value}`))
362
464
  , path);
363
- const queryString = Object.entries(queryParameters).map(([key, value]) => {
465
+ const queryString = Object.entries(queryParameters).flatMap(([key, value]) => {
364
466
  if (Array.isArray(value) && collectionFormats?.[key] === 'multi') {
365
- return value.map(v => `${encodeURIComponent(key)}=${encodeURIComponent(`${v}`)}`).join('&');
467
+ return value.map(v => `${encodeURIComponent(key)}=${encodeURIComponent(`${v}`)}`);
366
468
  }
367
- return `${encodeURIComponent(key)}=${encodeURIComponent(Array.isArray(value) ? value.map(String).join(',') : String(value))}`;
469
+ if (collectionFormats?.[key] === 'deepObject' && value !== null && typeof value === 'object') {
470
+ return this.$deepObject(encodeURIComponent(key), value);
471
+ }
472
+ const delimiter = this.$collectionDelimiters[collectionFormats?.[key] ?? 'csv'] ?? ',';
473
+ return [`${encodeURIComponent(key)}=${encodeURIComponent(Array.isArray(value) ? value.map(String).join(delimiter) : String(value))}`];
368
474
  }).join('&');
369
475
  return baseUrl + pathWithParameters + (queryString ? `?${queryString}` : '');
370
476
  };
371
477
 
372
- private $headers = (headerParameters: { [key: string]: any }, collectionFormats?: { [key: string]: 'multi' | 'csv' }): [string, string][] => {
478
+ private $headers = (headerParameters: { [key: string]: any }, collectionFormats?: { [key: string]: 'multi' | 'csv' | 'ssv' | 'pipes' }): [string, string][] => {
373
479
  return Object.entries(headerParameters).flatMap(([key, value]) => {
374
480
  if (Array.isArray(value) && collectionFormats?.[key] === 'multi') {
375
481
  return value.map(v => [key, String(v)]) as [string, string][];
376
482
  }
377
- return [[key, String(value)]];
483
+ const delimiter = this.$collectionDelimiters[collectionFormats?.[key] ?? 'csv'] ?? ',';
484
+ return [[key, Array.isArray(value) ? value.map(String).join(delimiter) : String(value)]];
378
485
  });
379
486
  };
380
487
 
488
+ <%_ if (hasMultipartBody) { _%>
489
+ private $formData = (model: { [key: string]: any }): FormData => {
490
+ const formData = new FormData();
491
+ const append = (key: string, value: any): void => {
492
+ if (value === undefined || value === null) {
493
+ return;
494
+ }
495
+ if (value instanceof Blob || typeof value === 'string') {
496
+ formData.append(key, value);
497
+ } else if (Array.isArray(value)) {
498
+ value.forEach((v) => append(key, v));
499
+ } else if (typeof value === 'object') {
500
+ formData.append(key, JSON.stringify(value));
501
+ } else {
502
+ formData.append(key, String(value));
503
+ }
504
+ };
505
+ Object.entries(model).forEach(([key, value]) => append(key, value));
506
+ return formData;
507
+ };
508
+
509
+ <%_ } _%>
381
510
  private $fetch: typeof fetch = (...args) => (this.$config.fetch ?? fetch)(...args);
382
511
  <%_ allOperations.forEach((op) => { _%>
383
512
  <%_ const hasTag = op.tags && op.tags.length > 0; _%>
@@ -418,15 +547,33 @@ export class <%- className %> {
418
547
  }
419
548
  <%_ } _%>
420
549
  <%_ } _%>
421
- <%_ if (op.parameters.filter(p => p.collectionFormat).length > 0) { _%>
422
- const collectionFormats = {
423
- <%_ op.parameters.filter(p => p.collectionFormat).forEach((parameter) => { _%>
550
+ <%_ /* Query and header collection formats are kept in separate maps: a
551
+ query and a header parameter may share a name, which would collide
552
+ in a single object literal. */ _%>
553
+ <%_ const queryCollectionParams = op.parameters.filter(p => p.collectionFormat && p.in === 'query'); _%>
554
+ <%_ const headerCollectionParams = op.parameters.filter(p => p.collectionFormat && p.in === 'header'); _%>
555
+ <%_ if (queryCollectionParams.length > 0) { _%>
556
+ const queryCollectionFormats = {
557
+ <%_ queryCollectionParams.forEach((parameter) => { _%>
558
+ '<%- parameter.prop %>': '<%- parameter.collectionFormat %>',
559
+ <%_ }); _%>
560
+ } as const;
561
+ <%_ } _%>
562
+ <%_ if (headerCollectionParams.length > 0) { _%>
563
+ const headerCollectionFormats = {
564
+ <%_ headerCollectionParams.forEach((parameter) => { _%>
424
565
  '<%- parameter.prop %>': '<%- parameter.collectionFormat %>',
425
566
  <%_ }); _%>
426
567
  } as const;
427
568
  <%_ } _%>
428
569
  <%_ if (op.parametersBody) { _%>
429
- <%_ if (op.parametersBody.isPrimitive && ['number', 'boolean', 'string'].includes(op.parametersBody.type) && !['array', 'dictionary'].includes(op.parametersBody.export) && !["date", "date-time"].includes(op.parametersBody.format)) { _%>
570
+ <%_ if (bodyMediaTypeOf(op) === 'multipart/form-data') { _%>
571
+ <%_ /* A multipart body is sent as FormData: Blob fields stream as file
572
+ parts, primitives serialise to strings, and fetch computes the
573
+ boundary itself (so the Content-Type header is intentionally unset
574
+ above). */ _%>
575
+ const body = <% if (!op.parametersBody.isRequired) { %>input === undefined ? undefined : <% } %>this.$formData(<%- op.explicitRequestBodyParameter ? `$IO.${op.operationIdPascalCase}RequestBodyParameters.toJson(input).${op.explicitRequestBodyParameter.prop}` : renderToJsonValue(op.parametersBody, 'input') %>);
576
+ <%_ } else if (op.parametersBody.isPrimitive && ['number', 'boolean', 'string'].includes(op.parametersBody.type) && !['array', 'dictionary'].includes(op.parametersBody.export) && !["date", "date-time"].includes(op.parametersBody.format)) { _%>
430
577
  const body = <% if (!op.parametersBody.isRequired) { %>input === undefined ? undefined : <% } %>String(input<%- op.explicitRequestBodyParameter ? `.${op.explicitRequestBodyParameter.typescriptName}` : '' %>);
431
578
  <%_ } else if (op.parametersBody.isPrimitive && ["date", "date-time"].includes(op.parametersBody.format)) { _%>
432
579
  const body = <%- renderToJsonDateValue('input', op.parametersBody.format) %>;
@@ -457,8 +604,8 @@ export class <%- className %> {
457
604
  const body = undefined;
458
605
  <%_ } _%>
459
606
 
460
- const response = await this.$fetch(this.$url('<%- op.path %>', pathParameters, queryParameters<% if (op.parameters.filter(p => p.collectionFormat).length > 0) { %>, collectionFormats<% } %>), {
461
- headers: this.$headers(headerParameters<% if (op.parameters.filter(p => p.collectionFormat).length > 0) { %>, collectionFormats<% } %>),
607
+ const response = await this.$fetch(this.$url('<%- op.path %>', pathParameters, queryParameters<% if (queryCollectionParams.length > 0) { %>, queryCollectionFormats<% } %>), {
608
+ headers: this.$headers(headerParameters<% if (headerCollectionParams.length > 0) { %>, headerCollectionFormats<% } %>),
462
609
  method: '<%- op.method %>',
463
610
  body,
464
611
  });
@@ -22,7 +22,8 @@ _%>
22
22
  <%_ if (model.export === "enum") { _%>
23
23
  <%- docString(model, '') %>export type <%= model.typescriptName %> =
24
24
  <%_ model.enum.forEach((enumMember, i) => { _%>
25
- | '<%- enumMember.value %>'
25
+ <%_ /* JSON.stringify escapes quotes, newlines and backslashes in values */ _%>
26
+ | <%- JSON.stringify(`${enumMember.value}`) %>
26
27
  <%_ }); _%>
27
28
  <%_ } else if (isComposite) { _%>
28
29
  <%- docString(model, '') %>export type <%- model.typescriptName %> =<% model.properties.forEach((composedType, i) => { %><% if (i > 0) { %><% if (model.export === "all-of") { %> &<% } else { %> |<% } %><% } %> <%- composedType.typescriptType %><% }); %>;
@@ -30,9 +31,8 @@ _%>
30
31
  <%- docString(model, '') %>export type <%= model.typescriptName %> = <% if (model.isNullable && model.type !== 'null') { %>null | <% } %>{
31
32
  <%_ if (model.export === "dictionary" && model.link) { _%>
32
33
  <%_ /* When the spec says `type: object` with `additionalProperties: true`
33
- (or omits additionalProperties), hey-api sets `model.link` but the
34
- link's typescriptType is empty — fall back to `unknown` to keep the
35
- emitted code valid. */ _%>
34
+ (or omits additionalProperties), the value model has an unknown type —
35
+ fall back to `unknown` to keep the emitted code valid. */ _%>
36
36
  <%- docString(model.link, ' ') %> [key: string]: <%- model.link.typescriptType || 'unknown' %>;
37
37
  <%_ } _%>
38
38
  <%_ model.properties.forEach((property) => { _%>
@@ -17,7 +17,7 @@ import { parseOpenApiSpec } from "../utils/parse.js";
17
17
  * Build a data structure which can be used to generate code from OpenAPI
18
18
  */ export const buildOpenApiCodeGenerationData = async (tree, specPath)=>{
19
19
  const spec = await parseOpenApiSpec(tree, specPath);
20
- return await buildOpenApiCodeGenData(spec);
20
+ return buildOpenApiCodeGenData(spec);
21
21
  };
22
22
  /**
23
23
  * Generate an OpenAPI typescript client in the target directory
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../packages/nx-plugin/src/open-api/ts-client/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { generateFiles, type Tree } from '@nx/devkit';\nimport * as path from 'path';\nimport { formatFilesInSubtree } from '../../utils/format';\nimport { buildOpenApiCodeGenData } from '../utils/codegen-data';\nimport type { CodeGenData } from '../utils/codegen-data/types';\nimport { parseOpenApiSpec } from '../utils/parse';\nimport type { OpenApiTsClientGeneratorSchema } from './schema';\n\n/**\n * Generates typescript client from an openapi spec\n */\nexport const openApiTsClientGenerator = async (\n tree: Tree,\n options: OpenApiTsClientGeneratorSchema,\n) => {\n const data = await buildOpenApiCodeGenerationData(\n tree,\n options.openApiSpecPath,\n );\n\n generateOpenApiTsClient(tree, data, options.outputPath);\n\n await formatFilesInSubtree(tree);\n};\n\n/**\n * Build a data structure which can be used to generate code from OpenAPI\n */\nexport const buildOpenApiCodeGenerationData = async (\n tree: Tree,\n specPath: string,\n) => {\n const spec = await parseOpenApiSpec(tree, specPath);\n return await buildOpenApiCodeGenData(spec);\n};\n\n/**\n * Generate an OpenAPI typescript client in the target directory\n */\nexport const generateOpenApiTsClient = (\n tree: Tree,\n data: CodeGenData,\n outputPath: string,\n) => {\n generateFiles(\n tree,\n path.join(import.meta.dirname, 'files'),\n outputPath,\n data,\n );\n};\n\nexport default openApiTsClientGenerator;\n"],"names":["generateFiles","path","formatFilesInSubtree","buildOpenApiCodeGenData","parseOpenApiSpec","openApiTsClientGenerator","tree","options","data","buildOpenApiCodeGenerationData","openApiSpecPath","generateOpenApiTsClient","outputPath","specPath","spec","join","dirname"],"mappings":"AAAA;;;CAGC,GACD,SAASA,aAAa,QAAmB,aAAa;AACtD,YAAYC,UAAU,OAAO;AAC7B,SAASC,oBAAoB,QAAQ,wBAAqB;AAC1D,SAASC,uBAAuB,QAAQ,2BAAwB;AAEhE,SAASC,gBAAgB,QAAQ,oBAAiB;AAGlD;;CAEC,GACD,OAAO,MAAMC,2BAA2B,OACtCC,MACAC;IAEA,MAAMC,OAAO,MAAMC,+BACjBH,MACAC,QAAQG,eAAe;IAGzBC,wBAAwBL,MAAME,MAAMD,QAAQK,UAAU;IAEtD,MAAMV,qBAAqBI;AAC7B,EAAE;AAEF;;CAEC,GACD,OAAO,MAAMG,iCAAiC,OAC5CH,MACAO;IAEA,MAAMC,OAAO,MAAMV,iBAAiBE,MAAMO;IAC1C,OAAO,MAAMV,wBAAwBW;AACvC,EAAE;AAEF;;CAEC,GACD,OAAO,MAAMH,0BAA0B,CACrCL,MACAE,MACAI;IAEAZ,cACEM,MACAL,KAAKc,IAAI,CAAC,YAAYC,OAAO,EAAE,UAC/BJ,YACAJ;AAEJ,EAAE;AAEF,eAAeH,yBAAyB"}
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/open-api/ts-client/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { generateFiles, type Tree } from '@nx/devkit';\nimport * as path from 'path';\nimport { formatFilesInSubtree } from '../../utils/format';\nimport { buildOpenApiCodeGenData } from '../utils/codegen-data';\nimport type { CodeGenData } from '../utils/codegen-data/types';\nimport { parseOpenApiSpec } from '../utils/parse';\nimport type { OpenApiTsClientGeneratorSchema } from './schema';\n\n/**\n * Generates typescript client from an openapi spec\n */\nexport const openApiTsClientGenerator = async (\n tree: Tree,\n options: OpenApiTsClientGeneratorSchema,\n) => {\n const data = await buildOpenApiCodeGenerationData(\n tree,\n options.openApiSpecPath,\n );\n\n generateOpenApiTsClient(tree, data, options.outputPath);\n\n await formatFilesInSubtree(tree);\n};\n\n/**\n * Build a data structure which can be used to generate code from OpenAPI\n */\nexport const buildOpenApiCodeGenerationData = async (\n tree: Tree,\n specPath: string,\n) => {\n const spec = await parseOpenApiSpec(tree, specPath);\n return buildOpenApiCodeGenData(spec);\n};\n\n/**\n * Generate an OpenAPI typescript client in the target directory\n */\nexport const generateOpenApiTsClient = (\n tree: Tree,\n data: CodeGenData,\n outputPath: string,\n) => {\n generateFiles(\n tree,\n path.join(import.meta.dirname, 'files'),\n outputPath,\n data,\n );\n};\n\nexport default openApiTsClientGenerator;\n"],"names":["generateFiles","path","formatFilesInSubtree","buildOpenApiCodeGenData","parseOpenApiSpec","openApiTsClientGenerator","tree","options","data","buildOpenApiCodeGenerationData","openApiSpecPath","generateOpenApiTsClient","outputPath","specPath","spec","join","dirname"],"mappings":"AAAA;;;CAGC,GACD,SAASA,aAAa,QAAmB,aAAa;AACtD,YAAYC,UAAU,OAAO;AAC7B,SAASC,oBAAoB,QAAQ,wBAAqB;AAC1D,SAASC,uBAAuB,QAAQ,2BAAwB;AAEhE,SAASC,gBAAgB,QAAQ,oBAAiB;AAGlD;;CAEC,GACD,OAAO,MAAMC,2BAA2B,OACtCC,MACAC;IAEA,MAAMC,OAAO,MAAMC,+BACjBH,MACAC,QAAQG,eAAe;IAGzBC,wBAAwBL,MAAME,MAAMD,QAAQK,UAAU;IAEtD,MAAMV,qBAAqBI;AAC7B,EAAE;AAEF;;CAEC,GACD,OAAO,MAAMG,iCAAiC,OAC5CH,MACAO;IAEA,MAAMC,OAAO,MAAMV,iBAAiBE,MAAMO;IAC1C,OAAOV,wBAAwBW;AACjC,EAAE;AAEF;;CAEC,GACD,OAAO,MAAMH,0BAA0B,CACrCL,MACAE,MACAI;IAEAZ,cACEM,MACAL,KAAKc,IAAI,CAAC,YAAYC,OAAO,EAAE,UAC/BJ,YACAJ;AAEJ,EAAE;AAEF,eAAeH,yBAAyB"}
@@ -36,6 +36,11 @@ import type {
36
36
  <%_ uniq(allOperations.flatMap((op) => op.result && modelsByName[op.result.type] ? [modelsByName[op.result.type].typescriptType] : [])).forEach((returnTypeModel) => { _%>
37
37
  <%- returnTypeModel %>,
38
38
  <%_ }); _%>
39
+ <%_ /* Infinite-query cursor types may reference a named model (e.g. an enum),
40
+ which the cursorType uses but is otherwise not imported. */ _%>
41
+ <%_ uniq(allOperations.flatMap((op) => op.isInfiniteQuery && op.infiniteQueryCursorProperty && modelsByName[op.infiniteQueryCursorProperty.type] ? [modelsByName[op.infiniteQueryCursorProperty.type].typescriptType] : [])).forEach((cursorTypeModel) => { _%>
42
+ <%- cursorTypeModel %>,
43
+ <%_ }); _%>
39
44
  } from './types.gen.js';
40
45
  <%_ } _%>
41
46
  import { <%- className %> } from './client.gen.js';
@@ -1226,6 +1226,76 @@ describe('openApiTsHooksGenerator', () => {
1226
1226
  );
1227
1227
  });
1228
1228
 
1229
+ it('should import the cursor model when the cursor parameter is a $ref', async () => {
1230
+ // Regression: when the infinite-query cursor parameter is a $ref to a named
1231
+ // schema (e.g. an enum), the options proxy references that model in the
1232
+ // cursor type but must also import it, or the proxy fails to compile.
1233
+ const spec: Spec = {
1234
+ openapi: '3.0.0',
1235
+ info: { title, version: '1.0.0' },
1236
+ paths: {
1237
+ '/records': {
1238
+ get: {
1239
+ ...{ 'x-cursor': 'token' },
1240
+ operationId: 'listRecords',
1241
+ parameters: [
1242
+ {
1243
+ name: 'token',
1244
+ in: 'query',
1245
+ required: false,
1246
+ schema: { $ref: '#/components/schemas/PageToken' },
1247
+ },
1248
+ ],
1249
+ responses: {
1250
+ '200': {
1251
+ description: 'ok',
1252
+ content: {
1253
+ 'application/json': {
1254
+ schema: {
1255
+ type: 'object',
1256
+ required: ['records'],
1257
+ properties: {
1258
+ records: { type: 'array', items: { type: 'string' } },
1259
+ },
1260
+ },
1261
+ },
1262
+ },
1263
+ },
1264
+ },
1265
+ },
1266
+ },
1267
+ },
1268
+ components: {
1269
+ schemas: {
1270
+ PageToken: { type: 'string', enum: ['a', 'b', 'c'] },
1271
+ },
1272
+ },
1273
+ };
1274
+
1275
+ tree.write('openapi.json', JSON.stringify(spec));
1276
+
1277
+ await openApiTsHooksGenerator(tree, {
1278
+ openApiSpecPath: 'openapi.json',
1279
+ outputPath: 'src/generated',
1280
+ });
1281
+
1282
+ const optionsProxy = tree.read(
1283
+ 'src/generated/options-proxy.gen.ts',
1284
+ 'utf-8',
1285
+ )!;
1286
+ // The cursor model is used in the cursor type and imported from types.gen.
1287
+ expect(optionsProxy).toContain('PageToken | undefined | null');
1288
+ expect(optionsProxy).toMatch(
1289
+ /import type \{[\s\S]*PageToken,[\s\S]*\} from '\.\/types\.gen\.js'/,
1290
+ );
1291
+
1292
+ validateTypeScript([
1293
+ 'src/generated/client.gen.ts',
1294
+ 'src/generated/types.gen.ts',
1295
+ 'src/generated/options-proxy.gen.ts',
1296
+ ]);
1297
+ });
1298
+
1229
1299
  it('should handle infinite query with custom cursor parameter via object vendor extension', async () => {
1230
1300
  const spec: Spec = {
1231
1301
  openapi: '3.0.0',
@@ -3,14 +3,8 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { type Model } from './types';
6
- /**
7
- * Return the typescript type for the given model
8
- */
9
6
  export declare const toTypeScriptType: (property: Model) => string;
10
7
  export declare const toTypeScriptName: (name: string) => string;
11
8
  export declare const toTypeScriptModelName: (name: string) => string;
12
- /**
13
- * Return the python type for a given property
14
- */
15
9
  export declare const toPythonType: (property: Model) => string;
16
10
  export declare const toPythonName: (namedEntity: "model" | "property" | "operation", name: string) => string;
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
- */ import camelCase from "lodash.camelcase";
5
- import { snakeCase, toClassName } from "../../../utils/names.js";
6
- import { flattenModelLink, PRIMITIVE_TYPES } from "./types.js";
4
+ */ import { camelCase, snakeCase, toClassName } from "../../../utils/names.js";
5
+ import { PRIMITIVE_TYPES } from "./types.js";
7
6
  const toTypescriptPrimitive = (property)=>{
8
7
  if (property.type === 'string' && [
9
8
  'date',
@@ -15,18 +14,23 @@ const toTypescriptPrimitive = (property)=>{
15
14
  }
16
15
  return property.type;
17
16
  };
18
- /**
19
- * Return the typescript type for the given model
20
- */ export const toTypeScriptType = (property)=>{
21
- const propertyLink = flattenModelLink(property.link);
17
+ export const toTypeScriptType = (property)=>{
18
+ // A discriminated subtype's discriminator property renders as its literal
19
+ // tag, making the union a true (narrowable) tagged union.
20
+ if (property.discriminatorValue) {
21
+ return property.discriminatorValue;
22
+ }
23
+ const link = property.link;
24
+ // Enum links serialise as their primitive; use the model's own type instead.
25
+ const valueType = ()=>link && link.export !== 'enum' ? toTypeScriptType(link) : property.type;
22
26
  switch(property.export){
23
27
  case 'enum':
24
28
  case 'generic':
25
29
  return toTypescriptPrimitive(property);
26
30
  case 'array':
27
- return `Array<${propertyLink && propertyLink.export !== 'enum' ? toTypeScriptType(propertyLink) : property.type}>`;
31
+ return `Array<${valueType()}>`;
28
32
  case 'dictionary':
29
- return `{ [key: string]: ${propertyLink && propertyLink.export !== 'enum' ? toTypeScriptType(propertyLink) : property.type}; }`;
33
+ return `{ [key: string]: ${valueType()}; }`;
30
34
  case 'one-of':
31
35
  case 'any-of':
32
36
  case 'all-of':
@@ -43,7 +47,9 @@ const toTypescriptPrimitive = (property)=>{
43
47
  }
44
48
  };
45
49
  export const toTypeScriptName = (name)=>{
46
- return camelCase(name);
50
+ // A name of only non-identifier characters (e.g. "_") camelCases to an empty
51
+ // string; fall back to an underscore so the emitted property stays valid.
52
+ return camelCase(name) || (name ?? '').replace(/[^a-zA-Z0-9]/g, '_') || '_';
47
53
  };
48
54
  const TYPESCRIPT_RESERVED_MODEL_NAMES = new Set([
49
55
  'Date',
@@ -102,18 +108,17 @@ const toPythonPrimitive = (property)=>{
102
108
  }
103
109
  return property.type;
104
110
  };
105
- /**
106
- * Return the python type for a given property
107
- */ export const toPythonType = (property)=>{
108
- const propertyLink = flattenModelLink(property.link);
111
+ export const toPythonType = (property)=>{
112
+ const link = property.link;
113
+ const valueType = ()=>link && link.export !== 'enum' ? toPythonType(link) : property.type;
109
114
  switch(property.export){
110
115
  case 'generic':
111
116
  case 'reference':
112
117
  return toPythonPrimitive(property);
113
118
  case 'array':
114
- return `List[${propertyLink && propertyLink.export !== 'enum' ? toPythonType(propertyLink) : property.type}]`;
119
+ return `List[${valueType()}]`;
115
120
  case 'dictionary':
116
- return `Dict[str, ${propertyLink && propertyLink.export !== 'enum' ? toPythonType(propertyLink) : property.type}]`;
121
+ return `Dict[str, ${valueType()}]`;
117
122
  case 'one-of':
118
123
  case 'any-of':
119
124
  case 'all-of':
@@ -178,8 +183,8 @@ const PYTHON_KEYWORDS = new Set([
178
183
  ]);
179
184
  export const toPythonName = (namedEntity, name)=>{
180
185
  const nameSnakeCase = snakeCase(name);
181
- // Check if the name is a reserved word. Reserved words that overlap with TypeScript will already be escaped
182
- // with a leading _ by @hey-api/openapi-ts, so we remove this to test
186
+ // Names overlapping a TypeScript reserved word carry a leading `_`; strip it
187
+ // before testing against the Python keyword set.
183
188
  if (PYTHON_KEYWORDS.has(name.startsWith('_') ? name.slice(1) : name)) {
184
189
  const nameSuffix = `_${nameSnakeCase}`;
185
190
  switch(namedEntity){
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/open-api/utils/codegen-data/languages.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport camelCase from 'lodash.camelcase';\nimport { snakeCase, toClassName } from '../../../utils/names';\nimport { flattenModelLink, type Model, PRIMITIVE_TYPES } from './types';\n\nconst toTypescriptPrimitive = (property: Model): string => {\n if (\n property.type === 'string' &&\n ['date', 'date-time'].includes(property.format ?? '')\n ) {\n return 'Date';\n } else if (property.type === 'binary') {\n return 'Blob';\n }\n return property.type;\n};\n\n/**\n * Return the typescript type for the given model\n */\nexport const toTypeScriptType = (property: Model): string => {\n const propertyLink = flattenModelLink(property.link);\n switch (property.export) {\n case 'enum':\n case 'generic':\n return toTypescriptPrimitive(property);\n case 'array':\n return `Array<${propertyLink && propertyLink.export !== 'enum' ? toTypeScriptType(propertyLink) : property.type}>`;\n case 'dictionary':\n return `{ [key: string]: ${propertyLink && propertyLink.export !== 'enum' ? toTypeScriptType(propertyLink) : property.type}; }`;\n case 'one-of':\n case 'any-of':\n case 'all-of':\n return toTypeScriptModelName(property.name);\n case 'reference':\n default:\n if (property.type === 'unknown') {\n return 'unknown';\n }\n if (PRIMITIVE_TYPES.has(property.type)) {\n return toTypescriptPrimitive(property);\n }\n return toTypeScriptModelName(property.type);\n }\n};\n\nexport const toTypeScriptName = (name: string): string => {\n return camelCase(name);\n};\n\nconst TYPESCRIPT_RESERVED_MODEL_NAMES = new Set([\n 'Date',\n 'Blob',\n 'Object',\n 'String',\n 'Boolean',\n 'Integer',\n 'Long',\n 'Float',\n 'Array',\n 'ReadonlyArray',\n 'File',\n 'Error',\n 'Map',\n 'Set',\n 'Number',\n 'Symbol',\n 'BigInt',\n 'Function',\n 'Promise',\n 'RegExp',\n 'JSON',\n]);\n\nexport const toTypeScriptModelName = (name: string): string => {\n const candidateName = toClassName(name);\n\n // Prepend underscore for any reserved model names\n return TYPESCRIPT_RESERVED_MODEL_NAMES.has(candidateName)\n ? `_${candidateName}`\n : candidateName;\n};\n\nconst toPythonPrimitive = (property: Model): string => {\n if (property.type === 'string' && property.format === 'date') {\n return 'date';\n } else if (property.type === 'string' && property.format === 'date-time') {\n return 'datetime';\n } else if (property.type === 'any') {\n return 'object';\n } else if (property.type === 'binary') {\n return 'bytearray';\n } else if (property.type === 'number') {\n if ((property as any).openapiType === 'integer') {\n return 'int';\n }\n\n switch (property.format) {\n case 'int32':\n case 'int64':\n return 'int';\n case 'float':\n case 'double':\n default:\n return 'float';\n }\n } else if (property.type === 'boolean') {\n return 'bool';\n } else if (property.type === 'string') {\n return 'str';\n }\n return property.type;\n};\n\n/**\n * Return the python type for a given property\n */\nexport const toPythonType = (property: Model): string => {\n const propertyLink = flattenModelLink(property.link);\n switch (property.export) {\n case 'generic':\n case 'reference':\n return toPythonPrimitive(property);\n case 'array':\n return `List[${propertyLink && propertyLink.export !== 'enum' ? toPythonType(propertyLink) : property.type}]`;\n case 'dictionary':\n return `Dict[str, ${propertyLink && propertyLink.export !== 'enum' ? toPythonType(propertyLink) : property.type}]`;\n case 'one-of':\n case 'any-of':\n case 'all-of':\n return property.name;\n default:\n // \"any\" has export = interface\n if (PRIMITIVE_TYPES.has(property.type)) {\n return toPythonPrimitive(property);\n }\n return property.type;\n }\n};\n\n// @see https://github.com/OpenAPITools/openapi-generator/blob/e2a62ace74de361bef6338b7fa37da8577242aef/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java#L106\nconst PYTHON_KEYWORDS = new Set([\n // @property\n 'property',\n // typing keywords\n 'schema',\n 'base64',\n 'json',\n 'date',\n 'float',\n // python reserved words\n 'and',\n 'del',\n 'from',\n 'not',\n 'while',\n 'as',\n 'elif',\n 'global',\n 'or',\n 'with',\n 'assert',\n 'else',\n 'if',\n 'pass',\n 'yield',\n 'break',\n 'except',\n 'import',\n 'print',\n 'class',\n 'exec',\n 'in',\n 'raise',\n 'continue',\n 'finally',\n 'is',\n 'return',\n 'def',\n 'for',\n 'lambda',\n 'try',\n 'self',\n 'nonlocal',\n 'None',\n 'True',\n 'False',\n 'async',\n 'await',\n]);\n\nexport const toPythonName = (\n namedEntity: 'model' | 'property' | 'operation',\n name: string,\n) => {\n const nameSnakeCase = snakeCase(name);\n\n // Check if the name is a reserved word. Reserved words that overlap with TypeScript will already be escaped\n // with a leading _ by @hey-api/openapi-ts, so we remove this to test\n if (PYTHON_KEYWORDS.has(name.startsWith('_') ? name.slice(1) : name)) {\n const nameSuffix = `_${nameSnakeCase}`;\n switch (namedEntity) {\n case 'model':\n return `model${nameSuffix}`;\n case 'operation':\n return `call${nameSuffix}`;\n case 'property':\n return `var${nameSuffix}`;\n default:\n break;\n }\n }\n return nameSnakeCase;\n};\n"],"names":["camelCase","snakeCase","toClassName","flattenModelLink","PRIMITIVE_TYPES","toTypescriptPrimitive","property","type","includes","format","toTypeScriptType","propertyLink","link","export","toTypeScriptModelName","name","has","toTypeScriptName","TYPESCRIPT_RESERVED_MODEL_NAMES","Set","candidateName","toPythonPrimitive","openapiType","toPythonType","PYTHON_KEYWORDS","toPythonName","namedEntity","nameSnakeCase","startsWith","slice","nameSuffix"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/open-api/utils/codegen-data/languages.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { camelCase, snakeCase, toClassName } from '../../../utils/names';\nimport { type Model, PRIMITIVE_TYPES } from './types';\n\nconst toTypescriptPrimitive = (property: Model): string => {\n if (\n property.type === 'string' &&\n ['date', 'date-time'].includes(property.format ?? '')\n ) {\n return 'Date';\n } else if (property.type === 'binary') {\n return 'Blob';\n }\n return property.type;\n};\n\nexport const toTypeScriptType = (property: Model): string => {\n // A discriminated subtype's discriminator property renders as its literal\n // tag, making the union a true (narrowable) tagged union.\n if (property.discriminatorValue) {\n return property.discriminatorValue;\n }\n const link = property.link;\n // Enum links serialise as their primitive; use the model's own type instead.\n const valueType = () =>\n link && link.export !== 'enum' ? toTypeScriptType(link) : property.type;\n switch (property.export) {\n case 'enum':\n case 'generic':\n return toTypescriptPrimitive(property);\n case 'array':\n return `Array<${valueType()}>`;\n case 'dictionary':\n return `{ [key: string]: ${valueType()}; }`;\n case 'one-of':\n case 'any-of':\n case 'all-of':\n return toTypeScriptModelName(property.name);\n case 'reference':\n default:\n if (property.type === 'unknown') {\n return 'unknown';\n }\n if (PRIMITIVE_TYPES.has(property.type)) {\n return toTypescriptPrimitive(property);\n }\n return toTypeScriptModelName(property.type);\n }\n};\n\nexport const toTypeScriptName = (name: string): string => {\n // A name of only non-identifier characters (e.g. \"_\") camelCases to an empty\n // string; fall back to an underscore so the emitted property stays valid.\n return camelCase(name) || (name ?? '').replace(/[^a-zA-Z0-9]/g, '_') || '_';\n};\n\nconst TYPESCRIPT_RESERVED_MODEL_NAMES = new Set([\n 'Date',\n 'Blob',\n 'Object',\n 'String',\n 'Boolean',\n 'Integer',\n 'Long',\n 'Float',\n 'Array',\n 'ReadonlyArray',\n 'File',\n 'Error',\n 'Map',\n 'Set',\n 'Number',\n 'Symbol',\n 'BigInt',\n 'Function',\n 'Promise',\n 'RegExp',\n 'JSON',\n]);\n\nexport const toTypeScriptModelName = (name: string): string => {\n const candidateName = toClassName(name);\n\n // Prepend underscore for any reserved model names\n return TYPESCRIPT_RESERVED_MODEL_NAMES.has(candidateName)\n ? `_${candidateName}`\n : candidateName;\n};\n\nconst toPythonPrimitive = (property: Model): string => {\n if (property.type === 'string' && property.format === 'date') {\n return 'date';\n } else if (property.type === 'string' && property.format === 'date-time') {\n return 'datetime';\n } else if (property.type === 'any') {\n return 'object';\n } else if (property.type === 'binary') {\n return 'bytearray';\n } else if (property.type === 'number') {\n if (property.openapiType === 'integer') {\n return 'int';\n }\n\n switch (property.format) {\n case 'int32':\n case 'int64':\n return 'int';\n case 'float':\n case 'double':\n default:\n return 'float';\n }\n } else if (property.type === 'boolean') {\n return 'bool';\n } else if (property.type === 'string') {\n return 'str';\n }\n return property.type;\n};\n\nexport const toPythonType = (property: Model): string => {\n const link = property.link;\n const valueType = () =>\n link && link.export !== 'enum' ? toPythonType(link) : property.type;\n switch (property.export) {\n case 'generic':\n case 'reference':\n return toPythonPrimitive(property);\n case 'array':\n return `List[${valueType()}]`;\n case 'dictionary':\n return `Dict[str, ${valueType()}]`;\n case 'one-of':\n case 'any-of':\n case 'all-of':\n return property.name;\n default:\n // \"any\" has export = interface\n if (PRIMITIVE_TYPES.has(property.type)) {\n return toPythonPrimitive(property);\n }\n return property.type;\n }\n};\n\n// @see https://github.com/OpenAPITools/openapi-generator/blob/e2a62ace74de361bef6338b7fa37da8577242aef/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java#L106\nconst PYTHON_KEYWORDS = new Set([\n // @property\n 'property',\n // typing keywords\n 'schema',\n 'base64',\n 'json',\n 'date',\n 'float',\n // python reserved words\n 'and',\n 'del',\n 'from',\n 'not',\n 'while',\n 'as',\n 'elif',\n 'global',\n 'or',\n 'with',\n 'assert',\n 'else',\n 'if',\n 'pass',\n 'yield',\n 'break',\n 'except',\n 'import',\n 'print',\n 'class',\n 'exec',\n 'in',\n 'raise',\n 'continue',\n 'finally',\n 'is',\n 'return',\n 'def',\n 'for',\n 'lambda',\n 'try',\n 'self',\n 'nonlocal',\n 'None',\n 'True',\n 'False',\n 'async',\n 'await',\n]);\n\nexport const toPythonName = (\n namedEntity: 'model' | 'property' | 'operation',\n name: string,\n) => {\n const nameSnakeCase = snakeCase(name);\n\n // Names overlapping a TypeScript reserved word carry a leading `_`; strip it\n // before testing against the Python keyword set.\n if (PYTHON_KEYWORDS.has(name.startsWith('_') ? name.slice(1) : name)) {\n const nameSuffix = `_${nameSnakeCase}`;\n switch (namedEntity) {\n case 'model':\n return `model${nameSuffix}`;\n case 'operation':\n return `call${nameSuffix}`;\n case 'property':\n return `var${nameSuffix}`;\n default:\n break;\n }\n }\n return nameSnakeCase;\n};\n"],"names":["camelCase","snakeCase","toClassName","PRIMITIVE_TYPES","toTypescriptPrimitive","property","type","includes","format","toTypeScriptType","discriminatorValue","link","valueType","export","toTypeScriptModelName","name","has","toTypeScriptName","replace","TYPESCRIPT_RESERVED_MODEL_NAMES","Set","candidateName","toPythonPrimitive","openapiType","toPythonType","PYTHON_KEYWORDS","toPythonName","namedEntity","nameSnakeCase","startsWith","slice","nameSuffix"],"mappings":"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"}