@azure-tools/typespec-ts 0.47.1 → 0.48.0-alpha.20260108.1

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 (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +5 -1
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/lib.d.ts +11 -2
  6. package/dist/src/lib.d.ts.map +1 -1
  7. package/dist/src/lib.js +7 -1
  8. package/dist/src/lib.js.map +1 -1
  9. package/dist/src/modular/emitModels.js +1 -1
  10. package/dist/src/modular/emitModels.js.map +1 -1
  11. package/dist/src/modular/emitSamples.js +11 -6
  12. package/dist/src/modular/emitSamples.js.map +1 -1
  13. package/dist/src/modular/helpers/operationHelpers.d.ts +3 -5
  14. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  15. package/dist/src/modular/helpers/operationHelpers.js +76 -42
  16. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  17. package/dist/src/modular/serialization/serializeUtils.d.ts +1 -0
  18. package/dist/src/modular/serialization/serializeUtils.d.ts.map +1 -1
  19. package/dist/src/modular/serialization/serializeUtils.js.map +1 -1
  20. package/dist/src/modular/static-helpers-metadata.d.ts +25 -0
  21. package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
  22. package/dist/src/modular/static-helpers-metadata.js +25 -0
  23. package/dist/src/modular/static-helpers-metadata.js.map +1 -1
  24. package/dist/src/utils/clientUtils.d.ts.map +1 -1
  25. package/dist/src/utils/clientUtils.js +14 -3
  26. package/dist/src/utils/clientUtils.js.map +1 -1
  27. package/dist/src/utils/operationUtil.d.ts +11 -1
  28. package/dist/src/utils/operationUtil.d.ts.map +1 -1
  29. package/dist/src/utils/operationUtil.js +65 -21
  30. package/dist/src/utils/operationUtil.js.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +4 -4
  33. package/src/index.ts +4 -1
  34. package/src/lib.ts +8 -2
  35. package/src/modular/emitModels.ts +1 -1
  36. package/src/modular/emitSamples.ts +10 -3
  37. package/src/modular/helpers/operationHelpers.ts +100 -52
  38. package/src/modular/serialization/serializeUtils.ts +4 -0
  39. package/src/modular/static-helpers-metadata.ts +25 -0
  40. package/src/utils/clientUtils.ts +13 -3
  41. package/src/utils/operationUtil.ts +71 -24
  42. package/static/static-helpers/serialization/build-newline-collection.ts +3 -0
  43. package/static/static-helpers/serialization/parse-csv-collection.ts +3 -0
  44. package/static/static-helpers/serialization/parse-newline-collection.ts +3 -0
  45. package/static/static-helpers/serialization/parse-pipe-collection.ts +3 -0
  46. package/static/static-helpers/serialization/parse-ssv-collection.ts +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-tools/typespec-ts",
3
- "version": "0.47.1",
3
+ "version": "0.48.0-alpha.20260108.1",
4
4
  "description": "An experimental TypeSpec emitter for TypeScript RLC",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  "@azure-tools/typespec-autorest": "^0.63.0",
27
27
  "@azure-tools/typespec-azure-core": "^0.63.0",
28
28
  "@azure-tools/typespec-azure-resource-manager": "^0.63.0",
29
- "@azure-tools/typespec-client-generator-core": "^0.63.0",
29
+ "@azure-tools/typespec-client-generator-core": "^0.63.3",
30
30
  "@azure/abort-controller": "^2.1.2",
31
31
  "@azure/core-auth": "^1.6.0",
32
32
  "@azure/core-lro": "^3.1.0",
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@azure-tools/typespec-azure-core": "^0.63.0",
68
- "@azure-tools/typespec-client-generator-core": "^0.63.0",
68
+ "@azure-tools/typespec-client-generator-core": "^0.63.3",
69
69
  "@typespec/compiler": "^1.7.0",
70
70
  "@typespec/http": "^1.7.0",
71
71
  "@typespec/rest": "^0.77.0",
@@ -73,7 +73,7 @@
73
73
  "@typespec/xml": "^0.77.0"
74
74
  },
75
75
  "dependencies": {
76
- "@azure-tools/rlc-common": "^0.47.1",
76
+ "@azure-tools/rlc-common": "0.48.0-alpha.20260108.1",
77
77
  "fs-extra": "^11.1.0",
78
78
  "lodash": "^4.17.21",
79
79
  "prettier": "^3.3.3",
package/src/index.ts CHANGED
@@ -258,7 +258,10 @@ export async function $onEmit(context: EmitContext) {
258
258
  for (const client of clients) {
259
259
  const rlcModels = await transformRLCModel(client, dpgContext);
260
260
  rlcCodeModels.push(rlcModels);
261
- serviceNameToRlcModelsMap.set(client.service.name, rlcModels);
261
+ const serviceName = Array.isArray(client.service)
262
+ ? (client.service[0]?.name ?? "Unknown")
263
+ : client.service.name;
264
+ serviceNameToRlcModelsMap.set(serviceName, rlcModels);
262
265
  needUnexpectedHelper.set(
263
266
  getClientName(rlcModels),
264
267
  hasUnexpectedHelper(rlcModels)
package/src/lib.ts CHANGED
@@ -54,7 +54,7 @@ export interface EmitterOptions {
54
54
  "azure-arm"?: boolean;
55
55
  "source-from"?: "TypeSpec" | "Swagger";
56
56
  "is-modular-library"?: boolean;
57
- "module-kind"?: "esm" | "cjs";
57
+ "module-kind"?: "esm";
58
58
  "enable-operation-group"?: boolean;
59
59
  flavor?: PackageFlavor;
60
60
  "enable-model-namespace"?: boolean;
@@ -272,7 +272,7 @@ export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
272
272
  "module-kind": {
273
273
  type: "string",
274
274
  nullable: true,
275
- enum: ["esm", "cjs"],
275
+ enum: ["esm"],
276
276
  default: "esm",
277
277
  description: "Internal option for test."
278
278
  },
@@ -608,6 +608,12 @@ const libDef = {
608
608
  messages: {
609
609
  default: paramMessage`Model name conflict detected: "${"modelName"}" exists in multiple namespaces: ${"namespaces"}. Please use @clientName to rename them.`
610
610
  }
611
+ },
612
+ "un-supported-array-encoding": {
613
+ severity: "warning",
614
+ messages: {
615
+ default: paramMessage`The array property "${"arrayName"}" of ${"arrayType"} type is not supported for encoding and will be ignored.`
616
+ }
611
617
  }
612
618
  },
613
619
  emitter: {
@@ -670,7 +670,7 @@ export function normalizeModelName(
670
670
  : "";
671
671
  const internalModelPrefix =
672
672
  isPagedResultModel(context, type) || type.isGeneratedName ? "_" : "";
673
- return `${internalModelPrefix}${normalizeName(namespacePrefix + type.name + unionSuffix, nameType, true)}`;
673
+ return `${internalModelPrefix}${normalizeName(namespacePrefix + type.name, nameType, true)}${unionSuffix}`;
674
674
  }
675
675
 
676
676
  function buildModelPolymorphicType(context: SdkContext, type: SdkModelType) {
@@ -571,12 +571,19 @@ function getParameterValue(
571
571
  }
572
572
  let propRetValue;
573
573
 
574
- if (property?.flatten && property.type.kind === "model") {
574
+ if (
575
+ property?.flatten &&
576
+ property.type.kind === "model" &&
577
+ options?.overrides?.enableFlatten !== false
578
+ ) {
579
+ // For flatten property, we need to recursively get its properties
580
+ // but disable further flattening to match the TypeScript interface structure
575
581
  const paramValue = getParameterValue(context, propValue, {
576
582
  overrides: {
577
583
  propertyRenames:
578
584
  useContext("sdkTypes").flattenProperties.get(property)
579
- ?.conflictMap
585
+ ?.conflictMap,
586
+ enableFlatten: false
580
587
  }
581
588
  });
582
589
  propRetValue =
@@ -584,7 +591,7 @@ function getParameterValue(
584
591
  } else {
585
592
  propRetValue =
586
593
  `"${mapper.get(propName) ?? propName}": ` +
587
- getParameterValue(context, propValue);
594
+ getParameterValue(context, propValue, options);
588
595
  }
589
596
  if (propRetValue) values.push(propRetValue);
590
597
  }
@@ -21,7 +21,10 @@ import {
21
21
  getCollectionFormatHelper,
22
22
  hasCollectionFormatInfo,
23
23
  isBinaryPayload,
24
- ServiceOperation
24
+ ServiceOperation,
25
+ getCollectionFormatParseHelper,
26
+ getCollectionFormatFromArrayEncoding,
27
+ KnownCollectionFormat
25
28
  } from "../../utils/operationUtil.js";
26
29
  import {
27
30
  getPropertyWithOverrides,
@@ -245,6 +248,7 @@ export function getDeserializePrivateFunction(
245
248
  context,
246
249
  deserializedType,
247
250
  deserializedRoot,
251
+ true,
248
252
  isBinaryPayload(context, response.type!.__raw!, contentTypes!)
249
253
  ? "binary"
250
254
  : getEncodeForType(deserializedType)
@@ -856,6 +860,7 @@ function buildBodyParameter(
856
860
  isBinaryPayload(context, bodyParameter.__raw!, bodyParameter.contentTypes)
857
861
  ? "binary"
858
862
  : getEncodeForType(bodyParameter.type),
863
+ undefined,
859
864
  true
860
865
  );
861
866
  return `\nbody: ${serializedBody.startsWith(nullOrUndefinedPrefix) ? "" : nullOrUndefinedPrefix}${serializedBody},`;
@@ -884,7 +889,7 @@ export function getParameterMap(
884
889
  }
885
890
 
886
891
  if (hasCollectionFormatInfo(param.kind, (param as any).collectionFormat)) {
887
- return getCollectionFormat(context, param, optionalParamName);
892
+ return getCollectionFormatForParam(context, param, optionalParamName);
888
893
  }
889
894
 
890
895
  // if the parameter or property is optional, we don't need to handle the default value
@@ -909,39 +914,22 @@ export function getParameterMap(
909
914
  return `"${param.name}": undefined`;
910
915
  }
911
916
 
912
- function getCollectionFormat(
917
+ function getCollectionFormatForParam(
913
918
  context: SdkContext,
914
919
  param: SdkHttpParameter,
915
920
  optionalParamName: string = "options"
916
921
  ) {
917
922
  const serializedName = getPropertySerializedName(param);
918
923
  const format = (param as any).collectionFormat;
919
- const collectionInfo = getCollectionFormatHelper(param.kind, format ?? "");
920
- if (!collectionInfo) {
921
- throw "Has collection format info but without helper function detected";
922
- }
923
- const isMulti = format.toLowerCase() === "multi";
924
- const additionalParam = isMulti ? `, "${serializedName}"` : "";
925
- if (!param.optional) {
926
- return `"${serializedName}": ${collectionInfo}(${serializeRequestValue(
927
- context,
928
- param.type,
929
- param.name,
930
- true,
931
- getEncodeForType(param.type),
932
- true
933
- )}${additionalParam})`;
934
- }
935
- return `"${serializedName}": ${optionalParamName}?.${
936
- param.name
937
- } !== undefined ? ${collectionInfo}(${serializeRequestValue(
924
+ return `"${serializedName}": ${serializeRequestValue(
938
925
  context,
939
926
  param.type,
940
- `${optionalParamName}?.${param.name}`,
941
- false,
942
- getEncodeForType(param.type),
927
+ param.optional ? `${optionalParamName}?.${param.name}` : param.name,
928
+ !param.optional,
929
+ format,
930
+ serializedName,
943
931
  true
944
- )}${additionalParam}): undefined`;
932
+ )}`;
945
933
  }
946
934
 
947
935
  function isContentType(param: SdkHttpParameter): boolean {
@@ -992,6 +980,7 @@ function getRequired(context: SdkContext, param: SdkHttpParameter) {
992
980
  clientValue,
993
981
  true,
994
982
  getEncodeForType(param.type),
983
+ serializedName,
995
984
  true
996
985
  )}`;
997
986
  }
@@ -1033,6 +1022,7 @@ function getOptional(
1033
1022
  paramName,
1034
1023
  false,
1035
1024
  getEncodeForType(param.type),
1025
+ serializedName,
1036
1026
  true
1037
1027
  )}`;
1038
1028
  }
@@ -1172,6 +1162,41 @@ function getNullableCheck(name: string, type: SdkType) {
1172
1162
  return `${name} === null ? null :`;
1173
1163
  }
1174
1164
 
1165
+ /**
1166
+ * Determines the appropriate encoding format for a model property, especially for arrays with collection format encoding.
1167
+ * For example, returns "csv" for comma-delimited arrays or the property's type encoding for regular properties.
1168
+ */
1169
+ function getEncodeForModelProperty(
1170
+ context: SdkContext,
1171
+ property: SdkModelPropertyType
1172
+ ): string | undefined {
1173
+ if (property.encode && property.type.kind === "array") {
1174
+ // Only arrays of string type can have collectionFormat encoding
1175
+ if (property.type.valueType.kind !== "string") {
1176
+ reportDiagnostic(context.program, {
1177
+ code: "un-supported-array-encoding",
1178
+ format: {
1179
+ arrayName: property.name,
1180
+ arrayType: property.type.valueType.kind
1181
+ },
1182
+ target: NoTarget
1183
+ });
1184
+ return getEncodeForType(property.type);
1185
+ }
1186
+
1187
+ const collectionFormat = getCollectionFormatFromArrayEncoding(
1188
+ property.encode
1189
+ );
1190
+ if (
1191
+ collectionFormat &&
1192
+ hasCollectionFormatInfo(property.kind, collectionFormat)
1193
+ ) {
1194
+ return collectionFormat;
1195
+ }
1196
+ }
1197
+ return getEncodeForType(property.type);
1198
+ }
1199
+
1175
1200
  function getSerializationExpressionForFlatten(
1176
1201
  context: SdkContext,
1177
1202
  property: SdkModelPropertyType,
@@ -1194,9 +1219,6 @@ function getSerializationExpressionForFlatten(
1194
1219
  !isReadOnly(p) &&
1195
1220
  !isMetadata(context.program, p.__raw!)
1196
1221
  );
1197
- if (validProps.length === 0) {
1198
- return `undefined`;
1199
- }
1200
1222
  const optionalPrefix = property.optional
1201
1223
  ? `${resolveReference(SerializationHelpers.areAllPropsUndefined)}(${propertyPath}, [${validProps
1202
1224
  .map((p) => `"${p.name}"`)
@@ -1245,7 +1267,8 @@ export function getSerializationExpression(
1245
1267
  property.type,
1246
1268
  propertyFullName,
1247
1269
  !property.optional,
1248
- getEncodeForType(property.type),
1270
+ getEncodeForModelProperty(context, property),
1271
+ getPropertySerializedName(property),
1249
1272
  propertyPath === "" ? true : false
1250
1273
  );
1251
1274
  }
@@ -1373,20 +1396,17 @@ export function getResponseMapping(
1373
1396
  context,
1374
1397
  property.type,
1375
1398
  `${propertyPath}${dot}["${serializedName}"]`,
1376
- getEncodeForType(property.type)
1377
- );
1378
- props.push(
1379
- `${propertyName}: ${deserializeValue === `${propertyPath}${dot}["${serializedName}"]` ? "" : nullOrUndefinedPrefix}${deserializeValue}`
1399
+ !property.optional,
1400
+ getEncodeForModelProperty(context, property)
1380
1401
  );
1402
+ props.push(`${propertyName}: ${deserializeValue}`);
1381
1403
  }
1382
1404
  }
1383
1405
  return props;
1384
1406
  }
1385
1407
 
1386
1408
  /**
1387
- * This function helps converting strings into JS complex types recursively.
1388
- * We need to drill down into Array elements to make sure that the element type is
1389
- * deserialized correctly
1409
+ * Converts JavaScript values to their serialized wire format for HTTP requests.
1390
1410
  */
1391
1411
  export function serializeRequestValue(
1392
1412
  context: SdkContext,
@@ -1394,6 +1414,7 @@ export function serializeRequestValue(
1394
1414
  clientValue: string,
1395
1415
  required: boolean,
1396
1416
  format?: string,
1417
+ serializedName?: string,
1397
1418
  isTopLevel: boolean = false
1398
1419
  ): string {
1399
1420
  const getSdkType = useSdkTypes();
@@ -1414,8 +1435,8 @@ export function serializeRequestValue(
1414
1435
  return `${nullOrUndefinedPrefix}${clientValue}.toISOString()`;
1415
1436
  }
1416
1437
  case "array": {
1417
- const prefix = nullOrUndefinedPrefix + clientValue;
1418
1438
  if (type.valueType) {
1439
+ const prefix = nullOrUndefinedPrefix + clientValue;
1419
1440
  const elementNullOrUndefinedPrefix =
1420
1441
  isTypeNullable(type.valueType) || getOptionalForType(type.valueType)
1421
1442
  ? "!p ? p : "
@@ -1435,7 +1456,17 @@ export function serializeRequestValue(
1435
1456
  ) {
1436
1457
  return `${prefix}.map((p: any) => { return ${elementNullOrUndefinedPrefix}p})`;
1437
1458
  } else {
1438
- return `${prefix}.map((p: any) => { return ${elementNullOrUndefinedPrefix}${serializeRequestValue(context, type.valueType, "p", true, getEncodeForType(type.valueType))}})`;
1459
+ const serializedValue = `${clientValue}.map((p: any) => { return ${elementNullOrUndefinedPrefix}${serializeRequestValue(context, type.valueType, "p", true, getEncodeForType(type.valueType))}})`;
1460
+ if (format) {
1461
+ const formatHelper = getCollectionFormatHelper(format);
1462
+ if (formatHelper) {
1463
+ if (format?.toLowerCase() === KnownCollectionFormat.Multi) {
1464
+ return `${nullOrUndefinedPrefix}${formatHelper}(${serializedValue}, "${serializedName}")`;
1465
+ }
1466
+ return `${nullOrUndefinedPrefix}${formatHelper}(${serializedValue})`;
1467
+ }
1468
+ }
1469
+ return `${nullOrUndefinedPrefix}${serializedValue}`;
1439
1470
  }
1440
1471
  }
1441
1472
  return clientValue;
@@ -1507,14 +1538,16 @@ export function deserializeResponseValue(
1507
1538
  context: SdkContext,
1508
1539
  type: SdkType,
1509
1540
  restValue: string,
1510
- format?: string
1541
+ required: boolean,
1542
+ format?: string,
1543
+ recursionDepth: number = 0
1511
1544
  ): string {
1512
1545
  const dependencies = useDependencies();
1513
1546
  const stringToUint8ArrayReference = resolveReference(
1514
1547
  dependencies.stringToUint8Array
1515
1548
  );
1516
1549
  const nullOrUndefinedPrefix =
1517
- isTypeNullable(type) || getOptionalForType(type)
1550
+ isTypeNullable(type) || getOptionalForType(type) || !required
1518
1551
  ? `!${restValue}? ${restValue}: `
1519
1552
  : "";
1520
1553
  switch (type.kind) {
@@ -1522,12 +1555,13 @@ export function deserializeResponseValue(
1522
1555
  return `${nullOrUndefinedPrefix} new Date(${type.encode === "unixTimestamp" ? `${restValue} * 1000` : restValue})`;
1523
1556
  case "array": {
1524
1557
  const prefix = nullOrUndefinedPrefix + restValue;
1558
+ const varName = recursionDepth > 0 ? `p${recursionDepth}` : "p";
1525
1559
  let elementNullOrUndefinedPrefix = "";
1526
1560
  if (
1527
1561
  type.valueType &&
1528
1562
  (isTypeNullable(type.valueType) || getOptionalForType(type.valueType))
1529
1563
  ) {
1530
- elementNullOrUndefinedPrefix = "!p ? p :";
1564
+ elementNullOrUndefinedPrefix = `!${varName} ? ${varName} :`;
1531
1565
  }
1532
1566
  const deserializeFunctionName = type.valueType
1533
1567
  ? buildModelDeserializer(
@@ -1540,26 +1574,38 @@ export function deserializeResponseValue(
1540
1574
  )
1541
1575
  : undefined;
1542
1576
  if (deserializeFunctionName) {
1543
- return `${prefix}.map((p: any) => { return ${elementNullOrUndefinedPrefix}${deserializeFunctionName}(p)})`;
1577
+ return `${prefix}.map((${varName}: any) => { return ${elementNullOrUndefinedPrefix}${deserializeFunctionName}(${varName})})`;
1544
1578
  } else if (
1545
1579
  type.valueType &&
1546
1580
  isAzureCoreErrorType(context.program, type.valueType.__raw)
1547
1581
  ) {
1548
- return `${prefix}.map((p: any) => { return ${elementNullOrUndefinedPrefix}p})`;
1582
+ return `${prefix}.map((${varName}: any) => { return ${elementNullOrUndefinedPrefix}${varName}})`;
1549
1583
  } else if (type.valueType) {
1550
- return `${prefix}.map((p: any) => { return ${elementNullOrUndefinedPrefix}${deserializeResponseValue(context, type.valueType, "p", getEncodeForType(type.valueType))}})`;
1584
+ if (format) {
1585
+ const parseHelper = getCollectionFormatParseHelper(format);
1586
+ if (parseHelper) {
1587
+ // We shouldn't check for an empty string here since an empty string should be parsed as an empty array
1588
+ const optionalPrefixForString =
1589
+ isTypeNullable(type) || getOptionalForType(type) || !required
1590
+ ? `${restValue} === null || ${restValue} === undefined ? ${restValue}: `
1591
+ : "";
1592
+ return `${optionalPrefixForString}${parseHelper}(${restValue})`;
1593
+ }
1594
+ }
1595
+ return `${prefix}.map((${varName}: any) => { return ${elementNullOrUndefinedPrefix}${deserializeResponseValue(context, type.valueType, varName, true, getEncodeForType(type.valueType), recursionDepth + 1)}})`;
1551
1596
  } else {
1552
1597
  return restValue;
1553
1598
  }
1554
1599
  }
1555
1600
  case "dict": {
1556
- const prefix = nullOrUndefinedPrefix + restValue;
1601
+ const keyVar = recursionDepth > 0 ? `k${recursionDepth}` : "k";
1602
+ const valueVar = recursionDepth > 0 ? `p${recursionDepth}` : "p";
1557
1603
  let elementNullOrUndefinedPrefix = "";
1558
1604
  if (
1559
1605
  type.valueType &&
1560
1606
  (isTypeNullable(type.valueType) || getOptionalForType(type.valueType))
1561
1607
  ) {
1562
- elementNullOrUndefinedPrefix = "!p ? p :";
1608
+ elementNullOrUndefinedPrefix = `!${valueVar} ? ${valueVar} :`;
1563
1609
  }
1564
1610
  const deserializeFunctionName = type.valueType
1565
1611
  ? buildModelDeserializer(
@@ -1572,21 +1618,21 @@ export function deserializeResponseValue(
1572
1618
  )
1573
1619
  : undefined;
1574
1620
  if (deserializeFunctionName) {
1575
- return `Object.fromEntries(Object.entries(${prefix}).map(([k, p]: [string, any]) => [k, ${elementNullOrUndefinedPrefix}${deserializeFunctionName}(p)]))`;
1621
+ return `${nullOrUndefinedPrefix}Object.fromEntries(Object.entries(${restValue}).map(([${keyVar}, ${valueVar}]: [string, any]) => [${keyVar}, ${elementNullOrUndefinedPrefix}${deserializeFunctionName}(${valueVar})]))`;
1576
1622
  } else if (
1577
1623
  type.valueType &&
1578
1624
  isAzureCoreErrorType(context.program, type.valueType.__raw)
1579
1625
  ) {
1580
- return `Object.fromEntries(Object.entries(${prefix}).map(([k, p]: [string, any]) => [k, ${elementNullOrUndefinedPrefix}p]))`;
1626
+ return `${nullOrUndefinedPrefix}Object.fromEntries(Object.entries(${restValue}).map(([${keyVar}, ${valueVar}]: [string, any]) => [${keyVar}, ${elementNullOrUndefinedPrefix}${valueVar}]))`;
1581
1627
  } else if (type.valueType) {
1582
- return `Object.fromEntries(Object.entries(${prefix}).map(([k, p]: [string, any]) => [k, ${elementNullOrUndefinedPrefix}${deserializeResponseValue(context, type.valueType, "p", getEncodeForType(type.valueType))}]))`;
1628
+ return `${nullOrUndefinedPrefix}Object.fromEntries(Object.entries(${restValue}).map(([${keyVar}, ${valueVar}]: [string, any]) => [${keyVar}, ${elementNullOrUndefinedPrefix}${deserializeResponseValue(context, type.valueType, valueVar, true, getEncodeForType(type.valueType), recursionDepth + 1)}]))`;
1583
1629
  } else {
1584
1630
  return restValue;
1585
1631
  }
1586
1632
  }
1587
1633
  case "bytes":
1588
1634
  if (format !== "binary" && format !== "bytes") {
1589
- return `typeof ${restValue} === 'string'
1635
+ return `${nullOrUndefinedPrefix}typeof ${restValue} === 'string'
1590
1636
  ? ${stringToUint8ArrayReference}(${restValue}, "${format ?? "base64"}")
1591
1637
  : ${restValue}`;
1592
1638
  }
@@ -1616,7 +1662,9 @@ export function deserializeResponseValue(
1616
1662
  context,
1617
1663
  type.type,
1618
1664
  restValue,
1619
- getEncodeForType(type.type)
1665
+ false,
1666
+ getEncodeForType(type.type),
1667
+ recursionDepth + 1
1620
1668
  );
1621
1669
  default:
1622
1670
  return restValue;
@@ -201,6 +201,10 @@ export interface ModelOverrideOptions {
201
201
  // The <Property, Client_Name> map for any renamed properties.
202
202
  // Mainly because the original client name has collision with other property names during flattening.
203
203
  propertyRenames?: Map<SdkModelPropertyType, string>;
204
+
205
+ // If true (default), enable flattening for nested flatten properties when generating samples.
206
+ // When false, nested flatten properties are not further flattened to match the TypeScript interface structure.
207
+ enableFlatten?: boolean;
204
208
  }
205
209
 
206
210
  export function getPropertyWithOverrides(
@@ -24,6 +24,31 @@ export const SerializationHelpers = {
24
24
  name: "buildTsvCollection",
25
25
  location: "serialization/build-tsv-collection.ts"
26
26
  },
27
+ buildNewlineCollection: {
28
+ kind: "function",
29
+ name: "buildNewlineCollection",
30
+ location: "serialization/build-newline-collection.ts"
31
+ },
32
+ parseCsvCollection: {
33
+ kind: "function",
34
+ name: "parseCsvCollection",
35
+ location: "serialization/parse-csv-collection.ts"
36
+ },
37
+ parsePipeCollection: {
38
+ kind: "function",
39
+ name: "parsePipeCollection",
40
+ location: "serialization/parse-pipe-collection.ts"
41
+ },
42
+ parseSsvCollection: {
43
+ kind: "function",
44
+ name: "parseSsvCollection",
45
+ location: "serialization/parse-ssv-collection.ts"
46
+ },
47
+ parseNewlineCollection: {
48
+ kind: "function",
49
+ name: "parseNewlineCollection",
50
+ location: "serialization/parse-newline-collection.ts"
51
+ },
27
52
  serializeRecord: {
28
53
  kind: "function",
29
54
  name: "serializeRecord",
@@ -20,7 +20,14 @@ import { NameType, normalizeName } from "@azure-tools/rlc-common";
20
20
 
21
21
  export function getRLCClients(dpgContext: SdkContext): SdkClient[] {
22
22
  const services = new Set<Namespace>();
23
- listClients(dpgContext).forEach((c) => services.add(c.service));
23
+ listClients(dpgContext).forEach((c) => {
24
+ const clientService = c.service;
25
+ if (Array.isArray(clientService)) {
26
+ clientService.forEach((ns) => services.add(ns));
27
+ } else {
28
+ services.add(clientService);
29
+ }
30
+ });
24
31
  const rawServiceNamespaces = listAllServiceNamespaces(dpgContext);
25
32
  if (services.size === 0 && rawServiceNamespaces.length > 0) {
26
33
  // If no clients are found, fall back to raw service namespaces
@@ -45,7 +52,10 @@ export function getRLCClients(dpgContext: SdkContext): SdkClient[] {
45
52
 
46
53
  export function listOperationsUnderRLCClient(client: SdkClient): Operation[] {
47
54
  const operations = [];
48
- const queue: (Namespace | Interface)[] = [client.service];
55
+ const serviceArray = Array.isArray(client.service)
56
+ ? client.service
57
+ : [client.service];
58
+ const queue: (Namespace | Interface)[] = [...serviceArray];
49
59
  while (queue.length > 0) {
50
60
  const current = queue.shift()!;
51
61
  if (
@@ -55,7 +65,7 @@ export function listOperationsUnderRLCClient(client: SdkClient): Operation[] {
55
65
  getNamespaceFullName(d.definition?.namespace) ===
56
66
  "Azure.ClientGenerator.Core"
57
67
  ) &&
58
- current !== client.service
68
+ !serviceArray.includes(current as Namespace)
59
69
  ) {
60
70
  continue;
61
71
  }
@@ -461,7 +461,8 @@ export function hasCollectionFormatInfo(
461
461
  getHasSsvCollection(paramType, paramFormat) ||
462
462
  getHasTsvCollection(paramType, paramFormat) ||
463
463
  getHasCsvCollection(paramType, paramFormat) ||
464
- getHasPipeCollection(paramType, paramFormat)
464
+ getHasPipeCollection(paramType, paramFormat) ||
465
+ getHasNewlineCollection(paramType, paramFormat)
465
466
  );
466
467
  }
467
468
 
@@ -503,50 +504,96 @@ function getHasMultiCollection(
503
504
  ) {
504
505
  return (
505
506
  ((includeQuery && paramType === "query") || paramType === "header") &&
506
- paramFormat === "multi"
507
+ paramFormat === KnownCollectionFormat.Multi
507
508
  );
508
509
  }
509
510
  function getHasSsvCollection(paramType: string, paramFormat: string) {
510
- return paramType === "query" && paramFormat === "ssv";
511
+ return (
512
+ (paramType === "query" || paramType === "property") &&
513
+ paramFormat === KnownCollectionFormat.Ssv
514
+ );
511
515
  }
512
516
 
513
517
  function getHasTsvCollection(paramType: string, paramFormat: string) {
514
- return paramType === "query" && paramFormat === "tsv";
518
+ return paramType === "query" && paramFormat === KnownCollectionFormat.Tsv;
515
519
  }
516
520
 
517
521
  function getHasCsvCollection(paramType: string, paramFormat: string) {
518
- return paramType === "header" && paramFormat === "csv";
522
+ return (
523
+ (paramType === "header" || paramType === "property") &&
524
+ paramFormat === KnownCollectionFormat.Csv
525
+ );
519
526
  }
520
527
 
521
528
  function getHasPipeCollection(paramType: string, paramFormat: string) {
522
- return paramType === "query" && paramFormat === "pipes";
529
+ return (
530
+ (paramType === "query" || paramType === "property") &&
531
+ paramFormat === KnownCollectionFormat.Pipes
532
+ );
523
533
  }
524
534
 
525
- export function getCollectionFormatHelper(
526
- paramType: string,
527
- paramFormat: string
528
- ) {
529
- if (getHasMultiCollection(paramType, paramFormat, false)) {
530
- return resolveReference(SerializationHelpers.buildMultiCollection);
531
- }
532
-
533
- if (getHasPipeCollection(paramType, paramFormat)) {
534
- return resolveReference(SerializationHelpers.buildPipeCollection);
535
- }
535
+ function getHasNewlineCollection(paramType: string, paramFormat: string) {
536
+ return (
537
+ paramType === "property" && paramFormat === KnownCollectionFormat.Newline
538
+ );
539
+ }
536
540
 
537
- if (getHasSsvCollection(paramType, paramFormat)) {
538
- return resolveReference(SerializationHelpers.buildSsvCollection);
541
+ export function getCollectionFormatHelper(format: string) {
542
+ switch (format) {
543
+ case KnownCollectionFormat.Multi:
544
+ return resolveReference(SerializationHelpers.buildMultiCollection);
545
+ case KnownCollectionFormat.Pipes:
546
+ return resolveReference(SerializationHelpers.buildPipeCollection);
547
+ case KnownCollectionFormat.Ssv:
548
+ return resolveReference(SerializationHelpers.buildSsvCollection);
549
+ case KnownCollectionFormat.Tsv:
550
+ return resolveReference(SerializationHelpers.buildTsvCollection);
551
+ case KnownCollectionFormat.Csv:
552
+ return resolveReference(SerializationHelpers.buildCsvCollection);
553
+ case KnownCollectionFormat.Newline:
554
+ return resolveReference(SerializationHelpers.buildNewlineCollection);
555
+ default:
556
+ return undefined;
539
557
  }
558
+ }
540
559
 
541
- if (getHasTsvCollection(paramType, paramFormat)) {
542
- return resolveReference(SerializationHelpers.buildTsvCollection);
560
+ export function getCollectionFormatParseHelper(format: string) {
561
+ switch (format) {
562
+ case KnownCollectionFormat.Pipes:
563
+ return resolveReference(SerializationHelpers.parsePipeCollection);
564
+ case KnownCollectionFormat.Ssv:
565
+ return resolveReference(SerializationHelpers.parseSsvCollection);
566
+ case KnownCollectionFormat.Csv:
567
+ return resolveReference(SerializationHelpers.parseCsvCollection);
568
+ case KnownCollectionFormat.Newline:
569
+ return resolveReference(SerializationHelpers.parseNewlineCollection);
570
+ default:
571
+ return undefined;
543
572
  }
573
+ }
544
574
 
545
- if (getHasCsvCollection(paramType, paramFormat)) {
546
- return resolveReference(SerializationHelpers.buildCsvCollection);
575
+ export function getCollectionFormatFromArrayEncoding(encoding: string) {
576
+ switch (encoding) {
577
+ case "pipeDelimited":
578
+ return KnownCollectionFormat.Pipes;
579
+ case "spaceDelimited":
580
+ return KnownCollectionFormat.Ssv;
581
+ case "commaDelimited":
582
+ return KnownCollectionFormat.Csv;
583
+ case "newlineDelimited":
584
+ return KnownCollectionFormat.Newline;
585
+ default:
586
+ return undefined;
547
587
  }
588
+ }
548
589
 
549
- return undefined;
590
+ export enum KnownCollectionFormat {
591
+ Csv = "csv",
592
+ Ssv = "ssv",
593
+ Tsv = "tsv",
594
+ Pipes = "pipes",
595
+ Newline = "newline",
596
+ Multi = "multi"
550
597
  }
551
598
 
552
599
  export function getCustomRequestHeaderNameForOperation(
@@ -0,0 +1,3 @@
1
+ export function buildNewlineCollection(items: string[] | number[]): string {
2
+ return items.join("\n");
3
+ }