@azure-tools/typespec-ts 0.46.1-alpha.20251201.1 → 0.47.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +15 -3
  2. package/LICENSE +21 -0
  3. package/dist/src/framework/hooks/sdkTypes.d.ts +7 -1
  4. package/dist/src/framework/hooks/sdkTypes.d.ts.map +1 -1
  5. package/dist/src/framework/hooks/sdkTypes.js +59 -1
  6. package/dist/src/framework/hooks/sdkTypes.js.map +1 -1
  7. package/dist/src/lib.d.ts +19 -1
  8. package/dist/src/lib.d.ts.map +1 -1
  9. package/dist/src/lib.js +12 -0
  10. package/dist/src/lib.js.map +1 -1
  11. package/dist/src/modular/buildClassicalClient.d.ts.map +1 -1
  12. package/dist/src/modular/buildClassicalClient.js +84 -6
  13. package/dist/src/modular/buildClassicalClient.js.map +1 -1
  14. package/dist/src/modular/emitModels.d.ts.map +1 -1
  15. package/dist/src/modular/emitModels.js +57 -14
  16. package/dist/src/modular/emitModels.js.map +1 -1
  17. package/dist/src/modular/emitSamples.d.ts.map +1 -1
  18. package/dist/src/modular/emitSamples.js +52 -25
  19. package/dist/src/modular/emitSamples.js.map +1 -1
  20. package/dist/src/modular/helpers/operationHelpers.d.ts +5 -4
  21. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  22. package/dist/src/modular/helpers/operationHelpers.js +113 -23
  23. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  24. package/dist/src/modular/helpers/typeHelpers.d.ts +3 -1
  25. package/dist/src/modular/helpers/typeHelpers.d.ts.map +1 -1
  26. package/dist/src/modular/helpers/typeHelpers.js +7 -3
  27. package/dist/src/modular/helpers/typeHelpers.js.map +1 -1
  28. package/dist/src/modular/serialization/buildDeserializerFunction.d.ts +4 -2
  29. package/dist/src/modular/serialization/buildDeserializerFunction.d.ts.map +1 -1
  30. package/dist/src/modular/serialization/buildDeserializerFunction.js +60 -17
  31. package/dist/src/modular/serialization/buildDeserializerFunction.js.map +1 -1
  32. package/dist/src/modular/serialization/buildSerializerFunction.d.ts +4 -2
  33. package/dist/src/modular/serialization/buildSerializerFunction.d.ts.map +1 -1
  34. package/dist/src/modular/serialization/buildSerializerFunction.js +61 -19
  35. package/dist/src/modular/serialization/buildSerializerFunction.js.map +1 -1
  36. package/dist/src/modular/serialization/serializeUtils.d.ts +11 -0
  37. package/dist/src/modular/serialization/serializeUtils.d.ts.map +1 -1
  38. package/dist/src/modular/serialization/serializeUtils.js +15 -0
  39. package/dist/src/modular/serialization/serializeUtils.js.map +1 -1
  40. package/dist/src/modular/static-helpers-metadata.d.ts +5 -0
  41. package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
  42. package/dist/src/modular/static-helpers-metadata.js +5 -0
  43. package/dist/src/modular/static-helpers-metadata.js.map +1 -1
  44. package/dist/src/utils/namespaceUtils.d.ts.map +1 -1
  45. package/dist/src/utils/namespaceUtils.js +19 -4
  46. package/dist/src/utils/namespaceUtils.js.map +1 -1
  47. package/dist/src/utils/operationUtil.d.ts +1 -0
  48. package/dist/src/utils/operationUtil.d.ts.map +1 -1
  49. package/dist/src/utils/operationUtil.js +26 -0
  50. package/dist/src/utils/operationUtil.js.map +1 -1
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +7 -11
  53. package/src/framework/hooks/sdkTypes.ts +102 -2
  54. package/src/lib.ts +12 -0
  55. package/src/modular/buildClassicalClient.ts +116 -6
  56. package/src/modular/emitModels.ts +72 -19
  57. package/src/modular/emitSamples.ts +85 -20
  58. package/src/modular/helpers/operationHelpers.ts +151 -41
  59. package/src/modular/helpers/typeHelpers.ts +19 -3
  60. package/src/modular/serialization/buildDeserializerFunction.ts +87 -35
  61. package/src/modular/serialization/buildSerializerFunction.ts +86 -36
  62. package/src/modular/serialization/serializeUtils.ts +37 -0
  63. package/src/modular/static-helpers-metadata.ts +5 -0
  64. package/src/utils/namespaceUtils.ts +19 -3
  65. package/src/utils/operationUtil.ts +35 -0
  66. package/static/static-helpers/serialization/check-prop-undefined.ts +17 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-tools/typespec-ts",
3
- "version": "0.46.1-alpha.20251201.1",
3
+ "version": "0.47.0",
4
4
  "description": "An experimental TypeSpec emitter for TypeScript RLC",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -18,14 +18,14 @@
18
18
  "license": "MIT",
19
19
  "devDependencies": {
20
20
  "@azure-rest/core-client": "^2.3.1",
21
- "@typespec/http-specs": "0.1.0-alpha.29-dev.2",
22
- "@typespec/spector": "0.1.0-alpha.21-dev.1",
23
- "@typespec/spec-api": "0.1.0-alpha.11-dev.0",
21
+ "@typespec/http-specs": "0.1.0-alpha.29-dev.5",
22
+ "@typespec/spector": "0.1.0-alpha.21-dev.3",
23
+ "@typespec/spec-api": "0.1.0-alpha.11-dev.1",
24
24
  "@typespec/tspd": "0.73.1",
25
- "@azure-tools/azure-http-specs": "0.1.0-alpha.33-dev.0",
25
+ "@azure-tools/azure-http-specs": "0.1.0-alpha.33-dev.4",
26
26
  "@azure-tools/typespec-autorest": "^0.62.0",
27
27
  "@azure-tools/typespec-azure-core": "^0.62.0",
28
- "@azure-tools/typespec-azure-resource-manager": "^0.62.0",
28
+ "@azure-tools/typespec-azure-resource-manager": "^0.62.1",
29
29
  "@azure-tools/typespec-client-generator-core": "^0.62.0",
30
30
  "@azure/abort-controller": "^2.1.2",
31
31
  "@azure/core-auth": "^1.6.0",
@@ -38,7 +38,6 @@
38
38
  "@types/fs-extra": "^9.0.13",
39
39
  "@types/lodash": "^4.17.4",
40
40
  "@types/mocha": "^10.0.6",
41
- "@types/node": "^18.0.0",
42
41
  "@typescript-eslint/eslint-plugin": "^8.28.0",
43
42
  "@typescript-eslint/parser": "^8.28.0",
44
43
  "@typespec/compiler": "^1.6.0",
@@ -51,14 +50,11 @@
51
50
  "chalk": "^4.0.0",
52
51
  "cross-env": "^7.0.3",
53
52
  "eslint-plugin-require-extensions": "0.1.3",
54
- "eslint": "^8.9.0",
55
53
  "mkdirp": "^3.0.1",
56
54
  "mocha": "^10.4.0",
57
55
  "npm-run-all": "~4.1.5",
58
56
  "prettier": "^3.3.3",
59
- "rimraf": "^5.0.0",
60
57
  "ts-node": "~10.9.1",
61
- "typescript": "~5.8.2",
62
58
  "vitest": "~1.6.0",
63
59
  "@vitest/coverage-v8": "~1.6.0",
64
60
  "@vitest/coverage-istanbul": "~1.6.0",
@@ -77,7 +73,7 @@
77
73
  "@typespec/xml": "^0.76.0"
78
74
  },
79
75
  "dependencies": {
80
- "@azure-tools/rlc-common": "0.46.1-alpha.20251201.1",
76
+ "@azure-tools/rlc-common": "^0.47.0",
81
77
  "fs-extra": "^11.1.0",
82
78
  "lodash": "^4.17.21",
83
79
  "prettier": "^3.3.3",
@@ -2,6 +2,8 @@ import { Operation, Type, getNamespaceFullName } from "@typespec/compiler";
2
2
  import {
3
3
  SdkClientType,
4
4
  SdkHttpOperation,
5
+ SdkModelPropertyType,
6
+ SdkModelType,
5
7
  SdkServiceMethod,
6
8
  SdkType,
7
9
  getClientType
@@ -10,12 +12,27 @@ import { provideContext, useContext } from "../../contextManager.js";
10
12
 
11
13
  import { visitPackageTypes } from "../../modular/emitModels.js";
12
14
  import { SdkContext } from "../../utils/interfaces.js";
15
+ import {
16
+ getAllAncestors,
17
+ getAllProperties
18
+ } from "../../modular/helpers/operationHelpers.js";
19
+ import { normalizeModelPropertyName } from "../../modular/type-expressions/get-type-expression.js";
20
+ import { reportDiagnostic } from "../../lib.js";
21
+ import { NameType, normalizeName } from "@azure-tools/rlc-common";
13
22
 
14
23
  export const emitQueue: Set<SdkType> = new Set<SdkType>();
24
+ export const flattenPropertyModelMap: Map<SdkModelPropertyType, SdkModelType> =
25
+ new Map<SdkModelPropertyType, SdkModelType>();
15
26
 
16
27
  export interface SdkTypeContext {
17
28
  operations: Map<Type, SdkServiceMethod<SdkHttpOperation>>;
18
29
  types: Map<Type, SdkType>;
30
+ flattenProperties: Map<SdkModelPropertyType, SdkFlattenPropertyContext>;
31
+ }
32
+
33
+ export interface SdkFlattenPropertyContext {
34
+ baseModel: SdkModelType;
35
+ conflictMap?: Map<SdkModelPropertyType, string>;
19
36
  }
20
37
 
21
38
  export function useSdkTypes() {
@@ -56,11 +73,20 @@ export function useSdkTypes() {
56
73
 
57
74
  export function provideSdkTypes(context: SdkContext) {
58
75
  const { sdkPackage } = context;
59
- const sdkTypesContext = {
76
+ const sdkTypesContext: SdkTypeContext = {
60
77
  operations: new Map<Type, SdkServiceMethod<SdkHttpOperation>>(),
61
- types: new Map<Type, SdkType>()
78
+ types: new Map<Type, SdkType>(),
79
+ flattenProperties: new Map<
80
+ SdkModelPropertyType,
81
+ SdkFlattenPropertyContext
82
+ >()
62
83
  };
63
84
  visitPackageTypes(context);
85
+ enrichFlattenProperties(
86
+ context,
87
+ sdkTypesContext.flattenProperties,
88
+ flattenPropertyModelMap
89
+ );
64
90
  for (const sdkModel of emitQueue) {
65
91
  switch (sdkModel.kind) {
66
92
  case "model":
@@ -126,6 +152,80 @@ export function provideSdkTypes(context: SdkContext) {
126
152
  provideContext("sdkTypes", sdkTypesContext);
127
153
  }
128
154
 
155
+ // Enrich flatten properties with their base models and conflict maps
156
+ function enrichFlattenProperties(
157
+ context: SdkContext,
158
+ propertyContextMap: Map<SdkModelPropertyType, SdkFlattenPropertyContext>,
159
+ propertyModelMap: Map<SdkModelPropertyType, SdkModelType>
160
+ ) {
161
+ // Build a map of base model to its existing properties excluding flatten properties
162
+ // To check for conflicts later
163
+ const baseModelProperties = new Map<SdkModelType, Set<string>>();
164
+ propertyModelMap.forEach((baseModel, _) => {
165
+ if (!baseModelProperties.has(baseModel)) {
166
+ const propertiesExcludedFlatten = getAllProperties(
167
+ context,
168
+ baseModel,
169
+ getAllAncestors(baseModel)
170
+ )
171
+ .filter((p) => p.flatten === false || p.flatten === undefined)
172
+ .map((p) => normalizeModelPropertyName(context, p));
173
+ baseModelProperties.set(
174
+ baseModel,
175
+ new Set<string>(propertiesExcludedFlatten)
176
+ );
177
+ }
178
+ });
179
+ for (const [flattenProperty, baseModel] of propertyModelMap) {
180
+ const flattenContext: SdkFlattenPropertyContext = {
181
+ baseModel: baseModel
182
+ };
183
+ propertyContextMap.set(flattenProperty, flattenContext);
184
+ const existingProperties = baseModelProperties.get(baseModel)!;
185
+ const conflictMap = new Map<SdkModelPropertyType, string>();
186
+ const flattenModel = flattenProperty.type;
187
+
188
+ if (flattenModel.kind !== "model") {
189
+ continue;
190
+ }
191
+ if (baseModelProperties.has(flattenModel)) {
192
+ // If the flatten model is also a base model of other flatten properties, which means it has multiple consecutive flatten operations
193
+ // Since we cannot handle the flatten transition, report warning and skip it for now
194
+ reportDiagnostic(context.program, {
195
+ code: "unsupported-flatten-transition",
196
+ format: {
197
+ propertyName: flattenProperty.name,
198
+ modelName: baseModel.name
199
+ },
200
+ target: flattenProperty.__raw!
201
+ });
202
+ }
203
+ const allFlattenProperties = getAllProperties(
204
+ context,
205
+ flattenModel,
206
+ getAllAncestors(flattenModel)
207
+ );
208
+ for (const flattenChildProperty of allFlattenProperties) {
209
+ let childPropertyName = normalizeModelPropertyName(
210
+ context,
211
+ flattenChildProperty
212
+ );
213
+ if (existingProperties.has(childPropertyName)) {
214
+ childPropertyName = normalizeName(
215
+ `${childPropertyName}_${flattenProperty.name}_${childPropertyName}`,
216
+ NameType.Property
217
+ );
218
+ conflictMap.set(flattenChildProperty, childPropertyName);
219
+ }
220
+ existingProperties.add(childPropertyName);
221
+ }
222
+ if (conflictMap.size === 0) {
223
+ continue;
224
+ }
225
+ flattenContext.conflictMap = conflictMap;
226
+ }
227
+ }
228
+
129
229
  export function getAllOperationsFromClient(
130
230
  client: SdkClientType<SdkHttpOperation>
131
231
  ) {
package/src/lib.ts CHANGED
@@ -549,6 +549,12 @@ const libDef = {
549
549
  default: paramMessage`The parameter name ${"parameterName"} has conflicts with others and please use @clientName to rename it.`
550
550
  }
551
551
  },
552
+ "unsupported-flatten-transition": {
553
+ severity: "warning",
554
+ messages: {
555
+ default: paramMessage`The property "${"propertyName"}" in "${"modelName"}" has multiple consecutive flatten operations. Flatten transitions are not supported so consecutive transitions will be ignored.`
556
+ }
557
+ },
552
558
  "unsupported-parameter-type": {
553
559
  severity: "error",
554
560
  messages: {
@@ -596,6 +602,12 @@ const libDef = {
596
602
  messages: {
597
603
  default: paramMessage`Error traversing directory ${"directory"}: ${"error"}`
598
604
  }
605
+ },
606
+ "detected-model-name-conflict": {
607
+ severity: "warning",
608
+ messages: {
609
+ default: paramMessage`Model name conflict detected: "${"modelName"}" exists in multiple namespaces: ${"namespaces"}. Please use @clientName to rename them.`
610
+ }
599
611
  }
600
612
  },
601
613
  emitter: {
@@ -31,7 +31,10 @@ import {
31
31
  SdkServiceMethod,
32
32
  SdkServiceOperation
33
33
  } from "@azure-tools/typespec-client-generator-core";
34
- import { getMethodHierarchiesMap } from "../utils/operationUtil.js";
34
+ import {
35
+ getMethodHierarchiesMap,
36
+ isTenantLevelOperation
37
+ } from "../utils/operationUtil.js";
35
38
  import { useContext } from "../contextManager.js";
36
39
  import { refkey } from "../framework/refkey.js";
37
40
  import { SimplePollerHelpers } from "./static-helpers-metadata.js";
@@ -119,11 +122,28 @@ export function buildClassicalClient(
119
122
  });
120
123
  }
121
124
 
122
- // TODO: We may need to generate constructor overloads at some point. Here we'd do that.
123
- const constructor = clientClass.addConstructor({
124
- docs: getDocsFromDescription(client.doc),
125
- parameters: classicalParams
126
- });
125
+ // Check if constructor overloads for subscriptionId is needed
126
+ const hasSubscriptionIdParam = classicalParams.some(
127
+ (param) => param.name.toLowerCase() === "subscriptionid"
128
+ );
129
+ const shouldSubscriptionIdOptional =
130
+ dpgContext.arm &&
131
+ hasSubscriptionIdParam &&
132
+ hasTenantLevelOperations(client, dpgContext);
133
+
134
+ let constructor;
135
+ if (shouldSubscriptionIdOptional) {
136
+ constructor = generateConstructorWithOverloads(
137
+ clientClass,
138
+ classicalParams,
139
+ client
140
+ );
141
+ } else {
142
+ constructor = clientClass.addConstructor({
143
+ docs: getDocsFromDescription(client.doc),
144
+ parameters: classicalParams
145
+ });
146
+ }
127
147
 
128
148
  const paramNames = (contextParams ?? [])
129
149
  .map((p) => p.name)
@@ -134,6 +154,11 @@ export function buildClassicalClient(
134
154
  emitterOptions,
135
155
  "azsdk-js-client"
136
156
  )}}`;
157
+ } else if (
158
+ x.toLowerCase() === "subscriptionid" &&
159
+ shouldSubscriptionIdOptional
160
+ ) {
161
+ return `subscriptionId ?? ""`;
137
162
  } else {
138
163
  return x;
139
164
  }
@@ -370,3 +395,88 @@ function addChildClient(
370
395
  )`
371
396
  );
372
397
  }
398
+
399
+ function hasTenantLevelOperations(
400
+ client: SdkClientType<SdkServiceOperation>,
401
+ dpgContext: SdkContext
402
+ ): boolean {
403
+ const methodMap = getMethodHierarchiesMap(dpgContext, client);
404
+
405
+ for (const [_, operations] of methodMap) {
406
+ for (const op of operations) {
407
+ if (isTenantLevelOperation(op, client)) {
408
+ // Found a tenant-level operation
409
+ return true;
410
+ }
411
+ }
412
+ }
413
+
414
+ return false;
415
+ }
416
+
417
+ function generateConstructorWithOverloads(
418
+ clientClass: ClassDeclaration,
419
+ classicalParams: any[],
420
+ client: SdkClientType<SdkServiceOperation>
421
+ ) {
422
+ const filteredParams = classicalParams.filter(
423
+ (p) =>
424
+ p.name.toLowerCase() !== "subscriptionid" &&
425
+ p.name.toLowerCase() !== "options"
426
+ );
427
+
428
+ const clientConstructor = clientClass.addConstructor({
429
+ docs: getDocsFromDescription(client.doc),
430
+ parameters: [
431
+ ...filteredParams,
432
+ {
433
+ name: "subscriptionIdOrOptions",
434
+ type: `string | ${getClassicalClientName(client)}OptionalParams`,
435
+ hasQuestionToken: true
436
+ },
437
+ {
438
+ name: "options",
439
+ type: `${getClassicalClientName(client)}OptionalParams`,
440
+ hasQuestionToken: true
441
+ }
442
+ ]
443
+ });
444
+
445
+ clientConstructor.addOverload({
446
+ parameters: [
447
+ ...filteredParams,
448
+ {
449
+ name: "options",
450
+ type: `${getClassicalClientName(client)}OptionalParams`,
451
+ hasQuestionToken: true
452
+ }
453
+ ]
454
+ });
455
+
456
+ clientConstructor.addOverload({
457
+ parameters: [
458
+ ...filteredParams,
459
+ ...classicalParams.filter(
460
+ (p) => p.name.toLowerCase() === "subscriptionid"
461
+ ),
462
+ {
463
+ name: "options",
464
+ type: `${getClassicalClientName(client)}OptionalParams`,
465
+ hasQuestionToken: true
466
+ }
467
+ ]
468
+ });
469
+
470
+ clientConstructor.addStatements([
471
+ `let subscriptionId: string | undefined;`,
472
+ ``,
473
+ `if (typeof subscriptionIdOrOptions === "string") {`,
474
+ ` subscriptionId = subscriptionIdOrOptions;`,
475
+ `} else if (typeof subscriptionIdOrOptions === "object") {`,
476
+ ` options = subscriptionIdOrOptions;`,
477
+ `}`,
478
+ `options = options ?? {};`
479
+ ]);
480
+
481
+ return clientConstructor;
482
+ }
@@ -40,8 +40,14 @@ import {
40
40
 
41
41
  import { SdkContext } from "../utils/interfaces.js";
42
42
  import { addDeclaration } from "../framework/declaration.js";
43
- import { buildModelDeserializer } from "./serialization/buildDeserializerFunction.js";
44
- import { buildModelSerializer } from "./serialization/buildSerializerFunction.js";
43
+ import {
44
+ buildModelDeserializer,
45
+ buildPropertyDeserializer
46
+ } from "./serialization/buildDeserializerFunction.js";
47
+ import {
48
+ buildModelSerializer,
49
+ buildPropertySerializer
50
+ } from "./serialization/buildSerializerFunction.js";
45
51
  import path from "path";
46
52
  import { refkey } from "../framework/refkey.js";
47
53
  import { useContext } from "../contextManager.js";
@@ -50,6 +56,7 @@ import { isAzureCoreErrorType } from "../utils/modelUtils.js";
50
56
  import { isExtensibleEnum } from "./type-expressions/get-enum-expression.js";
51
57
  import {
52
58
  getAllDiscriminatedValues,
59
+ getPropertyWithOverrides,
53
60
  isDiscriminatedUnion
54
61
  } from "./serialization/serializeUtils.js";
55
62
  import { reportDiagnostic } from "../lib.js";
@@ -60,6 +67,7 @@ import {
60
67
  } from "./type-expressions/get-type-expression.js";
61
68
  import {
62
69
  emitQueue,
70
+ flattenPropertyModelMap,
63
71
  getAllOperationsFromClient
64
72
  } from "../framework/hooks/sdkTypes.js";
65
73
  import { resolveReference } from "../framework/reference.js";
@@ -122,6 +130,14 @@ export function emitTypes(
122
130
  emitType(context, type, sourceFile);
123
131
  }
124
132
 
133
+ // Emit serialization/deserialization functions for flattened properties
134
+ for (const [property, _] of flattenPropertyModelMap) {
135
+ const namespaces = getModelNamespaces(context, property.type);
136
+ const filepath = getModelsPath(sourceRoot, namespaces);
137
+ sourceFile = outputProject.getSourceFile(filepath);
138
+ addSerializationFunctions(context, property, sourceFile!);
139
+ }
140
+
125
141
  const modelFiles = outputProject.getSourceFiles(
126
142
  sourceRoot + "/models/**/*.ts"
127
143
  );
@@ -310,36 +326,38 @@ export function getModelNamespaces(
310
326
 
311
327
  function addSerializationFunctions(
312
328
  context: SdkContext,
313
- type: SdkType,
329
+ typeOrProperty: SdkType | SdkModelPropertyType,
314
330
  sourceFile: SourceFile,
315
- skipDiscriminatedUnion = false
331
+ skipDiscriminatedUnionSuffix = false
316
332
  ) {
317
- const serializationFunction = buildModelSerializer(
318
- context,
319
- type,
320
- skipDiscriminatedUnion
321
- );
333
+ const options = {
334
+ nameOnly: false,
335
+ skipDiscriminatedUnionSuffix
336
+ };
337
+ const serializationFunction =
338
+ typeOrProperty.kind === "property"
339
+ ? buildPropertySerializer(context, typeOrProperty, options)
340
+ : buildModelSerializer(context, typeOrProperty, options);
322
341
 
323
- const serializerRefkey = refkey(type, "serializer");
324
- const deserailizerRefKey = refkey(type, "deserializer");
342
+ const serializerRefKey = refkey(typeOrProperty, "serializer");
343
+ const deserializerRefKey = refkey(typeOrProperty, "deserializer");
325
344
  if (
326
345
  serializationFunction &&
327
346
  typeof serializationFunction !== "string" &&
328
347
  serializationFunction.name
329
348
  ) {
330
- addDeclaration(sourceFile, serializationFunction, serializerRefkey);
349
+ addDeclaration(sourceFile, serializationFunction, serializerRefKey);
331
350
  }
332
- const deserializationFunction = buildModelDeserializer(
333
- context,
334
- type,
335
- skipDiscriminatedUnion
336
- );
351
+ const deserializationFunction =
352
+ typeOrProperty.kind === "property"
353
+ ? buildPropertyDeserializer(context, typeOrProperty, options)
354
+ : buildModelDeserializer(context, typeOrProperty, options);
337
355
  if (
338
356
  deserializationFunction &&
339
357
  typeof deserializationFunction !== "string" &&
340
358
  deserializationFunction.name
341
359
  ) {
342
- addDeclaration(sourceFile, deserializationFunction, deserailizerRefKey);
360
+ addDeclaration(sourceFile, deserializationFunction, deserializerRefKey);
343
361
  }
344
362
  }
345
363
 
@@ -474,16 +492,47 @@ function buildModelInterface(
474
492
  context: SdkContext,
475
493
  type: SdkModelType
476
494
  ): InterfaceDeclarationStructure {
495
+ const flattenPropertySet = new Set<SdkModelPropertyType>();
477
496
  const interfaceStructure = {
478
497
  kind: StructureKind.Interface,
479
498
  name: normalizeModelName(context, type, NameType.Interface, true),
480
499
  isExported: true,
481
500
  properties: type.properties
482
501
  .filter((p) => !isMetadata(context.program, p.__raw!))
502
+ .filter((p) => {
503
+ // filter out the flatten property to be processed later
504
+ if (p.flatten) {
505
+ flattenPropertySet.add(p);
506
+ return false;
507
+ }
508
+ return true;
509
+ })
483
510
  .map((p) => {
484
511
  return buildModelProperty(context, p, type);
485
512
  })
486
513
  } as InterfaceStructure;
514
+ for (const flatten of flattenPropertySet.keys()) {
515
+ if (flatten.type?.kind !== "model" || !flatten.type.properties) {
516
+ continue;
517
+ }
518
+ const conflictMap =
519
+ useContext("sdkTypes").flattenProperties.get(flatten)?.conflictMap;
520
+ const allProperties = getAllProperties(
521
+ context,
522
+ flatten.type,
523
+ getAllAncestors(flatten.type)
524
+ ).filter((p) => !isMetadata(context.program, p.__raw!));
525
+ interfaceStructure.properties!.push(
526
+ ...allProperties.map((p) => {
527
+ // when the flattened property is optional, all its child properties should be optional too
528
+ const property = getPropertyWithOverrides(p, {
529
+ allOptional: flatten.optional,
530
+ propertyRenames: conflictMap
531
+ });
532
+ return buildModelProperty(context, property, type);
533
+ })
534
+ );
535
+ }
487
536
 
488
537
  if (type.baseModel) {
489
538
  const parentReference = getModelExpression(context, type.baseModel, {
@@ -635,7 +684,7 @@ function buildModelPolymorphicType(context: SdkContext, type: SdkModelType) {
635
684
  }
636
685
  const typeDeclaration: TypeAliasDeclarationStructure = {
637
686
  kind: StructureKind.TypeAlias,
638
- name: `${normalizeName(type.name, NameType.Interface)}Union`,
687
+ name: `${normalizeModelName(context, type, NameType.Interface, true)}Union`,
639
688
  isExported: true,
640
689
  type: directSubtypes
641
690
  .filter((p) => {
@@ -747,6 +796,7 @@ function buildModelProperty(
747
796
  export function visitPackageTypes(context: SdkContext) {
748
797
  const { sdkPackage } = context;
749
798
  emitQueue.clear();
799
+ flattenPropertyModelMap.clear();
750
800
  // Add all models in the package to the emit queue
751
801
  for (const model of sdkPackage.models) {
752
802
  visitType(context, model);
@@ -852,6 +902,9 @@ function visitType(context: SdkContext, type: SdkType | undefined) {
852
902
  if (!emitQueue.has(property.type)) {
853
903
  visitType(context, property.type);
854
904
  }
905
+ if (property.flatten && property.type.kind === "model") {
906
+ flattenPropertyModelMap.set(property, type);
907
+ }
855
908
  }
856
909
  if (type.discriminatedSubtypes) {
857
910
  for (const subType of Object.values(type.discriminatedSubtypes)) {