@azure-tools/typespec-ts 0.45.1 → 0.46.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 (69) 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 +10 -4
  4. package/dist/src/framework/hooks/binder.js.map +1 -1
  5. package/dist/src/framework/load-static-helpers.d.ts +2 -0
  6. package/dist/src/framework/load-static-helpers.d.ts.map +1 -1
  7. package/dist/src/framework/load-static-helpers.js +13 -5
  8. package/dist/src/framework/load-static-helpers.js.map +1 -1
  9. package/dist/src/framework/sample.js +0 -5
  10. package/dist/src/framework/sample.js.map +1 -1
  11. package/dist/src/index.d.ts.map +1 -1
  12. package/dist/src/index.js +9 -29
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/lib.d.ts +10 -1
  15. package/dist/src/lib.d.ts.map +1 -1
  16. package/dist/src/lib.js +6 -0
  17. package/dist/src/lib.js.map +1 -1
  18. package/dist/src/modular/buildRestorePoller.d.ts.map +1 -1
  19. package/dist/src/modular/buildRestorePoller.js +1 -2
  20. package/dist/src/modular/buildRestorePoller.js.map +1 -1
  21. package/dist/src/modular/emitModels.d.ts.map +1 -1
  22. package/dist/src/modular/emitModels.js +5 -0
  23. package/dist/src/modular/emitModels.js.map +1 -1
  24. package/dist/src/modular/emitSamples.d.ts.map +1 -1
  25. package/dist/src/modular/emitSamples.js +59 -9
  26. package/dist/src/modular/emitSamples.js.map +1 -1
  27. package/dist/src/modular/external-dependencies.d.ts.map +1 -1
  28. package/dist/src/modular/external-dependencies.js +10 -0
  29. package/dist/src/modular/external-dependencies.js.map +1 -1
  30. package/dist/src/modular/helpers/clientHelpers.js +6 -4
  31. package/dist/src/modular/helpers/clientHelpers.js.map +1 -1
  32. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  33. package/dist/src/modular/helpers/operationHelpers.js +21 -10
  34. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  35. package/dist/src/modular/serialization/buildSerializerFunction.js +1 -1
  36. package/dist/src/modular/serialization/buildSerializerFunction.js.map +1 -1
  37. package/dist/src/modular/static-helpers-metadata.d.ts +5 -0
  38. package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
  39. package/dist/src/modular/static-helpers-metadata.js +5 -0
  40. package/dist/src/modular/static-helpers-metadata.js.map +1 -1
  41. package/dist/src/modular/type-expressions/get-model-expression.d.ts.map +1 -1
  42. package/dist/src/modular/type-expressions/get-model-expression.js +2 -1
  43. package/dist/src/modular/type-expressions/get-model-expression.js.map +1 -1
  44. package/dist/src/utils/emitUtil.js +0 -1
  45. package/dist/src/utils/emitUtil.js.map +1 -1
  46. package/dist/src/utils/fileSystemUtils.d.ts +3 -0
  47. package/dist/src/utils/fileSystemUtils.d.ts.map +1 -0
  48. package/dist/src/utils/fileSystemUtils.js +40 -0
  49. package/dist/src/utils/fileSystemUtils.js.map +1 -0
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +4 -4
  52. package/src/framework/hooks/binder.ts +12 -4
  53. package/src/framework/load-static-helpers.ts +15 -3
  54. package/src/framework/sample.ts +0 -6
  55. package/src/index.ts +16 -32
  56. package/src/lib.ts +6 -0
  57. package/src/modular/buildRestorePoller.ts +1 -2
  58. package/src/modular/emitModels.ts +7 -0
  59. package/src/modular/emitSamples.ts +87 -6
  60. package/src/modular/external-dependencies.ts +10 -0
  61. package/src/modular/helpers/clientHelpers.ts +5 -5
  62. package/src/modular/helpers/operationHelpers.ts +24 -10
  63. package/src/modular/serialization/buildSerializerFunction.ts +1 -1
  64. package/src/modular/static-helpers-metadata.ts +5 -0
  65. package/src/modular/type-expressions/get-model-expression.ts +2 -1
  66. package/src/utils/emitUtil.ts +0 -1
  67. package/src/utils/fileSystemUtils.ts +47 -0
  68. package/static/static-helpers/serialization/get-binary-response-browser.mts +20 -0
  69. package/static/static-helpers/serialization/get-binary-response.ts +25 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-tools/typespec-ts",
3
- "version": "0.45.1",
3
+ "version": "0.46.0",
4
4
  "description": "An experimental TypeSpec emitter for TypeScript RLC",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -18,11 +18,11 @@
18
18
  "license": "MIT",
19
19
  "devDependencies": {
20
20
  "@azure-rest/core-client": "^2.3.1",
21
- "@typespec/http-specs": "0.1.0-alpha.28-dev.0",
21
+ "@typespec/http-specs": "0.1.0-alpha.28-dev.1",
22
22
  "@typespec/spector": "0.1.0-alpha.20-dev.0",
23
23
  "@typespec/spec-api": "0.1.0-alpha.10-dev.0",
24
24
  "@typespec/tspd": "0.73.0",
25
- "@azure-tools/azure-http-specs": "0.1.0-alpha.31-dev.0",
25
+ "@azure-tools/azure-http-specs": "0.1.0-alpha.32-dev.2",
26
26
  "@azure-tools/typespec-autorest": "^0.61.0",
27
27
  "@azure-tools/typespec-azure-core": "^0.61.0",
28
28
  "@azure-tools/typespec-azure-resource-manager": "^0.61.0",
@@ -77,7 +77,7 @@
77
77
  "@typespec/xml": "^0.75.0"
78
78
  },
79
79
  "dependencies": {
80
- "@azure-tools/rlc-common": "^0.45.1",
80
+ "@azure-tools/rlc-common": "^0.46.0",
81
81
  "fs-extra": "^11.1.0",
82
82
  "lodash": "^4.17.21",
83
83
  "prettier": "^3.3.3",
@@ -303,7 +303,7 @@ class BinderImp implements Binder {
303
303
  }
304
304
 
305
305
  private cleanUnreferencedHelpers(sourceRoot: string) {
306
- const usedHelperFiles = new Set<SourceFile>();
306
+ const usedHelperNames: string[] = [];
307
307
  for (const helper of this.staticHelpers.values()) {
308
308
  const sourceFile = helper[SourceFileSymbol];
309
309
  if (!sourceFile) {
@@ -315,16 +315,24 @@ class BinderImp implements Binder {
315
315
  const referencedHelper = this.references.get(refkey(helper));
316
316
 
317
317
  if (referencedHelper?.size) {
318
- usedHelperFiles.add(sourceFile);
318
+ usedHelperNames.push(sourceFile.getBaseNameWithoutExtension());
319
319
  }
320
320
  }
321
321
 
322
+ function isFileUnused(file: SourceFile) {
323
+ const name = file.getBaseNameWithoutExtension();
324
+
325
+ // If one of the used helpers' name is a prefix of this file, the file likely represents a platform-specific implementation of the helper
326
+ // so it should be marked as used even if the file has no direct references.
327
+ return !usedHelperNames.some((s) => name.startsWith(s));
328
+ }
329
+
322
330
  this.project
323
331
  //normalizae the final path to adapt to different systems
324
332
  .getSourceFiles(
325
- normalizePath(path.join(sourceRoot, "static-helpers/**/*.ts"))
333
+ normalizePath(path.join(sourceRoot, "static-helpers/**/*.*ts"))
326
334
  )
327
- .filter((helperFile) => !usedHelperFiles.has(helperFile))
335
+ .filter(isFileUnused)
328
336
  .forEach((helperFile) => helperFile.delete());
329
337
  }
330
338
  }
@@ -13,6 +13,8 @@ import { refkey } from "./refkey.js";
13
13
  import { resolveProjectRoot } from "../utils/resolve-project-root.js";
14
14
  import { isAzurePackage } from "@azure-tools/rlc-common";
15
15
  import { ModularEmitterOptions } from "../modular/interfaces.js";
16
+ import { NoTarget, Program } from "@typespec/compiler";
17
+ import { reportDiagnostic } from "../lib.js";
16
18
  export const SourceFileSymbol = Symbol("SourceFile");
17
19
  export interface StaticHelperMetadata {
18
20
  name: string;
@@ -41,6 +43,7 @@ export interface LoadStaticHelpersOptions
41
43
  extends Partial<ModularEmitterOptions> {
42
44
  helpersAssetDirectory?: string;
43
45
  sourcesDir?: string;
46
+ program?: Program;
44
47
  }
45
48
 
46
49
  export async function loadStaticHelpers(
@@ -55,7 +58,8 @@ export async function loadStaticHelpers(
55
58
  DEFAULT_STATIC_HELPERS_PATH
56
59
  );
57
60
  const files = await traverseDirectory(
58
- options.helpersAssetDirectory ?? defaultStaticHelpersPath
61
+ options.helpersAssetDirectory ?? defaultStaticHelpersPath,
62
+ options.program
59
63
  );
60
64
 
61
65
  for (const file of files) {
@@ -161,6 +165,7 @@ function getDeclarationByMetadata(
161
165
  const _targetStaticHelpersBaseDir = "static-helpers";
162
166
  async function traverseDirectory(
163
167
  directory: string,
168
+ program?: Program,
164
169
  result: { source: string; target: string }[] = [],
165
170
  relativePath: string = ""
166
171
  ): Promise<{ source: string; target: string }[]> {
@@ -175,13 +180,14 @@ async function traverseDirectory(
175
180
  if (fileStat.isDirectory()) {
176
181
  await traverseDirectory(
177
182
  filePath,
183
+ program,
178
184
  result,
179
185
  path.join(relativePath, file)
180
186
  );
181
187
  } else if (
182
188
  fileStat.isFile() &&
183
189
  !file.endsWith(".d.ts") &&
184
- file.endsWith(".ts")
190
+ /.*\..?ts$/.test(file)
185
191
  ) {
186
192
  const target = path.join(
187
193
  _targetStaticHelpersBaseDir,
@@ -195,7 +201,13 @@ async function traverseDirectory(
195
201
 
196
202
  return result;
197
203
  } catch (error) {
198
- console.error(`Error traversing directory ${directory}:`, error);
204
+ if (program) {
205
+ reportDiagnostic(program, {
206
+ code: "directory-traversal-error",
207
+ format: { directory, error: String(error) },
208
+ target: NoTarget
209
+ });
210
+ }
199
211
  throw error;
200
212
  }
201
213
  }
@@ -69,12 +69,6 @@ sourceFile2.addStatements(`let obj: ${modelReference} = { id: 1 };`);
69
69
  // Apply imports to ensure correct references
70
70
  binder.resolveAllReferences("/modularPackageFolder/src");
71
71
 
72
- // Output the generated files
73
- console.log("// test.ts");
74
- console.log(sourceFile.getFullText());
75
- console.log("// test2.ts");
76
- console.log(sourceFile2.getFullText());
77
-
78
72
  // Output
79
73
  // test.ts
80
74
  // interface MyInterface {
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  AzurePollingDependencies,
9
9
  DefaultCoreDependencies
10
10
  } from "./modular/external-dependencies.js";
11
+ import { clearDirectory } from "./utils/fileSystemUtils.js";
11
12
  import { EmitContext, Program } from "@typespec/compiler";
12
13
  import { GenerationDirDetail, SdkContext } from "./utils/interfaces.js";
13
14
  import {
@@ -43,7 +44,9 @@ import {
43
44
  buildTopLevelIndex,
44
45
  buildTsConfig,
45
46
  buildTsSnippetsConfig,
46
- buildTsTestBrowserConfig,
47
+ buildTestBrowserTsConfig,
48
+ buildTestNodeTsConfig,
49
+ buildTestMainTsConfig,
47
50
  buildVitestConfig,
48
51
  getClientName,
49
52
  hasUnexpectedHelper,
@@ -52,8 +55,7 @@ import {
52
55
  buildSampleEnvFile,
53
56
  buildSnippets,
54
57
  buildTsSrcConfig,
55
- buildTsSampleConfig,
56
- buildTsTestConfig
58
+ buildTsSampleConfig
57
59
  } from "@azure-tools/rlc-common";
58
60
  import {
59
61
  buildRootIndex,
@@ -98,7 +100,6 @@ import { generateCrossLanguageDefinitionFile } from "./utils/crossLanguageDef.js
98
100
  export * from "./lib.js";
99
101
 
100
102
  export async function $onEmit(context: EmitContext) {
101
- console.time("onEmit");
102
103
  if (context.program.compilerOptions.noEmit || context.program.hasError()) {
103
104
  return;
104
105
  }
@@ -106,9 +107,7 @@ export async function $onEmit(context: EmitContext) {
106
107
  const outputProject = new Project();
107
108
  const program: Program = context.program;
108
109
  const emitterOptions: EmitterOptions = context.options;
109
- console.time("onEmit: create context");
110
110
  const dpgContext = await createContextWithDefaultOptions(context);
111
- console.timeEnd("onEmit: create context");
112
111
  // Enrich the dpg context with path detail and common options
113
112
  await enrichDpgContext();
114
113
  const rlcOptions = dpgContext.rlcOptions ?? {};
@@ -125,7 +124,6 @@ export async function $onEmit(context: EmitContext) {
125
124
  compilerContext: context,
126
125
  tcgcContext: dpgContext
127
126
  });
128
- console.time("onEmit: load static helpers");
129
127
  const staticHelpers = await loadStaticHelpers(
130
128
  outputProject,
131
129
  {
@@ -138,10 +136,10 @@ export async function $onEmit(context: EmitContext) {
138
136
  },
139
137
  {
140
138
  sourcesDir: dpgContext.generationPathDetail?.modularSourcesDir,
141
- options: rlcOptions
139
+ options: rlcOptions,
140
+ program
142
141
  }
143
142
  );
144
- console.timeEnd("onEmit: load static helpers");
145
143
  const extraDependencies = isAzurePackage({ options: rlcOptions })
146
144
  ? {
147
145
  ...AzurePollingDependencies,
@@ -149,7 +147,6 @@ export async function $onEmit(context: EmitContext) {
149
147
  ...AzureIdentityDependencies
150
148
  }
151
149
  : { ...DefaultCoreDependencies };
152
- console.time("onEmit: provide binder");
153
150
  const binder = provideBinder(outputProject, {
154
151
  staticHelpers,
155
152
  dependencies: {
@@ -157,7 +154,6 @@ export async function $onEmit(context: EmitContext) {
157
154
  }
158
155
  });
159
156
  provideSdkTypes(dpgContext);
160
- console.timeEnd("onEmit: provide binder");
161
157
 
162
158
  const rlcCodeModels: RLCModel[] = [];
163
159
  let modularEmitterOptions: ModularEmitterOptions;
@@ -165,9 +161,7 @@ export async function $onEmit(context: EmitContext) {
165
161
  await clearSrcFolder();
166
162
  // 2. Generate RLC code model
167
163
  // TODO: skip this step in modular once modular generator is sufficiently decoupled
168
- console.time("onEmit: build RLC code models");
169
164
  await buildRLCCodeModels();
170
- console.timeEnd("onEmit: build RLC code models");
171
165
 
172
166
  // 4. Generate sources
173
167
  if (emitterOptions["is-modular-library"]) {
@@ -195,7 +189,12 @@ export async function $onEmit(context: EmitContext) {
195
189
  emitterOptions["generate-sample"] = options.generateSample;
196
190
  // clear output folder if needed
197
191
  if (options.clearOutputFolder) {
198
- await fsextra.emptyDir(context.emitterOutputDir);
192
+ // Clear output directory while preserving TempTypeSpecFiles
193
+ await clearDirectory(
194
+ context.emitterOutputDir,
195
+ ["TempTypeSpecFiles"],
196
+ program
197
+ );
199
198
  }
200
199
  const hasTestFolder = await fsextra.pathExists(
201
200
  join(dpgContext.generationPathDetail?.metadataDir ?? "", "test")
@@ -274,7 +273,6 @@ export async function $onEmit(context: EmitContext) {
274
273
  }
275
274
 
276
275
  async function generateModularSources() {
277
- console.time("onEmit: generate modular sources");
278
276
  const modularSourcesRoot =
279
277
  dpgContext.generationPathDetail?.modularSourcesDir ?? "src";
280
278
  const project = useContext("outputProject");
@@ -297,13 +295,10 @@ export async function $onEmit(context: EmitContext) {
297
295
  );
298
296
 
299
297
  const isMultiClients = dpgContext.sdkPackage.clients.length > 1;
300
- console.time("onEmit: emit models");
301
298
  emitTypes(dpgContext, { sourceRoot: modularSourcesRoot });
302
- console.timeEnd("onEmit: emit models");
303
299
  buildSubpathIndexFile(modularEmitterOptions, "models", undefined, {
304
300
  recursive: true
305
301
  });
306
- console.time("onEmit: emit source files");
307
302
  const clientMap = getClientHierarchyMap(dpgContext);
308
303
  if (clientMap.length === 0) {
309
304
  // If no clients, we still need to build the root index file
@@ -343,12 +338,9 @@ export async function $onEmit(context: EmitContext) {
343
338
  subClient
344
339
  );
345
340
  }
346
- console.timeEnd("onEmit: emit source files");
347
341
  // Enable modular sample generation when explicitly set to true or MPG
348
342
  if (emitterOptions["generate-sample"] === true) {
349
- console.time("onEmit: emit samples");
350
343
  const samples = emitSamples(dpgContext);
351
- console.timeEnd("onEmit: emit samples");
352
344
  // Refine the rlc sample generation logic
353
345
  // TODO: remember to remove this out when RLC is splitted from Modular
354
346
  if (samples.length > 0) {
@@ -356,14 +348,10 @@ export async function $onEmit(context: EmitContext) {
356
348
  }
357
349
  }
358
350
 
359
- console.time("onEmit: resolve references");
360
351
  binder.resolveAllReferences(modularSourcesRoot);
361
352
  if (program.compilerOptions.noEmit || program.hasError()) {
362
353
  return;
363
354
  }
364
- console.timeEnd("onEmit: resolve references");
365
-
366
- console.time("onEmit: generate files");
367
355
 
368
356
  for (const file of project.getSourceFiles()) {
369
357
  await emitContentByBuilder(
@@ -372,8 +360,6 @@ export async function $onEmit(context: EmitContext) {
372
360
  modularEmitterOptions as any
373
361
  );
374
362
  }
375
- console.timeEnd("onEmit: generate files");
376
- console.timeEnd("onEmit: generate modular sources");
377
363
  }
378
364
 
379
365
  interface Metadata {
@@ -456,7 +442,9 @@ export async function $onEmit(context: EmitContext) {
456
442
  commonBuilders.push((model) => buildVitestConfig(model, "node"));
457
443
  commonBuilders.push((model) => buildVitestConfig(model, "esm"));
458
444
  commonBuilders.push((model) => buildVitestConfig(model, "browser"));
459
- commonBuilders.push((model) => buildTsTestBrowserConfig(model));
445
+ commonBuilders.push((model) => buildTestBrowserTsConfig(model));
446
+ commonBuilders.push((model) => buildTestNodeTsConfig(model));
447
+ commonBuilders.push((model) => buildTestMainTsConfig(model));
460
448
  }
461
449
  if (isAzureFlavor) {
462
450
  commonBuilders.push(buildEsLintConfig);
@@ -488,9 +476,6 @@ export async function $onEmit(context: EmitContext) {
488
476
  if (option.generateSample) {
489
477
  commonBuilders.push(buildTsSampleConfig);
490
478
  }
491
- if (option.generateTest) {
492
- commonBuilders.push(buildTsTestConfig);
493
- }
494
479
  }
495
480
 
496
481
  // TODO: need support snippets generation for multi-client cases. https://github.com/Azure/autorest.typescript/issues/3048
@@ -558,7 +543,6 @@ export async function $onEmit(context: EmitContext) {
558
543
  .map((subClient) => getClientContextPath(subClient, options))
559
544
  .map((path) => path.substring(path.indexOf("src")));
560
545
  }
561
- console.timeEnd("onEmit");
562
546
  }
563
547
 
564
548
  export async function createContextWithDefaultOptions(
package/src/lib.ts CHANGED
@@ -582,6 +582,12 @@ const libDef = {
582
582
  messages: {
583
583
  default: paramMessage`Failed to format file: ${"filePath"}. Error: ${"error"}.`
584
584
  }
585
+ },
586
+ "directory-traversal-error": {
587
+ severity: "error",
588
+ messages: {
589
+ default: paramMessage`Error traversing directory ${"directory"}: ${"error"}`
590
+ }
585
591
  }
586
592
  },
587
593
  emitter: {
@@ -126,8 +126,7 @@ export function buildRestorePoller(
126
126
  }
127
127
 
128
128
  interface DeserializationHelper {
129
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
130
- deserializer: Function;
129
+ deserializer: (result: PathUncheckedResponse) => Promise<any>;
131
130
  expectedStatuses: string[];
132
131
  }
133
132
 
@@ -112,6 +112,13 @@ export function emitTypes(
112
112
  sourceFile = outputProject.getSourceFile(filepath);
113
113
  if (!sourceFile) {
114
114
  sourceFile = outputProject.createSourceFile(filepath);
115
+ sourceFile.addStatements(
116
+ `/**
117
+ * This file contains only generated model types and (de)serializers.
118
+ * Disable this rule for deserializer functions which require 'any' for raw JSON input.
119
+ */
120
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */`
121
+ );
115
122
  }
116
123
  emitType(context, type, sourceFile);
117
124
  }
@@ -37,6 +37,8 @@ import {
37
37
  ServiceOperation
38
38
  } from "../utils/operationUtil.js";
39
39
  import { getSubscriptionId } from "../transform/transfromRLCOptions.js";
40
+ import { getClientParametersDeclaration } from "./helpers/clientHelpers.js";
41
+ import { getOperationFunction } from "./helpers/operationHelpers.js";
40
42
 
41
43
  /**
42
44
  * Interfaces for samples generations
@@ -53,6 +55,7 @@ interface EmitSampleOptions {
53
55
  generatedFiles: SourceFile[];
54
56
  classicalMethodPrefix?: string;
55
57
  subFolder?: string;
58
+ hierarchies?: string[]; // Add hierarchies to track operation path
56
59
  }
57
60
  /**
58
61
  * Helpers to emit samples
@@ -80,8 +83,8 @@ function emitClientSamples(
80
83
  ) {
81
84
  const methodMap = getMethodHierarchiesMap(dpgContext, client);
82
85
  for (const [prefixKey, operations] of methodMap) {
83
- const prefix = prefixKey
84
- .split("/")
86
+ const hierarchies = prefixKey ? prefixKey.split("/") : [];
87
+ const prefix = hierarchies
85
88
  .map((name) => {
86
89
  return normalizeName(name, NameType.Property);
87
90
  })
@@ -89,7 +92,8 @@ function emitClientSamples(
89
92
  for (const op of operations) {
90
93
  emitMethodSamples(dpgContext, op, {
91
94
  ...options,
92
- classicalMethodPrefix: prefix
95
+ classicalMethodPrefix: prefix,
96
+ hierarchies: hierarchies
93
97
  });
94
98
  }
95
99
  }
@@ -173,10 +177,35 @@ function emitMethodSamples(
173
177
  );
174
178
 
175
179
  // prepare operation-level parameters
180
+ // Get the actual function signature parameter order
181
+ const operationFunction = getOperationFunction(
182
+ dpgContext,
183
+ [options.hierarchies ?? [], method],
184
+ "Client"
185
+ );
186
+
187
+ // Extract parameter names from the function signature (excluding context and options)
188
+ const signatureParamNames =
189
+ operationFunction.parameters
190
+ ?.filter(
191
+ (p) =>
192
+ p.name !== "context" &&
193
+ !p.type?.toString().includes("OptionalParams")
194
+ )
195
+ .map((p) => p.name) ?? [];
196
+
176
197
  const methodParamValues = parameters.filter((p) => !p.onClient);
177
- const methodParams = methodParamValues
178
- .filter((p) => !p.isOptional)
179
- .map((p) => `${p.value}`);
198
+
199
+ // Create a map for quick lookup of parameter values by name
200
+ const paramValueMap = new Map(methodParamValues.map((p) => [p.name, p]));
201
+
202
+ // Reorder methodParamValues according to the signature order
203
+ const orderedRequiredParams = signatureParamNames
204
+ .map((name) => paramValueMap.get(name))
205
+ .filter((p): p is ExampleValue => p !== undefined && !p.isOptional);
206
+
207
+ const methodParams = orderedRequiredParams.map((p) => `${p.value}`);
208
+
180
209
  const optionalParams = methodParamValues
181
210
  .filter((p) => p.isOptional)
182
211
  .map((param) => `${param.name}: ${param.value}`);
@@ -265,6 +294,31 @@ function prepareExampleParameters(
265
294
  // TODO: blocked by TCGC issue: https://github.com/Azure/typespec-azure/issues/1419
266
295
  // refine this to support generic client-level parameters once resolved
267
296
  const result: ExampleValue[] = [];
297
+ const clientParams = getClientParametersDeclaration(
298
+ topLevelClient,
299
+ dpgContext,
300
+ {
301
+ onClientOnly: true
302
+ }
303
+ );
304
+
305
+ for (const param of clientParams) {
306
+ if (param.name === "options" || param.name === "credential") {
307
+ continue;
308
+ }
309
+
310
+ const exampleValue: ExampleValue = {
311
+ name: param.name === "endpointParam" ? "endpoint" : param.name,
312
+ value: getEnvironmentVariableName(
313
+ param.name,
314
+ getClassicalClientName(topLevelClient)
315
+ ),
316
+ isOptional: Boolean(param.hasQuestionToken),
317
+ onClient: true
318
+ };
319
+
320
+ result.push(exampleValue);
321
+ }
268
322
  const credentialExampleValue = getCredentialExampleValue(
269
323
  dpgContext,
270
324
  topLevelClient.clientInitialization
@@ -513,3 +567,30 @@ function escapeSpecialCharToSpace(str: string) {
513
567
  }
514
568
  return str.replace(/_|,|\.|\(|\)|'s |\[|\]/g, " ").replace(/\//g, " Or ");
515
569
  }
570
+
571
+ function getEnvironmentVariableName(
572
+ paramName: string,
573
+ clientName?: string
574
+ ): string {
575
+ // Remove "Param" suffix if present
576
+ const cleanName = paramName.replace(/Param$/, "");
577
+
578
+ // Remove "Client" suffix from client name if present and convert to UPPER_SNAKE_CASE
579
+ let prefix = "";
580
+ if (clientName) {
581
+ const cleanClientName = clientName.replace(/Client$/, "");
582
+ prefix =
583
+ cleanClientName
584
+ .replace(/([A-Z])/g, "_$1")
585
+ .toUpperCase()
586
+ .replace(/^_/, "") + "_";
587
+ }
588
+
589
+ // Convert camelCase to UPPER_SNAKE_CASE
590
+ const envVarName = cleanName
591
+ .replace(/([A-Z])/g, "_$1")
592
+ .toUpperCase()
593
+ .replace(/^_/, "");
594
+
595
+ return `process.env.${prefix}${envVarName} || ""`;
596
+ }
@@ -85,6 +85,11 @@ export const DefaultCoreDependencies: CoreDependencies = {
85
85
  kind: "externalDependency",
86
86
  name: "ErrorModel",
87
87
  module: "@typespec/ts-http-runtime"
88
+ },
89
+ AzureCoreErrorResponse: {
90
+ kind: "externalDependency",
91
+ name: "ErrorResponse",
92
+ module: "@azure-rest/core-client"
88
93
  }
89
94
  } as const;
90
95
 
@@ -196,6 +201,11 @@ export const AzureCoreDependencies: CoreDependencies = {
196
201
  kind: "externalDependency",
197
202
  name: "ErrorModel",
198
203
  module: "@azure-rest/core-client"
204
+ },
205
+ AzureCoreErrorResponse: {
206
+ kind: "externalDependency",
207
+ name: "ErrorResponse",
208
+ module: "@azure-rest/core-client"
199
209
  }
200
210
  };
201
211
 
@@ -73,14 +73,15 @@ export function getClientParameters(
73
73
  const hasDefaultValue = (p: SdkParameter) =>
74
74
  p.clientDefaultValue || p.__raw?.defaultValue || p.type.kind === "constant";
75
75
  const isRequired = (p: SdkParameter) =>
76
- !p.optional &&
77
- ((!hasDefaultValue(p) &&
76
+ // Special case: when apiVersionAsRequired is true, apiVersion should always be considered required
77
+ (options.apiVersionAsRequired && p.isApiVersionParam) ||
78
+ (!p.optional &&
79
+ !hasDefaultValue(p) &&
78
80
  !(
79
81
  p.type.kind === "endpoint" &&
80
82
  p.type.templateArguments[0] &&
81
83
  hasDefaultValue(p.type.templateArguments[0])
82
- )) ||
83
- (options.apiVersionAsRequired && p.isApiVersionParam));
84
+ ));
84
85
  const isOptional = (p: SdkParameter) => p.optional || hasDefaultValue(p);
85
86
  const skipCredentials = (p: SdkParameter) => p.kind !== "credential";
86
87
  const skipMethodParam = (p: SdkParameter) => p.kind !== "method";
@@ -98,7 +99,6 @@ export function getClientParameters(
98
99
  const params = clientParams.filter((p) =>
99
100
  filters.every((filter) => !filter || filter(p))
100
101
  );
101
-
102
102
  return params;
103
103
  }
104
104
 
@@ -8,6 +8,7 @@ import { NoTarget, Program } from "@typespec/compiler";
8
8
  import {
9
9
  PagingHelpers,
10
10
  PollingHelpers,
11
+ SerializationHelpers,
11
12
  UrlTemplateHelpers
12
13
  } from "../static-helpers-metadata.js";
13
14
  import {
@@ -472,11 +473,18 @@ export function getOperationFunction(
472
473
  };
473
474
 
474
475
  const statements: string[] = [];
475
- statements.push(
476
- `const result = await _${name}Send(${parameters
477
- .map((p) => p.name)
478
- .join(", ")});`
479
- );
476
+
477
+ const parameterList = parameters.map((p) => p.name).join(", ");
478
+ // Special case for binary-only bodies: use helper to call streaming methods so that Core doesn't poison the response body by
479
+ // doing a UTF-8 decode on the raw bytes.
480
+ if (response?.type?.kind === "bytes" && response.type.encode === "bytes") {
481
+ statements.push(`const streamableMethod = _${name}Send(${parameterList});`);
482
+ statements.push(
483
+ `const result = await ${resolveReference(SerializationHelpers.getBinaryResponse)}(streamableMethod);`
484
+ );
485
+ } else {
486
+ statements.push(`const result = await _${name}Send(${parameterList});`);
487
+ }
480
488
  statements.push(`return _${name}Deserialize(result);`);
481
489
 
482
490
  return {
@@ -703,10 +711,16 @@ function getHeaderAndBodyParameters(
703
711
  ) {
704
712
  continue;
705
713
  }
706
- parametersImplementation[param.kind].push({
707
- paramMap: getParameterMap(dpgContext, param, optionalParamName),
708
- param
709
- });
714
+ // Check if this parameter still exists in the corresponding method params (after override)
715
+ if (
716
+ param.correspondingMethodParams &&
717
+ param.correspondingMethodParams.length > 0
718
+ ) {
719
+ parametersImplementation[param.kind].push({
720
+ paramMap: getParameterMap(dpgContext, param, optionalParamName),
721
+ param
722
+ });
723
+ }
710
724
  }
711
725
  }
712
726
 
@@ -1571,7 +1585,7 @@ export function getExpectedStatuses(operation: ServiceOperation): string {
1571
1585
  let statusCodes = operation.operation.responses.map((x) => x.statusCodes);
1572
1586
  // LROs may call the same path but with GET to get the operation status.
1573
1587
  if (isLroOnlyOperation(operation) && operation.operation.verb !== "get") {
1574
- statusCodes = Array.from(new Set([...statusCodes, 200, 202]));
1588
+ statusCodes = Array.from(new Set([...statusCodes, 200, 201, 202]));
1575
1589
  }
1576
1590
 
1577
1591
  return `[${statusCodes.map((x) => `"${x}"`).join(", ")}]`;
@@ -473,7 +473,7 @@ function getAdditionalPropertiesStatement(
473
473
  false,
474
474
  true
475
475
  );
476
- const params = [`item.${getAdditionalPropertiesName(context, type)}`];
476
+ const params = [`item.${getAdditionalPropertiesName(context, type)} ?? {}`];
477
477
  if (typeof deserializerFunction === "string") {
478
478
  params.push("undefined");
479
479
  params.push(deserializerFunction);
@@ -28,6 +28,11 @@ export const SerializationHelpers = {
28
28
  kind: "function",
29
29
  name: "serializeRecord",
30
30
  location: "serialization/serialize-record.ts"
31
+ },
32
+ getBinaryResponse: {
33
+ kind: "function",
34
+ name: "getBinaryResponse",
35
+ location: "serialization/get-binary-response.ts"
31
36
  }
32
37
  } as const;
33
38
 
@@ -48,7 +48,8 @@ export function getModelExpression(
48
48
  }
49
49
 
50
50
  const externalModels: Record<string, string> = {
51
- "Azure.Core.Foundations.Error": "ErrorModel"
51
+ "Azure.Core.Foundations.Error": "ErrorModel",
52
+ "Azure.Core.Foundations.ErrorResponse": "AzureCoreErrorResponse"
52
53
  };
53
54
 
54
55
  export function getExternalModel(type: SdkModelType) {
@@ -91,7 +91,6 @@ async function emitFile(
91
91
  target: NoTarget
92
92
  });
93
93
  // Continue with unformatted content rather than crashing
94
- console.error(`Failed to format file: ${filePath}`, e);
95
94
  }
96
95
  }
97
96
  await host.mkdirp(dirname(filePath));