@azure-tools/typespec-ts 0.52.0 → 0.52.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.
- package/CHANGELOG.md +6 -0
- package/dist/src/lib.d.ts +9 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +5 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +52 -8
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
- package/dist/src/transform/transfromRLCOptions.js +3 -1
- package/dist/src/transform/transfromRLCOptions.js.map +1 -1
- package/dist/src/utils/operationUtil.d.ts +4 -1
- package/dist/src/utils/operationUtil.d.ts.map +1 -1
- package/dist/src/utils/operationUtil.js +8 -1
- package/dist/src/utils/operationUtil.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -22
- package/src/lib.ts +15 -0
- package/src/modular/helpers/operationHelpers.ts +46 -0
- package/src/transform/transfromRLCOptions.ts +3 -1
- package/src/utils/operationUtil.ts +25 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-tools/typespec-ts",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.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-dev.
|
|
22
|
-
"@typespec/spector": "0.1.0-dev.
|
|
23
|
-
"@typespec/spec-api": "0.1.0-alpha.14-dev.
|
|
21
|
+
"@typespec/http-specs": "0.1.0-alpha.36-dev.3",
|
|
22
|
+
"@typespec/spector": "0.1.0-alpha.25-dev.5",
|
|
23
|
+
"@typespec/spec-api": "0.1.0-alpha.14-dev.4",
|
|
24
24
|
"@typespec/tspd": "0.74.1",
|
|
25
|
-
"@azure-tools/azure-http-specs": "0.1.0-alpha.
|
|
26
|
-
"@azure-tools/typespec-autorest": "^0.
|
|
27
|
-
"@azure-tools/typespec-azure-core": "^0.
|
|
28
|
-
"@azure-tools/typespec-azure-resource-manager": "^0.
|
|
29
|
-
"@azure-tools/typespec-client-generator-core": "^0.
|
|
25
|
+
"@azure-tools/azure-http-specs": "0.1.0-alpha.40-dev.0",
|
|
26
|
+
"@azure-tools/typespec-autorest": "^0.67.0",
|
|
27
|
+
"@azure-tools/typespec-azure-core": "^0.67.0",
|
|
28
|
+
"@azure-tools/typespec-azure-resource-manager": "^0.67.0",
|
|
29
|
+
"@azure-tools/typespec-client-generator-core": "^0.67.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",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
"@types/lodash": "^4.17.4",
|
|
40
40
|
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
|
41
41
|
"@typescript-eslint/parser": "^8.28.0",
|
|
42
|
-
"@typespec/compiler": "^1.
|
|
43
|
-
"@typespec/http": "^1.
|
|
44
|
-
"@typespec/openapi": "^1.
|
|
45
|
-
"@typespec/rest": "^0.
|
|
42
|
+
"@typespec/compiler": "^1.11.0",
|
|
43
|
+
"@typespec/http": "^1.11.0",
|
|
44
|
+
"@typespec/openapi": "^1.11.0",
|
|
45
|
+
"@typespec/rest": "^0.81.0",
|
|
46
46
|
"@typespec/ts-http-runtime": "^0.1.0",
|
|
47
|
-
"@typespec/versioning": "^0.
|
|
47
|
+
"@typespec/versioning": "^0.81.0",
|
|
48
48
|
"chai": "^4.3.6",
|
|
49
49
|
"chalk": "^4.0.0",
|
|
50
50
|
"cross-env": "^7.0.3",
|
|
@@ -63,16 +63,16 @@
|
|
|
63
63
|
"js-yaml": "^4.1.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@azure-tools/typespec-azure-core": "^0.
|
|
67
|
-
"@azure-tools/typespec-client-generator-core": "^0.
|
|
68
|
-
"@typespec/compiler": "^1.
|
|
69
|
-
"@typespec/http": "^1.
|
|
70
|
-
"@typespec/rest": "^0.
|
|
71
|
-
"@typespec/versioning": "^0.
|
|
72
|
-
"@typespec/xml": "^0.
|
|
66
|
+
"@azure-tools/typespec-azure-core": "^0.67.0",
|
|
67
|
+
"@azure-tools/typespec-client-generator-core": "^0.67.1",
|
|
68
|
+
"@typespec/compiler": "^1.11.0",
|
|
69
|
+
"@typespec/http": "^1.11.0",
|
|
70
|
+
"@typespec/rest": "^0.81.0",
|
|
71
|
+
"@typespec/versioning": "^0.81.0",
|
|
72
|
+
"@typespec/xml": "^0.81.0"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@azure-tools/rlc-common": "^0.52.
|
|
75
|
+
"@azure-tools/rlc-common": "^0.52.1",
|
|
76
76
|
"fast-xml-parser": "^4.5.0",
|
|
77
77
|
"fs-extra": "^11.1.0",
|
|
78
78
|
"lodash": "^4.17.21",
|
package/src/lib.ts
CHANGED
|
@@ -86,6 +86,15 @@ export interface EmitterOptions {
|
|
|
86
86
|
* Set to false to return the non-model types directly.
|
|
87
87
|
*/
|
|
88
88
|
"wrap-non-model-return"?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* When set to true, HEAD operations with a void response body will return `{ body: boolean }`
|
|
91
|
+
* instead of `void`, where `body: true` indicates a 2xx success (resource exists) and
|
|
92
|
+
* `body: false` indicates a non-2xx response (e.g., 404 Not Found).
|
|
93
|
+
* This matches the HLC behavior for resource existence check operations.
|
|
94
|
+
* Only applies when `wrap-non-model-return` is also enabled.
|
|
95
|
+
* Defaults to `false`.
|
|
96
|
+
*/
|
|
97
|
+
"head-as-boolean"?: boolean;
|
|
89
98
|
/**
|
|
90
99
|
* When enabled, every regular (non-LRO, non-paging) operation return type is augmented with a
|
|
91
100
|
* `_response` property containing `rawResponse`, `parsedBody`, and `parsedHeaders`.
|
|
@@ -390,6 +399,12 @@ export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
|
|
|
390
399
|
description:
|
|
391
400
|
"When set to true (default for Azure services), non-model return types (arrays, scalars, enums, bytes with binary content type) will be wrapped in an XxxResponse type for HLC backward compatibility during TypeSpec migration."
|
|
392
401
|
},
|
|
402
|
+
"head-as-boolean": {
|
|
403
|
+
type: "boolean",
|
|
404
|
+
nullable: true,
|
|
405
|
+
description:
|
|
406
|
+
"When set to true, HEAD operations with void response return `{ body: boolean }` (true=2xx, false=non-2xx) instead of void. Requires wrap-non-model-return to also be enabled. Defaults to false."
|
|
407
|
+
},
|
|
393
408
|
"enable-storage-compat": {
|
|
394
409
|
type: "boolean",
|
|
395
410
|
nullable: true,
|
|
@@ -233,6 +233,14 @@ export function getDeserializePrivateFunction(
|
|
|
233
233
|
name: (response as any).name ?? "",
|
|
234
234
|
type: getTypeExpression(context, response.type)
|
|
235
235
|
};
|
|
236
|
+
} else if (
|
|
237
|
+
!response.type &&
|
|
238
|
+
isHeadOperation(operation) &&
|
|
239
|
+
context.rlcOptions?.headAsBoolean
|
|
240
|
+
) {
|
|
241
|
+
// HEAD operation with head-as-boolean but wrap-non-model-return disabled:
|
|
242
|
+
// return plain boolean instead of wrapped { body: boolean }
|
|
243
|
+
returnType = { name: "", type: "boolean" };
|
|
236
244
|
} else {
|
|
237
245
|
returnType = { name: "", type: "void" };
|
|
238
246
|
}
|
|
@@ -452,6 +460,18 @@ export function getDeserializePrivateFunction(
|
|
|
452
460
|
}
|
|
453
461
|
} else if (returnType.type === "void") {
|
|
454
462
|
statements.push("return;");
|
|
463
|
+
} else if (
|
|
464
|
+
!deserializedType &&
|
|
465
|
+
isHeadOperation(operation) &&
|
|
466
|
+
context.rlcOptions?.headAsBoolean
|
|
467
|
+
) {
|
|
468
|
+
if (shouldWrap) {
|
|
469
|
+
// Case 1: wrap-non-model-return + head-as-boolean → return { body: boolean }
|
|
470
|
+
statements.push(`return { body: result.status.startsWith("2") };`);
|
|
471
|
+
} else {
|
|
472
|
+
// Case 2: head-as-boolean only (no wrap) → return plain boolean
|
|
473
|
+
statements.push(`return result.status.startsWith("2");`);
|
|
474
|
+
}
|
|
455
475
|
} else {
|
|
456
476
|
statements.push("return;");
|
|
457
477
|
}
|
|
@@ -1040,6 +1060,14 @@ export function getOperationFunction(
|
|
|
1040
1060
|
name: "",
|
|
1041
1061
|
type: `${buildHeaderOnlyResponseType(context, responseHeaders)}`
|
|
1042
1062
|
};
|
|
1063
|
+
} else if (
|
|
1064
|
+
!response.type &&
|
|
1065
|
+
isHeadOperation(operation) &&
|
|
1066
|
+
context.rlcOptions?.headAsBoolean
|
|
1067
|
+
) {
|
|
1068
|
+
// HEAD operation with head-as-boolean but wrap-non-model-return disabled:
|
|
1069
|
+
// return plain boolean instead of wrapped { body: boolean }
|
|
1070
|
+
returnType = { name: "", type: "boolean" };
|
|
1043
1071
|
}
|
|
1044
1072
|
|
|
1045
1073
|
// When storage-compat is enabled, wrap the return type with StorageCompatResponseInfo
|
|
@@ -3148,6 +3176,13 @@ function isWrappableType(context: SdkContext, type: SdkType): boolean {
|
|
|
3148
3176
|
return true;
|
|
3149
3177
|
}
|
|
3150
3178
|
|
|
3179
|
+
/**
|
|
3180
|
+
* Returns true if the operation uses the HTTP HEAD method.
|
|
3181
|
+
*/
|
|
3182
|
+
function isHeadOperation(operation: ServiceOperation): boolean {
|
|
3183
|
+
return operation.operation.verb.toLowerCase() === "head";
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3151
3186
|
/**
|
|
3152
3187
|
* Determines whether wrapping the non-model return type is needed for an operation.
|
|
3153
3188
|
* Returns an object with `shouldWrap` (whether to wrap) and `isBinary` (whether it's a binary response).
|
|
@@ -3182,6 +3217,13 @@ export function checkWrapNonModelReturn(
|
|
|
3182
3217
|
|
|
3183
3218
|
const { type } = operation.response;
|
|
3184
3219
|
if (!type) {
|
|
3220
|
+
// Special case: HEAD operation with void response → wrap as boolean { body: boolean }
|
|
3221
|
+
// This matches HLC behavior where HEAD operations with no response body
|
|
3222
|
+
// return { body: boolean } indicating if the resource exists (2xx = true, 4xx = false).
|
|
3223
|
+
// Requires `head-as-boolean: true` to be explicitly set in the emitter options.
|
|
3224
|
+
if (isHeadOperation(operation) && context.rlcOptions?.headAsBoolean) {
|
|
3225
|
+
return { shouldWrap: true, isBinary: false };
|
|
3226
|
+
}
|
|
3185
3227
|
return noWrap; // void return type - no wrap needed
|
|
3186
3228
|
}
|
|
3187
3229
|
|
|
@@ -3227,6 +3269,10 @@ export function buildNonModelResponseTypeDeclaration(
|
|
|
3227
3269
|
*/
|
|
3228
3270
|
readableStreamBody?: NodeJS.ReadableStream;
|
|
3229
3271
|
}`;
|
|
3272
|
+
} else if (!operation.response.type && isHeadOperation(operation)) {
|
|
3273
|
+
// HEAD as boolean: the body property is a boolean indicating if the resource exists.
|
|
3274
|
+
// true = resource exists (2xx response), false = resource not found (e.g., 404)
|
|
3275
|
+
typeBody = `{ body: boolean }`;
|
|
3230
3276
|
} else {
|
|
3231
3277
|
const returnType = getTypeExpression(context, operation.response.type!);
|
|
3232
3278
|
typeBody = `{ body: ${returnType} }`;
|
|
@@ -95,6 +95,7 @@ function extractRLCOptions(
|
|
|
95
95
|
const enableStorageCompat = emitterOptions["enable-storage-compat"] === true;
|
|
96
96
|
const treatUnknownAsRecord =
|
|
97
97
|
emitterOptions["treat-unknown-as-record"] === true;
|
|
98
|
+
const headAsBoolean = emitterOptions["head-as-boolean"] === true;
|
|
98
99
|
const typespecTitleMap = emitterOptions["typespec-title-map"];
|
|
99
100
|
const hasSubscriptionId = getSubscriptionId(dpgContext);
|
|
100
101
|
const ignoreNullableOnOptional = getIgnoreNullableOnOptional(
|
|
@@ -141,7 +142,8 @@ function extractRLCOptions(
|
|
|
141
142
|
wrapNonModelReturn,
|
|
142
143
|
isMultiService,
|
|
143
144
|
enableStorageCompat,
|
|
144
|
-
treatUnknownAsRecord
|
|
145
|
+
treatUnknownAsRecord,
|
|
146
|
+
headAsBoolean
|
|
145
147
|
};
|
|
146
148
|
}
|
|
147
149
|
|
|
@@ -670,6 +670,14 @@ export type ServiceOperation = SdkServiceMethod<SdkHttpOperation> & {
|
|
|
670
670
|
oriName?: string;
|
|
671
671
|
};
|
|
672
672
|
|
|
673
|
+
export type ServiceParameter = (
|
|
674
|
+
| SdkMethodParameter
|
|
675
|
+
| SdkHttpParameter
|
|
676
|
+
| SdkBodyParameter
|
|
677
|
+
) & {
|
|
678
|
+
oriName?: string;
|
|
679
|
+
};
|
|
680
|
+
|
|
673
681
|
export function getMethodHierarchiesMap(
|
|
674
682
|
context: SdkContext,
|
|
675
683
|
client: SdkClientType<SdkServiceOperation>
|
|
@@ -804,9 +812,23 @@ export function isTenantLevelOperation(
|
|
|
804
812
|
|
|
805
813
|
function resolveParameterNameConflict(
|
|
806
814
|
operationOrGroup: SdkServiceMethod<SdkHttpOperation>,
|
|
807
|
-
p:
|
|
808
|
-
):
|
|
809
|
-
|
|
815
|
+
p: ServiceParameter
|
|
816
|
+
): ServiceParameter {
|
|
817
|
+
// When the name starts with $DO_NOT_NORMALIZE$, record the original name so that
|
|
818
|
+
// subsequent calls (e.g. emitNonModelResponseTypes then buildApiOptions both call
|
|
819
|
+
// getMethodHierarchiesMap on the same TCGC object) always normalize from the
|
|
820
|
+
// original, not from the already-mutated value.
|
|
821
|
+
const paramName = normalizeName(
|
|
822
|
+
p.name,
|
|
823
|
+
NameType.Parameter,
|
|
824
|
+
true,
|
|
825
|
+
undefined,
|
|
826
|
+
undefined,
|
|
827
|
+
p.oriName
|
|
828
|
+
);
|
|
829
|
+
if (!p.oriName) {
|
|
830
|
+
p.oriName = p.name;
|
|
831
|
+
}
|
|
810
832
|
if (paramName === operationOrGroup.name) {
|
|
811
833
|
p.name = `${paramName}Parameter`;
|
|
812
834
|
} else {
|