@azure-tools/typespec-ts 0.50.2 → 0.50.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/src/framework/hooks/binder.d.ts +1 -1
  3. package/dist/src/framework/hooks/binder.d.ts.map +1 -1
  4. package/dist/src/framework/hooks/binder.js +11 -3
  5. package/dist/src/framework/hooks/binder.js.map +1 -1
  6. package/dist/src/framework/load-static-helpers.d.ts +3 -0
  7. package/dist/src/framework/load-static-helpers.d.ts.map +1 -1
  8. package/dist/src/framework/load-static-helpers.js +49 -38
  9. package/dist/src/framework/load-static-helpers.js.map +1 -1
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/index.js +19 -10
  12. package/dist/src/index.js.map +1 -1
  13. package/dist/src/lib.d.ts +7 -0
  14. package/dist/src/lib.d.ts.map +1 -1
  15. package/dist/src/lib.js +5 -0
  16. package/dist/src/lib.js.map +1 -1
  17. package/dist/src/modular/buildOperations.d.ts.map +1 -1
  18. package/dist/src/modular/buildOperations.js +1 -1
  19. package/dist/src/modular/buildOperations.js.map +1 -1
  20. package/dist/src/modular/emitModels.d.ts +8 -0
  21. package/dist/src/modular/emitModels.d.ts.map +1 -1
  22. package/dist/src/modular/emitModels.js +32 -2
  23. package/dist/src/modular/emitModels.js.map +1 -1
  24. package/dist/src/modular/emitSamples.js +9 -4
  25. package/dist/src/modular/emitSamples.js.map +1 -1
  26. package/dist/src/modular/emitTests.d.ts +7 -0
  27. package/dist/src/modular/emitTests.d.ts.map +1 -0
  28. package/dist/src/modular/emitTests.js +160 -0
  29. package/dist/src/modular/emitTests.js.map +1 -0
  30. package/dist/src/modular/external-dependencies.d.ts +42 -0
  31. package/dist/src/modular/external-dependencies.d.ts.map +1 -1
  32. package/dist/src/modular/external-dependencies.js +42 -0
  33. package/dist/src/modular/external-dependencies.js.map +1 -1
  34. package/dist/src/modular/helpers/exampleValueHelpers.d.ts +83 -0
  35. package/dist/src/modular/helpers/exampleValueHelpers.d.ts.map +1 -0
  36. package/dist/src/modular/helpers/exampleValueHelpers.js +631 -0
  37. package/dist/src/modular/helpers/exampleValueHelpers.js.map +1 -0
  38. package/dist/src/modular/helpers/operationHelpers.d.ts +22 -2
  39. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  40. package/dist/src/modular/helpers/operationHelpers.js +178 -9
  41. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  42. package/dist/src/modular/static-helpers-metadata.d.ts +12 -0
  43. package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
  44. package/dist/src/modular/static-helpers-metadata.js +12 -0
  45. package/dist/src/modular/static-helpers-metadata.js.map +1 -1
  46. package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
  47. package/dist/src/transform/transfromRLCOptions.js +10 -0
  48. package/dist/src/transform/transfromRLCOptions.js.map +1 -1
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +2 -2
  51. package/src/framework/hooks/binder.ts +15 -5
  52. package/src/framework/load-static-helpers.ts +79 -51
  53. package/src/index.ts +22 -7
  54. package/src/lib.ts +13 -0
  55. package/src/modular/buildOperations.ts +2 -1
  56. package/src/modular/emitModels.ts +47 -2
  57. package/src/modular/emitSamples.ts +7 -1
  58. package/src/modular/emitTests.ts +227 -0
  59. package/src/modular/external-dependencies.ts +43 -0
  60. package/src/modular/helpers/exampleValueHelpers.ts +940 -0
  61. package/src/modular/helpers/operationHelpers.ts +229 -17
  62. package/src/modular/static-helpers-metadata.ts +13 -0
  63. package/src/transform/transfromRLCOptions.ts +14 -0
  64. package/static/static-helpers/serialization/get-binary-response-body-browser.mts +22 -0
  65. package/static/static-helpers/serialization/get-binary-response-body.ts +24 -0
  66. package/static/test-helpers/recordedClient.ts +30 -0
@@ -2,7 +2,8 @@ import {
2
2
  FunctionDeclarationStructure,
3
3
  OptionalKind,
4
4
  ParameterDeclarationStructure,
5
- StructureKind
5
+ StructureKind,
6
+ TypeAliasDeclarationStructure
6
7
  } from "ts-morph";
7
8
  import { NoTarget, Program } from "@typespec/compiler";
8
9
  import {
@@ -179,17 +180,26 @@ export function getSendPrivateFunction(
179
180
 
180
181
  export function getDeserializePrivateFunction(
181
182
  context: SdkContext,
182
- operation: ServiceOperation
183
+ method: [string[], ServiceOperation]
183
184
  ): OptionalKind<FunctionDeclarationStructure> {
185
+ const operation = method[1];
184
186
  const { name } = getOperationName(operation);
185
187
  const dependencies = useDependencies();
186
188
  const PathUncheckedResponseReference = resolveReference(
187
189
  dependencies.PathUncheckedResponse
188
190
  );
191
+
192
+ // Check if we need to wrap the non-model return type
193
+ const { shouldWrap, isBinary } = checkWrapNonModelReturn(context, operation);
194
+ // For binary wrap, the deserializer receives a StreamableMethod directly
195
+ const resultParamType =
196
+ shouldWrap && isBinary
197
+ ? resolveReference(dependencies.StreamableMethod)
198
+ : PathUncheckedResponseReference;
189
199
  const parameters: OptionalKind<ParameterDeclarationStructure>[] = [
190
200
  {
191
201
  name: "result",
192
- type: PathUncheckedResponseReference
202
+ type: resultParamType
193
203
  }
194
204
  ];
195
205
  const isLroOnly = isLroOnlyOperation(operation);
@@ -201,6 +211,7 @@ export function getDeserializePrivateFunction(
201
211
  const response = operation.response;
202
212
  const restResponse = operation.operation.responses[0];
203
213
  let returnType;
214
+
204
215
  if (isLroOnly || isLroAndPaging) {
205
216
  returnType = buildLroReturnType(context, operation);
206
217
  } else if (isPagingOnly && restResponse?.type) {
@@ -210,6 +221,12 @@ export function getDeserializePrivateFunction(
210
221
  name: (restResponse as any).name ?? "",
211
222
  type: getTypeExpression(context, restResponse.type)
212
223
  };
224
+ } else if (shouldWrap) {
225
+ // Use the wrapper response type name (resolved via binder for cross-file imports)
226
+ returnType = {
227
+ name: getOperationResponseTypeName(method),
228
+ type: resolveReference(refkey(operation, "response"))
229
+ };
213
230
  } else if (response.type) {
214
231
  returnType = {
215
232
  name: (response as any).name ?? "",
@@ -230,15 +247,17 @@ export function getDeserializePrivateFunction(
230
247
  const createRestErrorReference = resolveReference(
231
248
  dependencies.createRestError
232
249
  );
233
-
234
- statements.push(
235
- `const expectedStatuses = ${getExpectedStatuses(operation)};`
236
- );
237
- statements.push(
238
- `if(!expectedStatuses.includes(result.status)){`,
239
- `${getExceptionThrowStatement(context, operation)}`,
240
- "}"
241
- );
250
+ // For binary wrap, parameter is StreamableMethod - skip status check
251
+ if (!(shouldWrap && isBinary)) {
252
+ statements.push(
253
+ `const expectedStatuses = ${getExpectedStatuses(operation)};`
254
+ );
255
+ statements.push(
256
+ `if(!expectedStatuses.includes(result.status)){`,
257
+ `${getExceptionThrowStatement(context, operation)}`,
258
+ "}"
259
+ );
260
+ }
242
261
  const deserializedType =
243
262
  isLroOnly || isLroAndPaging
244
263
  ? operation?.lroMetadata?.finalResponse?.result
@@ -374,6 +393,40 @@ export function getDeserializePrivateFunction(
374
393
  skipDiscriminatedUnionSuffix: false
375
394
  }
376
395
  );
396
+ // Handle wrap-non-model-return for non-LRO, non-paging operations
397
+ if (shouldWrap) {
398
+ if (isBinary) {
399
+ const getBinaryResponseBodyReference = resolveReference(
400
+ SerializationHelpers.getBinaryResponseBody
401
+ );
402
+ const deserializeError = getExceptionDetails(
403
+ context,
404
+ operation
405
+ ).defaultDeserializer;
406
+ const deserializeErrorStr = deserializeError
407
+ ? `, ${deserializeError}`
408
+ : "";
409
+ statements.push(
410
+ `return ${getBinaryResponseBodyReference}(result, ${getExpectedStatuses(operation)}${deserializeErrorStr});
411
+ `
412
+ );
413
+ } else {
414
+ // Non-model response: wrap with body property
415
+ // Generate the appropriate deserialization for the body value
416
+ const bodyValue = deserializeResponseValue(
417
+ context,
418
+ deserializedType,
419
+ "result.body",
420
+ true,
421
+ getEncodeForType(deserializedType)
422
+ );
423
+ statements.push(`return { body: ${bodyValue} };`);
424
+ }
425
+ return {
426
+ ...functionStatement,
427
+ statements
428
+ };
429
+ }
377
430
  if (deserializeFunctionName) {
378
431
  statements.push(
379
432
  `return ${deserializeFunctionName}(${deserializedRoot})${multipartCastSuffix}`
@@ -947,8 +1000,18 @@ export function getOperationFunction(
947
1000
  bodyType = getTypeExpression(context, response.type!);
948
1001
  }
949
1002
 
1003
+ // Check if we need to wrap the non-model return type
1004
+ const { shouldWrap: wrapReturn, isBinary: wrapReturnIsBinary } =
1005
+ checkWrapNonModelReturn(context, operation);
1006
+
950
1007
  let returnType = { name: "", type: "void" };
951
- if (response.type) {
1008
+ if (wrapReturn) {
1009
+ // Use the wrapper response type name (resolved via binder for cross-file imports)
1010
+ returnType = {
1011
+ name: getOperationResponseTypeName(method),
1012
+ type: resolveReference(refkey(operation, "response"))
1013
+ };
1014
+ } else if (response.type) {
952
1015
  const type = response.type;
953
1016
 
954
1017
  // If feature flag enabled, we'll append the response headers to the operation response type.
@@ -1028,6 +1091,22 @@ export function getOperationFunction(
1028
1091
  const resultVarName = generateLocallyUniqueName("result", paramNames);
1029
1092
 
1030
1093
  const parameterList = parameters.map((p) => p.name).join(", ");
1094
+ // For binary wrap, pass the StreamableMethod directly to the deserializer.
1095
+ // The deserializer uses asBrowserStream()/asNodeStream() to build the wrapper.
1096
+ if (wrapReturn && wrapReturnIsBinary) {
1097
+ const streamableMethodVarName = generateLocallyUniqueName(
1098
+ "streamableMethod",
1099
+ paramNames
1100
+ );
1101
+ statements.push(
1102
+ `const ${streamableMethodVarName} = _${name}Send(${parameterList});`
1103
+ );
1104
+ statements.push(`return _${name}Deserialize(${streamableMethodVarName});`);
1105
+ return {
1106
+ ...functionStatement,
1107
+ statements
1108
+ } as FunctionDeclarationStructure & { propertyName?: string };
1109
+ }
1031
1110
 
1032
1111
  // When storage-compat is enabled, set up the onResponse interceptor before sending
1033
1112
  const storageCompatVarName = generateLocallyUniqueName(
@@ -1542,7 +1621,8 @@ function buildHeaderParameter(
1542
1621
  optionalParamName: string = "options"
1543
1622
  ): string {
1544
1623
  const paramName = param.name;
1545
- if (!param.optional && isTypeNullable(param.type) === true) {
1624
+ const effectiveOptional = getEffectiveOptional(param);
1625
+ if (!effectiveOptional && isTypeNullable(param.type) === true) {
1546
1626
  reportDiagnostic(program, {
1547
1627
  code: "nullable-required-header",
1548
1628
  target: NoTarget
@@ -1550,7 +1630,7 @@ function buildHeaderParameter(
1550
1630
  return paramMap;
1551
1631
  }
1552
1632
  const conditions = [];
1553
- if (param.optional) {
1633
+ if (effectiveOptional) {
1554
1634
  conditions.push(`${optionalParamName}?.${paramName} !== undefined`);
1555
1635
  }
1556
1636
  if (isTypeNullable(param.type) === true) {
@@ -1782,8 +1862,34 @@ function getContentTypeValue(
1782
1862
  }
1783
1863
  }
1784
1864
 
1865
+ /**
1866
+ * Gets the effective optionality for an HTTP parameter by checking
1867
+ * the linked method parameter via methodParameterSegments.
1868
+ * This is needed because @@override can change a method parameter's
1869
+ * optionality without updating the HTTP parameter's optional flag.
1870
+ * For client-level parameters (onClient), preserve the HTTP parameter's own flag.
1871
+ */
1872
+ function getEffectiveOptional(param: SdkHttpParameter): boolean {
1873
+ // For client-level parameters, the HTTP parameter's optional flag is authoritative
1874
+ if (param.onClient) {
1875
+ return Boolean(param.optional);
1876
+ }
1877
+ // For method-level parameters with a direct mapping to a single method param,
1878
+ // use the method parameter's optional flag (correctly reflects @@override changes)
1879
+ if (
1880
+ param.methodParameterSegments?.length === 1 &&
1881
+ param.methodParameterSegments[0]?.length === 1
1882
+ ) {
1883
+ const methodParam = param.methodParameterSegments[0]![0];
1884
+ if (methodParam) {
1885
+ return Boolean(methodParam.optional);
1886
+ }
1887
+ }
1888
+ return Boolean(param.optional);
1889
+ }
1890
+
1785
1891
  function isRequired(param: SdkHttpParameter) {
1786
- return !param.optional;
1892
+ return !getEffectiveOptional(param);
1787
1893
  }
1788
1894
 
1789
1895
  function getRequired(
@@ -1823,7 +1929,7 @@ function isConstant(param: SdkType): param is SdkConstantType {
1823
1929
  }
1824
1930
 
1825
1931
  function isOptional(param: SdkHttpParameter) {
1826
- return Boolean(param.optional);
1932
+ return getEffectiveOptional(param);
1827
1933
  }
1828
1934
 
1829
1935
  function getOptional(
@@ -2926,3 +3032,109 @@ function buildHeaderOnlyResponseValue(
2926
3032
 
2927
3033
  return `{ ${props.join(", ")} }`;
2928
3034
  }
3035
+
3036
+ /**
3037
+ * Returns the name for a non-model response wrapper type.
3038
+ * The name follows the pattern: {OperationGroupName}{MethodName}Response
3039
+ * @param method - The method tuple [prefixes, operation]
3040
+ */
3041
+ export function getOperationResponseTypeName(
3042
+ method: [string[], ServiceOperation]
3043
+ ): string {
3044
+ const prefixes = method[0];
3045
+ const operation = method[1];
3046
+ const prefix = !operation.name.includes("_")
3047
+ ? getClassicalLayerPrefix(prefixes, NameType.Interface)
3048
+ : "";
3049
+ return `${prefix}${normalizeName(operation.name, NameType.Interface)}Response`;
3050
+ }
3051
+
3052
+ /**
3053
+ * Determines whether wrapping the non-model return type is needed for an operation.
3054
+ * Returns an object with `shouldWrap` (whether to wrap) and `isBinary` (whether it's a binary response).
3055
+ */
3056
+ export function checkWrapNonModelReturn(
3057
+ context: SdkContext,
3058
+ operation: ServiceOperation
3059
+ ): { shouldWrap: boolean; isBinary: boolean } {
3060
+ const noWrap = { shouldWrap: false, isBinary: false };
3061
+
3062
+ // Only for non-LRO, non-paging normal operations
3063
+ if (
3064
+ isLroOnlyOperation(operation) ||
3065
+ isLroAndPagingOperation(operation) ||
3066
+ isPagingOnlyOperation(operation)
3067
+ ) {
3068
+ return noWrap;
3069
+ }
3070
+
3071
+ // Only if the feature flag is enabled
3072
+ if (!context.rlcOptions?.wrapNonModelReturn) {
3073
+ return noWrap;
3074
+ }
3075
+
3076
+ const response = operation.response;
3077
+ if (!response.type) {
3078
+ return noWrap; // void return type - no wrap needed
3079
+ }
3080
+
3081
+ const type = response.type;
3082
+ const contentTypes = operation.operation.responses[0]?.contentTypes ?? [];
3083
+
3084
+ // Check if it's a binary (bytes with binary content type) response
3085
+ if (type.__raw && isBinaryPayload(context, type.__raw, contentTypes)) {
3086
+ return { shouldWrap: true, isBinary: true };
3087
+ }
3088
+
3089
+ // Check if it's a non-model, non-record type (array, scalar, enum, etc.)
3090
+ if (type.kind !== "model" && type.kind !== "dict") {
3091
+ return { shouldWrap: true, isBinary: false };
3092
+ }
3093
+
3094
+ return noWrap;
3095
+ }
3096
+
3097
+ /**
3098
+ * Builds a TypeAliasDeclarationStructure for the non-model response wrapper type.
3099
+ * - For binary responses: { blobBody?: Promise<Blob>; readableStreamBody?: NodeJS.ReadableStream }
3100
+ * - For other non-model responses: { body: <type> }
3101
+ */
3102
+ export function buildNonModelResponseTypeDeclaration(
3103
+ context: SdkContext,
3104
+ method: [string[], ServiceOperation],
3105
+ isBinary: boolean
3106
+ ): TypeAliasDeclarationStructure {
3107
+ const typeName = getOperationResponseTypeName(method);
3108
+ const operation = method[1];
3109
+ let typeBody: string;
3110
+
3111
+ if (isBinary) {
3112
+ typeBody = `{
3113
+ /**
3114
+ * BROWSER ONLY
3115
+ *
3116
+ * The response body as a browser Blob.
3117
+ * Always \`undefined\` in node.js.
3118
+ */
3119
+ blobBody?: Promise<Blob>;
3120
+ /**
3121
+ * NODEJS ONLY
3122
+ *
3123
+ * The response body as a node.js Readable stream.
3124
+ * Always \`undefined\` in the browser.
3125
+ */
3126
+ readableStreamBody?: NodeJS.ReadableStream;
3127
+ }`;
3128
+ } else {
3129
+ const returnType = getTypeExpression(context, operation.response.type!);
3130
+ typeBody = `{ body: ${returnType} }`;
3131
+ }
3132
+
3133
+ return {
3134
+ kind: StructureKind.TypeAlias,
3135
+ name: typeName,
3136
+ type: typeBody,
3137
+ isExported: true,
3138
+ leadingTrivia: "\n"
3139
+ };
3140
+ }
@@ -59,6 +59,11 @@ export const SerializationHelpers = {
59
59
  name: "getBinaryResponse",
60
60
  location: "serialization/get-binary-response.ts"
61
61
  },
62
+ getBinaryResponseBody: {
63
+ kind: "function",
64
+ name: "getBinaryResponseBody",
65
+ location: "serialization/get-binary-response-body.ts"
66
+ },
62
67
  areAllPropsUndefined: {
63
68
  kind: "function",
64
69
  name: "areAllPropsUndefined",
@@ -163,6 +168,14 @@ export const CloudSettingHelpers = {
163
168
  }
164
169
  } as const;
165
170
 
171
+ export const CreateRecorderHelpers = {
172
+ createRecorder: {
173
+ kind: "function",
174
+ name: "createRecorder",
175
+ location: "recordedClient.ts"
176
+ }
177
+ } as const;
178
+
166
179
  export const XmlHelpers = {
167
180
  XmlSerializationOptions: {
168
181
  kind: "interface",
@@ -99,6 +99,7 @@ function extractRLCOptions(
99
99
  emitterOptions,
100
100
  flavor
101
101
  );
102
+ const wrapNonModelReturn = getWrapNonModelReturn(emitterOptions, flavor);
102
103
  const isMultiService = (dpgContext.allServiceNamespaces?.length ?? 0) > 1;
103
104
 
104
105
  return {
@@ -135,6 +136,7 @@ function extractRLCOptions(
135
136
  ignoreEnumMemberNameNormalize,
136
137
  hasSubscriptionId,
137
138
  ignoreNullableOnOptional,
139
+ wrapNonModelReturn,
138
140
  isMultiService,
139
141
  enableStorageCompat
140
142
  };
@@ -286,6 +288,18 @@ function getIgnoreNullableOnOptional(
286
288
  return flavor === "azure";
287
289
  }
288
290
 
291
+ function getWrapNonModelReturn(
292
+ emitterOptions: EmitterOptions,
293
+ flavor: PackageFlavor
294
+ ): boolean {
295
+ // If explicitly set in options, use that value
296
+ if (emitterOptions["wrap-non-model-return"] !== undefined) {
297
+ return Boolean(emitterOptions["wrap-non-model-return"]);
298
+ }
299
+ // Default to true for Azure services to maintain HLC backward compatibility
300
+ return flavor === "azure";
301
+ }
302
+
289
303
  function getIncludeShortcuts(emitterOptions: EmitterOptions) {
290
304
  return Boolean(emitterOptions["include-shortcuts"]);
291
305
  }
@@ -0,0 +1,22 @@
1
+ import { createRestError, StreamableMethod } from "@azure-rest/core-client";
2
+
3
+ /**
4
+ * Get the binary response body from a StreamableMethod, returning it as a Blob in browser environments and as a NodeJS.ReadableStream in Node.js environments. This function checks the response status against expected statuses and throws a RestError if the status is unexpected, optionally deserializing error details using a provided deserialization function.
5
+ */
6
+ export async function getBinaryResponseBody(
7
+ result: StreamableMethod,
8
+ expectedStatuses: string[] = ["200"],
9
+ deserializeError?: (error: any) => unknown
10
+ ): Promise<{ blobBody?: Promise<Blob>; readableStreamBody?: NodeJS.ReadableStream }> {
11
+ const browserStream = await result.asBrowserStream();
12
+ if (!expectedStatuses.includes(browserStream.status)) {
13
+ const error = createRestError(browserStream);
14
+ if (deserializeError) {
15
+ error.details = deserializeError(browserStream.body);
16
+ }
17
+
18
+ throw error;
19
+ }
20
+ return { blobBody: new Response(browserStream.body).blob(), readableStreamBody: undefined };
21
+
22
+ }
@@ -0,0 +1,24 @@
1
+ import { createRestError, StreamableMethod } from "@azure-rest/core-client";
2
+
3
+ /**
4
+ * Gets the binary response body from a StreamableMethod, returning it as a NodeJS.ReadableStream in Node.js environments. In browser environments, this function returns `undefined` for the stream and instead provides a `blobBody` that is a Promise resolving to a Blob, since browser environments do not support NodeJS.ReadableStream.
5
+ */
6
+ export async function getBinaryResponseBody(
7
+ result: StreamableMethod,
8
+ expectedStatuses: string[] = ["200"],
9
+ deserializeError?: (error: any) => unknown
10
+ ): Promise<{
11
+ blobBody?: Promise<Blob>;
12
+ readableStreamBody?: NodeJS.ReadableStream;
13
+ }> {
14
+ const nodeStream = await result.asNodeStream();
15
+ if (!expectedStatuses.includes(nodeStream.status)) {
16
+ const error = createRestError(nodeStream);
17
+ if (deserializeError) {
18
+ error.details = deserializeError(nodeStream.body);
19
+ }
20
+
21
+ throw error;
22
+ }
23
+ return { blobBody: undefined, readableStreamBody: nodeStream.body };
24
+ }
@@ -0,0 +1,30 @@
1
+ import type {
2
+ RecorderStartOptions,
3
+ VitestTestContext
4
+ } from "@azure-tools/test-recorder";
5
+ import { Recorder } from "@azure-tools/test-recorder";
6
+
7
+ const replaceableVariables: Record<string, string> = {
8
+ SUBSCRIPTION_ID: "azure_subscription_id"
9
+ };
10
+
11
+ const recorderEnvSetup: RecorderStartOptions = {
12
+ envSetupForPlayback: replaceableVariables,
13
+ removeCentralSanitizers: [
14
+ "AZSDK3493", // .name in the body is not a secret and is listed below in the beforeEach section
15
+ "AZSDK3430" // .id in the body is not a secret and is listed below in the beforeEach section
16
+ ]
17
+ };
18
+
19
+ /**
20
+ * creates the recorder and reads the environment variables from the `.env` file.
21
+ * Should be called first in the test suite to make sure environment variables are
22
+ * read before they are being used.
23
+ */
24
+ export async function createRecorder(
25
+ context: VitestTestContext
26
+ ): Promise<Recorder> {
27
+ const recorder = new Recorder(context);
28
+ await recorder.start(recorderEnvSetup);
29
+ return recorder;
30
+ }