@azure-tools/typespec-ts 0.49.0 → 0.49.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 (106) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/src/framework/hooks/binder.d.ts.map +1 -1
  3. package/dist/src/framework/hooks/binder.js +8 -17
  4. package/dist/src/framework/hooks/binder.js.map +1 -1
  5. package/dist/src/index.d.ts.map +1 -1
  6. package/dist/src/index.js +13 -11
  7. package/dist/src/index.js.map +1 -1
  8. package/dist/src/lib.d.ts +8 -0
  9. package/dist/src/lib.d.ts.map +1 -1
  10. package/dist/src/lib.js +10 -0
  11. package/dist/src/lib.js.map +1 -1
  12. package/dist/src/modular/buildOperations.d.ts.map +1 -1
  13. package/dist/src/modular/buildOperations.js +10 -5
  14. package/dist/src/modular/buildOperations.js.map +1 -1
  15. package/dist/src/modular/buildProjectFiles.d.ts.map +1 -1
  16. package/dist/src/modular/buildProjectFiles.js +13 -10
  17. package/dist/src/modular/buildProjectFiles.js.map +1 -1
  18. package/dist/src/modular/emitModels.d.ts.map +1 -1
  19. package/dist/src/modular/emitModels.js +43 -40
  20. package/dist/src/modular/emitModels.js.map +1 -1
  21. package/dist/src/modular/emitSamples.d.ts.map +1 -1
  22. package/dist/src/modular/emitSamples.js +9 -0
  23. package/dist/src/modular/emitSamples.js.map +1 -1
  24. package/dist/src/modular/helpers/clientHelpers.d.ts.map +1 -1
  25. package/dist/src/modular/helpers/clientHelpers.js +4 -1
  26. package/dist/src/modular/helpers/clientHelpers.js.map +1 -1
  27. package/dist/src/modular/helpers/namingHelpers.d.ts +7 -0
  28. package/dist/src/modular/helpers/namingHelpers.d.ts.map +1 -1
  29. package/dist/src/modular/helpers/namingHelpers.js +15 -0
  30. package/dist/src/modular/helpers/namingHelpers.js.map +1 -1
  31. package/dist/src/modular/helpers/operationHelpers.d.ts +19 -2
  32. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  33. package/dist/src/modular/helpers/operationHelpers.js +410 -39
  34. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  35. package/dist/src/modular/serialization/buildDeserializerFunction.d.ts.map +1 -1
  36. package/dist/src/modular/serialization/buildDeserializerFunction.js +9 -3
  37. package/dist/src/modular/serialization/buildDeserializerFunction.js.map +1 -1
  38. package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts +12 -0
  39. package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts.map +1 -1
  40. package/dist/src/modular/serialization/buildXmlSerializerFunction.js +259 -18
  41. package/dist/src/modular/serialization/buildXmlSerializerFunction.js.map +1 -1
  42. package/dist/src/modular/static-helpers-metadata.d.ts +10 -0
  43. package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
  44. package/dist/src/modular/static-helpers-metadata.js +10 -0
  45. package/dist/src/modular/static-helpers-metadata.js.map +1 -1
  46. package/dist/src/modular/type-expressions/get-model-expression.d.ts +3 -1
  47. package/dist/src/modular/type-expressions/get-model-expression.d.ts.map +1 -1
  48. package/dist/src/modular/type-expressions/get-model-expression.js +50 -9
  49. package/dist/src/modular/type-expressions/get-model-expression.js.map +1 -1
  50. package/dist/src/modular/type-expressions/get-nullable-expression.d.ts.map +1 -1
  51. package/dist/src/modular/type-expressions/get-nullable-expression.js +8 -0
  52. package/dist/src/modular/type-expressions/get-nullable-expression.js.map +1 -1
  53. package/dist/src/modular/type-expressions/get-type-expression.d.ts +3 -2
  54. package/dist/src/modular/type-expressions/get-type-expression.d.ts.map +1 -1
  55. package/dist/src/modular/type-expressions/get-type-expression.js.map +1 -1
  56. package/dist/src/transform/transform.d.ts.map +1 -1
  57. package/dist/src/transform/transform.js +10 -10
  58. package/dist/src/transform/transform.js.map +1 -1
  59. package/dist/src/transform/transformSchemas.d.ts.map +1 -1
  60. package/dist/src/transform/transformSchemas.js +4 -4
  61. package/dist/src/transform/transformSchemas.js.map +1 -1
  62. package/dist/src/transform/transfromRLCOptions.d.ts +1 -1
  63. package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
  64. package/dist/src/transform/transfromRLCOptions.js +37 -24
  65. package/dist/src/transform/transfromRLCOptions.js.map +1 -1
  66. package/dist/src/utils/clientUtils.d.ts +1 -1
  67. package/dist/src/utils/clientUtils.d.ts.map +1 -1
  68. package/dist/src/utils/clientUtils.js +40 -17
  69. package/dist/src/utils/clientUtils.js.map +1 -1
  70. package/dist/src/utils/crossLanguageDef.d.ts.map +1 -1
  71. package/dist/src/utils/crossLanguageDef.js +9 -3
  72. package/dist/src/utils/crossLanguageDef.js.map +1 -1
  73. package/dist/src/utils/interfaces.d.ts +2 -1
  74. package/dist/src/utils/interfaces.d.ts.map +1 -1
  75. package/dist/src/utils/modelUtils.d.ts +2 -2
  76. package/dist/src/utils/modelUtils.d.ts.map +1 -1
  77. package/dist/src/utils/modelUtils.js +15 -16
  78. package/dist/src/utils/modelUtils.js.map +1 -1
  79. package/dist/tsconfig.tsbuildinfo +1 -1
  80. package/package.json +22 -22
  81. package/src/framework/hooks/binder.ts +12 -22
  82. package/src/index.ts +17 -10
  83. package/src/lib.ts +20 -0
  84. package/src/modular/buildOperations.ts +12 -4
  85. package/src/modular/buildProjectFiles.ts +19 -16
  86. package/src/modular/emitModels.ts +72 -44
  87. package/src/modular/emitSamples.ts +11 -1
  88. package/src/modular/helpers/clientHelpers.ts +5 -1
  89. package/src/modular/helpers/namingHelpers.ts +19 -0
  90. package/src/modular/helpers/operationHelpers.ts +533 -40
  91. package/src/modular/serialization/buildDeserializerFunction.ts +11 -2
  92. package/src/modular/serialization/buildXmlSerializerFunction.ts +375 -24
  93. package/src/modular/static-helpers-metadata.ts +10 -0
  94. package/src/modular/type-expressions/get-model-expression.ts +78 -13
  95. package/src/modular/type-expressions/get-nullable-expression.ts +9 -0
  96. package/src/modular/type-expressions/get-type-expression.ts +3 -1
  97. package/src/transform/transform.ts +5 -2
  98. package/src/transform/transformSchemas.ts +4 -1
  99. package/src/transform/transfromRLCOptions.ts +65 -24
  100. package/src/utils/clientUtils.ts +49 -16
  101. package/src/utils/crossLanguageDef.ts +8 -0
  102. package/src/utils/interfaces.ts +2 -1
  103. package/src/utils/modelUtils.ts +22 -18
  104. package/static/static-helpers/serialization/serialize-record.ts +3 -3
  105. package/static/static-helpers/serialization/xml-helpers.ts +91 -36
  106. package/static/static-helpers/urlTemplate.ts +5 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-tools/typespec-ts",
3
- "version": "0.49.0",
3
+ "version": "0.49.1",
4
4
  "description": "An experimental TypeSpec emitter for TypeScript RLC",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -18,15 +18,15 @@
18
18
  "license": "MIT",
19
19
  "devDependencies": {
20
20
  "@azure-rest/core-client": "^2.3.1",
21
- "@typespec/http-specs": "0.1.0-alpha.32-dev.1",
22
- "@typespec/spector": "0.1.0-alpha.23-dev.1",
21
+ "@typespec/http-specs": "0.1.0-alpha.33-dev.0",
22
+ "@typespec/spector": "0.1.0-alpha.24-dev.0",
23
23
  "@typespec/spec-api": "0.1.0-alpha.13-dev.0",
24
- "@typespec/tspd": "0.73.3",
25
- "@azure-tools/azure-http-specs": "0.1.0-alpha.37-dev.1",
26
- "@azure-tools/typespec-autorest": "^0.64.0",
27
- "@azure-tools/typespec-azure-core": "^0.64.0",
28
- "@azure-tools/typespec-azure-resource-manager": "^0.64.0",
29
- "@azure-tools/typespec-client-generator-core": "^0.64.4",
24
+ "@typespec/tspd": "0.74.0",
25
+ "@azure-tools/azure-http-specs": "0.1.0-alpha.38-dev.2",
26
+ "@azure-tools/typespec-autorest": "^0.65.0",
27
+ "@azure-tools/typespec-azure-core": "^0.65.0",
28
+ "@azure-tools/typespec-azure-resource-manager": "^0.65.0",
29
+ "@azure-tools/typespec-client-generator-core": "^0.65.1",
30
30
  "@azure/abort-controller": "^2.1.2",
31
31
  "@azure/core-auth": "^1.6.0",
32
32
  "@azure/core-lro": "^3.1.0",
@@ -40,12 +40,12 @@
40
40
  "@types/mocha": "^10.0.6",
41
41
  "@typescript-eslint/eslint-plugin": "^8.28.0",
42
42
  "@typescript-eslint/parser": "^8.28.0",
43
- "@typespec/compiler": "^1.8.0",
44
- "@typespec/http": "^1.8.0",
45
- "@typespec/openapi": "^1.8.0",
46
- "@typespec/rest": "^0.78.0",
43
+ "@typespec/compiler": "^1.9.0",
44
+ "@typespec/http": "^1.9.0",
45
+ "@typespec/openapi": "^1.9.0",
46
+ "@typespec/rest": "^0.79.0",
47
47
  "@typespec/ts-http-runtime": "^0.1.0",
48
- "@typespec/versioning": "^0.78.0",
48
+ "@typespec/versioning": "^0.79.0",
49
49
  "chai": "^4.3.6",
50
50
  "chalk": "^4.0.0",
51
51
  "cross-env": "^7.0.3",
@@ -64,16 +64,16 @@
64
64
  "js-yaml": "^4.1.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "@azure-tools/typespec-azure-core": "^0.64.0",
68
- "@azure-tools/typespec-client-generator-core": "^0.64.4",
69
- "@typespec/compiler": "^1.8.0",
70
- "@typespec/http": "^1.8.0",
71
- "@typespec/rest": "^0.78.0",
72
- "@typespec/versioning": "^0.78.0",
73
- "@typespec/xml": "^0.78.0"
67
+ "@azure-tools/typespec-azure-core": "^0.65.0",
68
+ "@azure-tools/typespec-client-generator-core": "^0.65.1",
69
+ "@typespec/compiler": "^1.9.0",
70
+ "@typespec/http": "^1.9.0",
71
+ "@typespec/rest": "^0.79.0",
72
+ "@typespec/versioning": "^0.79.0",
73
+ "@typespec/xml": "^0.79.0"
74
74
  },
75
75
  "dependencies": {
76
- "@azure-tools/rlc-common": "^0.49.0",
76
+ "@azure-tools/rlc-common": "^0.49.1",
77
77
  "fast-xml-parser": "^4.5.0",
78
78
  "fs-extra": "^11.1.0",
79
79
  "lodash": "^4.17.21",
@@ -15,6 +15,7 @@ import {
15
15
  } from "../load-static-helpers.js";
16
16
  import path from "path/posix";
17
17
  import { normalizePath } from "@typespec/compiler";
18
+ import { generateLocallyUniqueName } from "../../modular/helpers/namingHelpers.js";
18
19
 
19
20
  export interface DeclarationInfo {
20
21
  name: string;
@@ -96,7 +97,7 @@ class BinderImp implements Binder {
96
97
  ): string {
97
98
  const existingNamesInFile =
98
99
  this.symbolsBySourceFile.get(sourceFile) ?? new Set<string>();
99
- return this.generateLocallyUniqueName(name, existingNamesInFile);
100
+ return generateLocallyUniqueName(name, existingNamesInFile);
100
101
  }
101
102
 
102
103
  /**
@@ -113,33 +114,22 @@ class BinderImp implements Binder {
113
114
  .flatMap((i) => i.namedImports as ImportSpecifierStructure[])
114
115
  .map((i) => i.alias ?? i.name);
115
116
 
117
+ // Also check actual imports already added to the source file (e.g., manual imports)
118
+ const actualImports = sourceFile
119
+ .getImportDeclarations()
120
+ .flatMap((imp) => imp.getNamedImports())
121
+ .map(
122
+ (namedImp) => namedImp.getAliasNode()?.getText() ?? namedImp.getName()
123
+ );
124
+
116
125
  const existingDeclarations =
117
126
  this.symbolsBySourceFile.get(sourceFile) ?? new Set<string>();
118
- return this.generateLocallyUniqueName(
127
+ return generateLocallyUniqueName(
119
128
  name,
120
- new Set([...existingImports, ...existingDeclarations])
129
+ new Set([...existingImports, ...actualImports, ...existingDeclarations])
121
130
  );
122
131
  }
123
132
 
124
- /**
125
- * Generates a locally unique name within a set of existing names.
126
- * @param name - The base name.
127
- * @param existingNames - A set of names already in use.
128
- * @returns A unique name not present in the existing names set.
129
- */
130
- private generateLocallyUniqueName(
131
- name: string,
132
- existingNames: Set<string>
133
- ): string {
134
- let uniqueName = name;
135
- let counter = 1;
136
- while (existingNames.has(uniqueName)) {
137
- uniqueName = `${name}_${counter}`;
138
- counter++;
139
- }
140
- return uniqueName;
141
- }
142
-
143
133
  /**
144
134
  * Resolves a reference to a declaration.
145
135
  * If a declaration is not ready yet, a placeholder is added to the source file.
package/src/index.ts CHANGED
@@ -82,6 +82,7 @@ import { buildRestorePoller } from "./modular/buildRestorePoller.js";
82
82
  import { buildSubpathIndexFile } from "./modular/buildSubpathIndex.js";
83
83
  import {
84
84
  createSdkContext,
85
+ listAllServiceNamespaces,
85
86
  SdkClientType,
86
87
  SdkServiceOperation
87
88
  } from "@azure-tools/typespec-client-generator-core";
@@ -90,7 +91,11 @@ import { emitLoggerFile } from "./modular/emitLoggerFile.js";
90
91
  import { emitTypes } from "./modular/emitModels.js";
91
92
  import { existsSync } from "fs";
92
93
  import { getModuleExports } from "./modular/buildProjectFiles.js";
93
- import { getClientHierarchyMap, getRLCClients } from "./utils/clientUtils.js";
94
+ import {
95
+ getClientHierarchyMap,
96
+ getRLCClients,
97
+ getModularClientOptions
98
+ } from "./utils/clientUtils.js";
94
99
  import { join } from "path";
95
100
  import { loadStaticHelpers } from "./framework/load-static-helpers.js";
96
101
  import { packageUsesXmlSerialization } from "./modular/serialization/buildXmlSerializerFunction.js";
@@ -192,6 +197,7 @@ export async function $onEmit(context: EmitContext) {
192
197
  const generationPathDetail: GenerationDirDetail =
193
198
  await calculateGenerationDir();
194
199
  dpgContext.generationPathDetail = generationPathDetail;
200
+ dpgContext.allServiceNamespaces = listAllServiceNamespaces(dpgContext);
195
201
  const options: RLCOptions = transformRLCOptions(emitterOptions, dpgContext);
196
202
  emitterOptions["is-modular-library"] = options.isModularLibrary;
197
203
  emitterOptions["generate-sample"] = options.generateSample;
@@ -261,9 +267,7 @@ export async function $onEmit(context: EmitContext) {
261
267
  for (const client of clients) {
262
268
  const rlcModels = await transformRLCModel(client, dpgContext);
263
269
  rlcCodeModels.push(rlcModels);
264
- const serviceName = Array.isArray(client.service)
265
- ? (client.service[0]?.name ?? "Unknown")
266
- : client.service.name;
270
+ const serviceName = client.services[0]?.name ?? "Unknown";
267
271
  serviceNameToRlcModelsMap.set(serviceName, rlcModels);
268
272
  needUnexpectedHelper.set(
269
273
  getClientName(rlcModels),
@@ -317,7 +321,6 @@ export async function $onEmit(context: EmitContext) {
317
321
  }
318
322
  );
319
323
 
320
- const isMultiClients = dpgContext.sdkPackage.clients.length > 1;
321
324
  emitTypes(dpgContext, { sourceRoot: modularSourcesRoot });
322
325
  buildSubpathIndexFile(modularEmitterOptions, "models", undefined, {
323
326
  recursive: true
@@ -351,7 +354,9 @@ export async function $onEmit(context: EmitContext) {
351
354
  exportIndex: true,
352
355
  interfaceOnly: true
353
356
  });
354
- if (isMultiClients) {
357
+ const { subfolder } = getModularClientOptions(subClient);
358
+ // Generate index file for clients with subfolders (multi-client scenarios and nested clients)
359
+ if (subfolder) {
355
360
  buildSubClientIndexFile(dpgContext, subClient, modularEmitterOptions);
356
361
  }
357
362
  buildRootIndex(
@@ -395,14 +400,16 @@ export async function $onEmit(context: EmitContext) {
395
400
  }
396
401
 
397
402
  function buildMetadataJson() {
398
- const apiVersion = dpgContext.sdkPackage.metadata.apiVersion;
403
+ const apiVersions = dpgContext.sdkPackage.metadata.apiVersions;
399
404
  const emitterVersion = getTypespecTsVersion(context);
400
- if (apiVersion === undefined && emitterVersion === undefined) {
405
+ if (apiVersions === undefined && emitterVersion === undefined) {
401
406
  return;
402
407
  }
403
408
  const content: Metadata = {};
404
- if (apiVersion !== undefined) {
405
- content.apiVersion = apiVersion;
409
+ if (apiVersions !== undefined && apiVersions.size > 0) {
410
+ // Use the first/latest API version if multiple are available
411
+ const firstVersion = Array.from(apiVersions.values())[0];
412
+ content.apiVersion = firstVersion;
406
413
  }
407
414
  if (emitterVersion !== undefined) {
408
415
  content.emitterVersion = emitterVersion;
package/src/lib.ts CHANGED
@@ -15,6 +15,13 @@ import {
15
15
  import { Options } from "prettier";
16
16
 
17
17
  export interface EmitterOptions {
18
+ /**
19
+ * Indicates whether to include response headers in the generated response type for modular operations.
20
+ * When set to true, modular operation responses with model or void bodies will have their headers
21
+ * represented as properties on the response type. Other SDK styles and response shapes are not
22
+ * currently affected by this option.
23
+ */
24
+ "include-headers-in-response"?: boolean;
18
25
  "include-shortcuts"?: boolean;
19
26
  "multi-client"?: boolean;
20
27
  batch?: any[];
@@ -71,12 +78,19 @@ export interface EmitterOptions {
71
78
  "default-value-object"?: boolean;
72
79
  //TODO should remove this after finish the release tool test
73
80
  "should-use-pnpm-dep"?: boolean;
81
+ "ignore-nullable-on-optional"?: boolean;
74
82
  }
75
83
 
76
84
  export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
77
85
  type: "object",
78
86
  additionalProperties: true,
79
87
  properties: {
88
+ "include-headers-in-response": {
89
+ type: "boolean",
90
+ nullable: true,
91
+ description:
92
+ "This option is used to indicate whether to include response headers in the generated response type. When set to true, the generated response type will include response headers as properties."
93
+ },
80
94
  "include-shortcuts": {
81
95
  type: "boolean",
82
96
  nullable: true,
@@ -343,6 +357,12 @@ export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
343
357
  type: "boolean",
344
358
  nullable: true,
345
359
  description: "Internal option for test."
360
+ },
361
+ "ignore-nullable-on-optional": {
362
+ type: "boolean",
363
+ nullable: true,
364
+ description:
365
+ "If an optional property is also marked as nullable, it will be treated as just optional. Defaults to `true` for Azure services."
346
366
  }
347
367
  },
348
368
  required: []
@@ -7,6 +7,7 @@ import {
7
7
  } from "ts-morph";
8
8
  import {
9
9
  getDeserializePrivateFunction,
10
+ getDeserializeHeadersPrivateFunction,
10
11
  getExpectedStatuses,
11
12
  getOperationFunction,
12
13
  getOperationOptionsName,
@@ -85,16 +86,23 @@ export function buildOperationFiles(
85
86
  const sendOperationDeclaration = getSendPrivateFunction(
86
87
  dpgContext,
87
88
  [prefixes, op],
88
- clientType
89
+ clientType,
90
+ client
89
91
  );
90
92
  const deserializeOperationDeclaration = getDeserializePrivateFunction(
91
93
  dpgContext,
92
94
  op
93
95
  );
94
- operationGroupFile.addFunctions([
96
+ const deserializeHeadersDeclaration =
97
+ getDeserializeHeadersPrivateFunction(dpgContext, op);
98
+ const functionsToAdd = [
95
99
  sendOperationDeclaration,
96
100
  deserializeOperationDeclaration
97
- ]);
101
+ ];
102
+ if (deserializeHeadersDeclaration) {
103
+ functionsToAdd.push(deserializeHeadersDeclaration);
104
+ }
105
+ operationGroupFile.addFunctions(functionsToAdd);
98
106
  addDeclaration(
99
107
  operationGroupFile,
100
108
  operationDeclaration,
@@ -191,7 +199,7 @@ export function buildOperationOptions(
191
199
  return {
192
200
  docs: getDocsFromDescription(p.doc),
193
201
  hasQuestionToken: true,
194
- type: getTypeExpression(context, p.type),
202
+ type: getTypeExpression(context, p.type, { isOptional: true }),
195
203
  name: normalizeName(p.name, NameType.Parameter)
196
204
  };
197
205
  })
@@ -35,23 +35,26 @@ function buildExportsForMultiClient(
35
35
  delete packageInfo.exports["./api"];
36
36
  }
37
37
  if (emitterOptions.options.hierarchyClient) {
38
- for (const flattenedClient of clientMap) {
39
- const { subfolder } = getModularClientOptions(flattenedClient);
40
- const client = flattenedClient[1];
41
- const methodMap = getMethodHierarchiesMap(context, client);
42
- for (const [prefixKey, _] of methodMap) {
43
- const prefixes = prefixKey.split("/");
44
- if (prefixKey === "") {
45
- continue;
38
+ // TODO: support api subpath exports for multi-service. Skip for now. https://github.com/Azure/autorest.typescript/issues/3717
39
+ if (!emitterOptions.options.isMultiService) {
40
+ for (const flattenedClient of clientMap) {
41
+ const { subfolder } = getModularClientOptions(flattenedClient);
42
+ const client = flattenedClient[1];
43
+ const methodMap = getMethodHierarchiesMap(context, client);
44
+ for (const [prefixKey, _] of methodMap) {
45
+ const prefixes = prefixKey.split("/");
46
+ if (prefixKey === "") {
47
+ continue;
48
+ }
49
+ const subApiPath = `api/${getClassicalLayerPrefix(
50
+ prefixes,
51
+ NameType.File,
52
+ "/"
53
+ )}`;
54
+ packageInfo.exports[
55
+ `./${subfolder ? subfolder + "/" : ""}${subApiPath}`
56
+ ] = `src/${subfolder ? subfolder + "/" : ""}${subApiPath}/index.ts`;
46
57
  }
47
- const subApiPath = `api/${getClassicalLayerPrefix(
48
- prefixes,
49
- NameType.File,
50
- "/"
51
- )}`;
52
- packageInfo.exports[
53
- `./${subfolder ? subfolder + "/" : ""}${subApiPath}`
54
- ] = `src/${subfolder ? subfolder + "/" : ""}${subApiPath}/index.ts`;
55
58
  }
56
59
  }
57
60
  delete packageInfo.exports["./models"];
@@ -35,7 +35,8 @@ import {
35
35
  } from "@azure-tools/typespec-client-generator-core";
36
36
  import {
37
37
  getExternalModel,
38
- getModelExpression
38
+ getModelExpression,
39
+ getMultipartFileTypeExpression
39
40
  } from "./type-expressions/get-model-expression.js";
40
41
 
41
42
  import { SdkContext } from "../utils/interfaces.js";
@@ -51,6 +52,8 @@ import {
51
52
  import {
52
53
  buildXmlModelSerializer,
53
54
  buildXmlModelDeserializer,
55
+ buildXmlObjectModelSerializer,
56
+ buildXmlObjectModelDeserializer,
54
57
  hasXmlSerialization
55
58
  } from "./serialization/buildXmlSerializerFunction.js";
56
59
  import path from "path";
@@ -75,8 +78,6 @@ import {
75
78
  flattenPropertyModelMap,
76
79
  getAllOperationsFromClient
77
80
  } from "../framework/hooks/sdkTypes.js";
78
- import { resolveReference } from "../framework/reference.js";
79
- import { MultipartHelpers } from "./static-helpers-metadata.js";
80
81
  import { getAllAncestors } from "./helpers/operationHelpers.js";
81
82
  import { getAllProperties } from "./helpers/operationHelpers.js";
82
83
  import { getDirectSubtypes } from "./helpers/typeHelpers.js";
@@ -216,6 +217,10 @@ function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
216
217
  return;
217
218
  }
218
219
  const apiVersionEnumOnly = type.usage === UsageFlags.ApiVersionEnum;
220
+ // Skip known api version enums for multi-service scenarios as users are not allowed to set api versions
221
+ if (apiVersionEnumOnly && context.rlcOptions?.isMultiService) {
222
+ return;
223
+ }
219
224
  const inputUsage = (type.usage & UsageFlags.Input) === UsageFlags.Input;
220
225
  const outputUsage = (type.usage & UsageFlags.Output) === UsageFlags.Output;
221
226
  const exceptionUsage =
@@ -266,6 +271,10 @@ function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
266
271
  }
267
272
 
268
273
  export function getApiVersionEnum(context: SdkContext) {
274
+ // Skip api version enum for multi-service scenarios since each service may have different versions
275
+ if (context.rlcOptions?.isMultiService) {
276
+ return;
277
+ }
269
278
  const apiVersionEnum = context.sdkPackage.enums.find(
270
279
  (e) => e.usage === UsageFlags.ApiVersionEnum
271
280
  );
@@ -293,9 +302,6 @@ export function getModelNamespaces(
293
302
  context: SdkContext,
294
303
  model: SdkType
295
304
  ): string[] {
296
- const deepestNamespace = getNamespaceFullName(
297
- listAllServiceNamespaces(context)[0]!
298
- );
299
305
  if (
300
306
  model.kind === "model" ||
301
307
  model.kind === "enum" ||
@@ -312,6 +318,14 @@ export function getModelNamespaces(
312
318
  return [];
313
319
  }
314
320
  const segments = model.namespace.split(".");
321
+ // Keep full namespace segments if multiple services are present because there isn't a root namespace to trim
322
+ if (context.rlcOptions?.isMultiService) {
323
+ return segments;
324
+ }
325
+
326
+ const allServiceNamespaces =
327
+ context.allServiceNamespaces ?? listAllServiceNamespaces(context);
328
+ const deepestNamespace = getNamespaceFullName(allServiceNamespaces[0]!);
315
329
  const rootNamespace = deepestNamespace.split(".") ?? [];
316
330
  if (segments.length > rootNamespace.length) {
317
331
  while (segments[0] === rootNamespace[0]) {
@@ -371,6 +385,14 @@ function addSerializationFunctions(
371
385
  if (typeOrProperty.kind === "model" && hasXmlSerialization(typeOrProperty)) {
372
386
  const xmlSerializerRefKey = refkey(typeOrProperty, "xmlSerializer");
373
387
  const xmlDeserializerRefKey = refkey(typeOrProperty, "xmlDeserializer");
388
+ const xmlObjectSerializerRefKey = refkey(
389
+ typeOrProperty,
390
+ "xmlObjectSerializer"
391
+ );
392
+ const xmlObjectDeserializerRefKey = refkey(
393
+ typeOrProperty,
394
+ "xmlObjectDeserializer"
395
+ );
374
396
 
375
397
  const xmlSerializationFunction = buildXmlModelSerializer(
376
398
  context,
@@ -385,6 +407,24 @@ function addSerializationFunctions(
385
407
  addDeclaration(sourceFile, xmlSerializationFunction, xmlSerializerRefKey);
386
408
  }
387
409
 
410
+ // Also generate XML object serializer for nested object serialization
411
+ const xmlObjectSerializationFunction = buildXmlObjectModelSerializer(
412
+ context,
413
+ typeOrProperty,
414
+ options
415
+ );
416
+ if (
417
+ xmlObjectSerializationFunction &&
418
+ typeof xmlObjectSerializationFunction !== "string" &&
419
+ xmlObjectSerializationFunction.name
420
+ ) {
421
+ addDeclaration(
422
+ sourceFile,
423
+ xmlObjectSerializationFunction,
424
+ xmlObjectSerializerRefKey
425
+ );
426
+ }
427
+
388
428
  const xmlDeserializationFunction = buildXmlModelDeserializer(
389
429
  context,
390
430
  typeOrProperty,
@@ -401,6 +441,24 @@ function addSerializationFunctions(
401
441
  xmlDeserializerRefKey
402
442
  );
403
443
  }
444
+
445
+ // Also generate XML object deserializer for nested object deserialization
446
+ const xmlObjectDeserializationFunction = buildXmlObjectModelDeserializer(
447
+ context,
448
+ typeOrProperty,
449
+ options
450
+ );
451
+ if (
452
+ xmlObjectDeserializationFunction &&
453
+ typeof xmlObjectDeserializationFunction !== "string" &&
454
+ xmlObjectDeserializationFunction.name
455
+ ) {
456
+ addDeclaration(
457
+ sourceFile,
458
+ xmlObjectDeserializationFunction,
459
+ xmlObjectDeserializerRefKey
460
+ );
461
+ }
404
462
  }
405
463
  }
406
464
 
@@ -777,45 +835,15 @@ function buildModelProperty(
777
835
  .join(" | ");
778
836
  }
779
837
  // eslint-disable-next-line
780
- else if (property.kind === "property" && property.isMultipartFileInput) {
781
- // eslint-disable-next-line
782
- const multipartOptions = property.multipartOptions;
783
- typeExpression = "{";
784
- typeExpression += `contents: ${resolveReference(MultipartHelpers.FileContents)};`;
785
-
786
- const isContentTypeOptional =
787
- multipartOptions?.contentType === undefined ||
788
- multipartOptions.contentType.optional ||
789
- multipartOptions.defaultContentTypes.length > 0;
790
- const isFilenameOptional =
791
- multipartOptions?.filename === undefined ||
792
- multipartOptions.filename.optional;
793
-
794
- const contentTypeType = multipartOptions?.contentType
795
- ? getTypeExpression(context, multipartOptions.contentType.type)
796
- : "string";
797
- const filenameType = multipartOptions?.filename
798
- ? getTypeExpression(context, multipartOptions.filename.type)
799
- : "string";
800
-
801
- typeExpression += `contentType${isContentTypeOptional ? "?" : ""}: ${contentTypeType};`;
802
- typeExpression += `filename${isFilenameOptional ? "?" : ""}: ${filenameType};`;
803
-
804
- typeExpression += "}";
805
-
806
- if (isContentTypeOptional && isFilenameOptional) {
807
- // Allow passing content directly if both filename and content type are optional
808
- typeExpression = `(${resolveReference(MultipartHelpers.FileContents)}) | ${typeExpression}`;
809
- } else {
810
- // If either one is required, still accept File at the top level since it requires a filename
811
- typeExpression = `File | ${typeExpression}`;
812
- }
813
-
814
- if (property.type.kind === "array") {
815
- typeExpression = `Array<${typeExpression}>`;
816
- }
838
+ else if (
839
+ property.kind === "property" &&
840
+ property.serializationOptions.multipart?.isFilePart
841
+ ) {
842
+ typeExpression = getMultipartFileTypeExpression(context, property);
817
843
  } else {
818
- typeExpression = getTypeExpression(context, property.type);
844
+ typeExpression = getTypeExpression(context, property.type, {
845
+ isOptional: property.optional
846
+ });
819
847
  }
820
848
 
821
849
  const propertyStructure: PropertySignatureStructure = {
@@ -11,7 +11,9 @@ import {
11
11
  SdkHttpParameterExampleValue,
12
12
  SdkServiceOperation,
13
13
  SdkExampleValue,
14
- SdkClientInitializationType
14
+ SdkClientInitializationType,
15
+ SdkModelPropertyType,
16
+ isReadOnly
15
17
  } from "@azure-tools/typespec-client-generator-core";
16
18
  import {
17
19
  isAzurePackage,
@@ -449,6 +451,10 @@ function prepareExampleParameters(
449
451
  if (!propExample) {
450
452
  continue;
451
453
  }
454
+ // Skip readonly properties as they cannot be set by users
455
+ if (isReadOnly(prop as SdkModelPropertyType)) {
456
+ continue;
457
+ }
452
458
  result.push(
453
459
  prepareExampleValue(
454
460
  dpgContext,
@@ -597,6 +603,10 @@ function getParameterValue(
597
603
  if (propValue === undefined || propValue === null) {
598
604
  continue;
599
605
  }
606
+ // Skip readonly properties as they cannot be set by users
607
+ if (property && isReadOnly(property as SdkModelPropertyType)) {
608
+ continue;
609
+ }
600
610
  let propRetValue;
601
611
 
602
612
  if (
@@ -105,6 +105,9 @@ export function getClientParameters(
105
105
  const skipMethodParam = (p: SdkParameter) => p.kind !== "method";
106
106
  const armSpecific = (p: SdkParameter) =>
107
107
  !(p.kind === "endpoint" && dpgContext.arm);
108
+ // Skip apiVersion parameter when it's multi-service (each service has its own default apiVersion)
109
+ const skipApiVersionOnMultiService = (p: SdkParameter) =>
110
+ !(dpgContext.rlcOptions?.isMultiService && p.isApiVersionParam);
108
111
  const filters = [
109
112
  options.requiredOnly ? isRequired : undefined,
110
113
  dpgContext.rlcOptions?.addCredentials === false
@@ -112,7 +115,8 @@ export function getClientParameters(
112
115
  : undefined,
113
116
  options.optionalOnly ? isOptional : undefined,
114
117
  options.onClientOnly ? skipMethodParam : undefined,
115
- options.skipArmSpecific ? undefined : armSpecific
118
+ options.skipArmSpecific ? undefined : armSpecific,
119
+ skipApiVersionOnMultiService
116
120
  ];
117
121
  const params = clientParams.filter((p) =>
118
122
  filters.every((filter) => !filter || filter(p))
@@ -73,3 +73,22 @@ export function getClassicalLayerPrefix(
73
73
  export function isDefined<T>(thing: T | undefined | null): thing is T {
74
74
  return typeof thing !== "undefined" && thing !== null;
75
75
  }
76
+
77
+ /**
78
+ * Generates a locally unique name within a set of existing names.
79
+ * @param name - The base name.
80
+ * @param existingNames - A set of names already in use.
81
+ * @returns A unique name not present in the existing names set.
82
+ */
83
+ export function generateLocallyUniqueName(
84
+ name: string,
85
+ existingNames: Set<string>
86
+ ): string {
87
+ let uniqueName = name;
88
+ let counter = 1;
89
+ while (existingNames.has(uniqueName)) {
90
+ uniqueName = `${name}_${counter}`;
91
+ counter++;
92
+ }
93
+ return uniqueName;
94
+ }