From 180d7aaaca3dea41b1f83e8affa3b5245ea912ce Mon Sep 17 00:00:00 2001 From: tadelesh Date: Wed, 17 Jun 2026 16:15:47 +0800 Subject: [PATCH 1/3] feat(typespec-client-generator-core): support per-service api-version map for multi-service packages The `api-version` emitter option now accepts either a string or a map from each service namespace's full name to its desired version. Services not listed in the map default to their latest version. Multi-service packages do not support the `all` value (in either form); it is ignored and treated as latest. Resolves #4009 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tcgc-api-version-map-2026-5-17-10-55-0.md | 7 + .../typespec-client-generator-core/README.md | 2 +- .../src/cache.ts | 7 +- .../src/interfaces.ts | 2 +- .../src/internal-utils.ts | 166 ++++++++++++------ .../typespec-client-generator-core/src/lib.ts | 28 ++- .../src/public-utils.ts | 7 +- .../src/types.ts | 2 +- .../test/clients/structure.test.ts | 14 +- .../package/api-versions-metadata.test.ts | 115 ++++++++++++ .../reference/emitter.md | 2 +- 11 files changed, 279 insertions(+), 73 deletions(-) create mode 100644 .chronus/changes/tcgc-api-version-map-2026-5-17-10-55-0.md diff --git a/.chronus/changes/tcgc-api-version-map-2026-5-17-10-55-0.md b/.chronus/changes/tcgc-api-version-map-2026-5-17-10-55-0.md new file mode 100644 index 0000000000..146f51fd4d --- /dev/null +++ b/.chronus/changes/tcgc-api-version-map-2026-5-17-10-55-0.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Support a per-service `api-version` map for multi-service packages. The `api-version` emitter option now accepts either a string (applied to single service packages, or the `latest`/`all` keywords) or a map from each service namespace's full name to its desired version. Services not listed in the map default to their latest version. diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index 85099d3bd0..5491f1bde0 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -64,7 +64,7 @@ When set to `true`, the emitter will generate convenience methods for each servi **Type:** `string` -Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. +Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version. ### `license` diff --git a/packages/typespec-client-generator-core/src/cache.ts b/packages/typespec-client-generator-core/src/cache.ts index ec10dbbcfe..db3501e237 100644 --- a/packages/typespec-client-generator-core/src/cache.ts +++ b/packages/typespec-client-generator-core/src/cache.ts @@ -41,6 +41,7 @@ export function prepareClientAndOperationCache(context: TCGCContext): void { const servicesNs = new Set(); clients.forEach((c) => c.services.forEach((s) => servicesNs.add(s))); + const isMultiService = servicesNs.size > 1; // handle versioning with mutated types context.__packageVersions = new Map(); @@ -54,10 +55,8 @@ export function prepareClientAndOperationCache(context: TCGCContext): void { continue; } - // Single service needs to filter versions based on `apiVersion` config - if (servicesNs.size === 1) { - removeVersionsLargerThanExplicitlySpecified(context, versions); - } + // Filter versions based on the resolved `apiVersion` config for this service + removeVersionsLargerThanExplicitlySpecified(context, versions, serviceNs, isMultiService); context.__packageVersionEnum!.set(serviceNs, versions[0].enumMember.enum); context.__packageVersions!.set( diff --git a/packages/typespec-client-generator-core/src/interfaces.ts b/packages/typespec-client-generator-core/src/interfaces.ts index 0f3d3fc513..09050952a3 100644 --- a/packages/typespec-client-generator-core/src/interfaces.ts +++ b/packages/typespec-client-generator-core/src/interfaces.ts @@ -41,7 +41,7 @@ export interface TCGCContext { generateConvenienceMethods?: boolean; examplesDir?: string; namespaceFlag?: string; - apiVersion?: string; + apiVersion?: string | Record; license?: { name: string; company?: string; diff --git a/packages/typespec-client-generator-core/src/internal-utils.ts b/packages/typespec-client-generator-core/src/internal-utils.ts index 9c7b655fa8..4c896eb5f0 100644 --- a/packages/typespec-client-generator-core/src/internal-utils.ts +++ b/packages/typespec-client-generator-core/src/internal-utils.ts @@ -98,7 +98,7 @@ export interface TCGCEmitterOptions extends BrandedSdkEmitterOptionsInterface { export interface UnbrandedSdkEmitterOptionsInterface { "generate-protocol-methods"?: boolean; "generate-convenience-methods"?: boolean; - "api-version"?: string; + "api-version"?: string | Record; license?: { name: string; company?: string; @@ -364,6 +364,16 @@ export function filterApiVersionsWithDecorators( type: Type, apiVersions: string[], ): string[] { + // The service namespace is only needed to resolve a per-service version map. + const isMultiService = context.getPackageVersions().size > 1; + const serviceNamespace = + typeof context.apiVersion === "object" ? getServiceNamespaceForType(context, type) : undefined; + const apiVersion = resolveApiVersionForService(context, serviceNamespace, isMultiService); + // index of the explicitly specified version in the list; -1 means latest / not found + const apiVersionIndex = + apiVersion === undefined || apiVersion === "latest" || apiVersion === "all" + ? -1 + : apiVersions.indexOf(apiVersion); const addedOnVersions = getAddedOnVersions(context.program, type)?.map((x) => x.value) ?? []; const removedOnVersions = getRemovedOnVersions(context.program, type)?.map((x) => x.value) ?? []; let added: boolean = addedOnVersions.length ? false : true; @@ -381,13 +391,8 @@ export function filterApiVersionsWithDecorators( removeCounter++; } if (added) { - // only add version smaller than config - if ( - context.apiVersion === undefined || - context.apiVersion === "latest" || - context.apiVersion === "all" || - apiVersions.indexOf(context.apiVersion) >= i - ) { + // only add version smaller than config (or all versions when no explicit version applies) + if (apiVersionIndex < 0 || apiVersionIndex >= i) { retval.push(version); } } @@ -671,14 +676,13 @@ export function getHttpOperationResponseHeaders( export function removeVersionsLargerThanExplicitlySpecified( context: TCGCContext, versions: { value: string | number }[], + serviceNamespace: Namespace | undefined, + isMultiService: boolean, ): void { // filter with specific api version - if ( - context.apiVersion !== undefined && - context.apiVersion !== "latest" && - context.apiVersion !== "all" - ) { - const index = versions.findIndex((version) => version.value === context.apiVersion); + const apiVersion = resolveApiVersionForService(context, serviceNamespace, isMultiService); + if (apiVersion !== undefined && apiVersion !== "latest" && apiVersion !== "all") { + const index = versions.findIndex((version) => version.value === apiVersion); if (index >= 0) { versions.splice(index + 1, versions.length - index - 1); } @@ -689,9 +693,15 @@ export function filterPreviewVersion( context: TCGCContext, sdkVersionsEnum: SdkEnumType, defaultApiVersion: string, + serviceNamespace?: Namespace, ): void { // if they explicitly set an api version, remove larger versions - removeVersionsLargerThanExplicitlySpecified(context, sdkVersionsEnum.values); + removeVersionsLargerThanExplicitlySpecified( + context, + sdkVersionsEnum.values, + serviceNamespace, + context.getPackageVersions().size > 1, + ); if (!context.previewStringRegex.test(defaultApiVersion)) { sdkVersionsEnum.values = sdkVersionsEnum.values.filter((v) => { if (typeof v.value !== "string") { @@ -942,16 +952,15 @@ function getVersioningMutator( export function handleVersioningMutationForGlobalNamespace(context: TCGCContext): Namespace { const globalNamespace = context.program.getGlobalNamespaceType(); - // First consider explicit clients + // Compute the set of service namespaces the SDK targets. This runs before the + // client/operation cache (and thus context.getPackageVersions()) is available, + // so the set is derived directly from explicit `@client`s or `@service`s. const servicesNs = new Set(); listScopedDecoratorData(context, clientKey).forEach((v, k) => { - // See all explicit clients that in TypeSpec program if (!unsafe_Realm.realmForType.has(k)) { (v as SdkClient).services.forEach((s) => servicesNs.add(s)); } }); - - // Then see the original services if (servicesNs.size === 0) { listServices(context.program).map((v) => servicesNs.add(v.type)); } @@ -959,49 +968,38 @@ export function handleVersioningMutationForGlobalNamespace(context: TCGCContext) // No service, thus no versioning mutation needed if (servicesNs.size === 0) return globalNamespace; - // Multi services' client should not honor the specific api-version set in config - if ( - servicesNs.size > 1 && - context.apiVersion !== undefined && - context.apiVersion !== "latest" && - context.apiVersion !== "all" - ) { - context.apiVersion = undefined; - } - - // Explicit all API version setting, thus no versioning mutation needed - if (context.apiVersion === "all") return globalNamespace; + const isMultiService = servicesNs.size > 1; // Compose service mutators const mutators: unsafe_MutatorWithNamespace[] = []; for (const serviceNs of servicesNs) { + // Resolve the api-version config that applies to this specific service. + const serviceApiVersion = resolveApiVersionForService(context, serviceNs, isMultiService); + + // Explicit `all` setting for this service, keep all its versions (no mutation). + if (serviceApiVersion === "all") continue; + const versions = getVersions(context.program, serviceNs)[1]?.getVersions(); - // If the service has no versioning, no mutation needed - if (!versions || versions.length === 0) return globalNamespace; + // If the service has no versioning, no mutation needed for it + if (!versions || versions.length === 0) continue; - // Single service needs to filter versions based on `apiVersion` config - if (servicesNs.size === 1) { - removeVersionsLargerThanExplicitlySpecified(context, versions); - } + // Filter versions based on the `apiVersion` config resolved for this service + removeVersionsLargerThanExplicitlySpecified(context, versions, serviceNs, isMultiService); const versionsValues = versions.map((v) => v.value); - // Fix apiVersion setting problem only if there's only one service - if (servicesNs.size === 1) { - if ( - context.apiVersion !== undefined && - context.apiVersion !== "latest" && - context.apiVersion !== "all" && - !versionsValues.includes(context.apiVersion) - ) { - reportDiagnostic(context.program, { - code: "api-version-undefined", - format: { version: context.apiVersion }, - target: serviceNs, - }); - context.apiVersion = versionsValues[versionsValues.length - 1]; - } + // Report when the explicitly specified version does not exist; fall back to the latest + if ( + serviceApiVersion !== undefined && + serviceApiVersion !== "latest" && + !versionsValues.includes(serviceApiVersion) + ) { + reportDiagnostic(context.program, { + code: "api-version-undefined", + format: { version: serviceApiVersion }, + target: serviceNs, + }); } // Get service mutator according to the version setting @@ -1020,6 +1018,70 @@ export function handleVersioningMutationForGlobalNamespace(context: TCGCContext) return subgraph.type; } +/** + * Resolve the `api-version` config that applies to a specific service namespace. + * + * - When the option is a string: `latest` is a global keyword; any other string + * (a specific version or `all`) applies only to the single service case and is + * ignored for multi-service packages. + * - When the option is a record, the version is looked up by the service + * namespace's full name. Services that are not listed return `undefined` + * (meaning "use the latest version"). + * + * Multi-service packages do not support the special `all` value (in either the + * string or the record form); it is ignored and treated as `undefined` (use the + * latest version of each service). + * + * The returned value can be a specific version, the special values `latest` / + * `all`, or `undefined`. + */ +export function resolveApiVersionForService( + context: TCGCContext, + serviceNamespace: Namespace | undefined, + isMultiService: boolean, +): string | undefined { + const config = context.apiVersion; + if (config === undefined) return undefined; + if (typeof config === "string") { + // `latest` is a global keyword that applies regardless of how many services + // the package targets. + if (config === "latest") return "latest"; + // `all` and specific version strings only apply to the single service case; + // multi-service packages do not support `all`. + return isMultiService ? undefined : config; + } + // Record case: map each service namespace's full name to a version. + if (serviceNamespace === undefined) return undefined; + const version = config[getNamespaceFullName(serviceNamespace)]; + // Multi-service packages do not support `all`; fall back to the latest version. + if (version === "all" && isMultiService) return undefined; + return version; +} + +/** + * Find the service namespace that owns the given type. Starts from the type's + * versioned namespace and walks up the enclosing namespaces until it reaches a + * known service namespace. Returns `undefined` if none is found. + * + * Must only be called after the client/operation cache is prepared, since the + * service namespaces are read from `context.getPackageVersions()`. + */ +function getServiceNamespaceForType( + context: TCGCContext, + type: Type | undefined, +): Namespace | undefined { + if (type === undefined) return undefined; + const services = context.getPackageVersions(); + if (services.size === 0) return undefined; + + let current: Namespace | undefined = getVersions(context.program, type)[0]; + while (current) { + if (services.has(current)) return current; + current = current.namespace; + } + return undefined; +} + export function resolveDuplicateGenearatedName( context: TCGCContext, type: Union | Model | TspLiteralType, diff --git a/packages/typespec-client-generator-core/src/lib.ts b/packages/typespec-client-generator-core/src/lib.ts index bc8580a19d..c5df7211fa 100644 --- a/packages/typespec-client-generator-core/src/lib.ts +++ b/packages/typespec-client-generator-core/src/lib.ts @@ -5,6 +5,27 @@ import { UnbrandedSdkEmitterOptionsInterface, } from "./internal-utils.js"; +// `api-version` accepts either a string (single service / `latest` / `all`) or a +// map from service namespace full name to version (multi-service). ajv rejects a +// top-level `nullable` next to `anyOf`, so it is declared on each branch and the +// schema is cast to satisfy the strongly-typed options schema. +const apiVersionSchema = { + anyOf: [ + { + type: "string", + nullable: true, + }, + { + type: "object", + additionalProperties: { type: "string" }, + required: [], + nullable: true, + }, + ], + description: + "Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version.", +} as any; + export const UnbrandedSdkEmitterOptions = { "generate-protocol-methods": { "generate-protocol-methods": { @@ -23,12 +44,7 @@ export const UnbrandedSdkEmitterOptions = { }, }, "api-version": { - "api-version": { - type: "string", - nullable: true, - description: - "Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`.", - }, + "api-version": apiVersionSchema, }, license: { license: { diff --git a/packages/typespec-client-generator-core/src/public-utils.ts b/packages/typespec-client-generator-core/src/public-utils.ts index 7521dc431f..76b2d462b1 100644 --- a/packages/typespec-client-generator-core/src/public-utils.ts +++ b/packages/typespec-client-generator-core/src/public-utils.ts @@ -74,7 +74,12 @@ export function getDefaultApiVersion( ): Version | undefined { try { const versions = getVersions(context.program, serviceNamespace)[1]!.getVersions(); - removeVersionsLargerThanExplicitlySpecified(context, versions); + removeVersionsLargerThanExplicitlySpecified( + context, + versions, + serviceNamespace, + context.getPackageVersions().size > 1, + ); // follow versioning principals of the versioning library and return last in list return versions[versions.length - 1]; } catch (e) { diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index ab015c8271..753d615426 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -2467,7 +2467,7 @@ export function handleAllTypes(context: TCGCContext): [void, readonly Diagnostic } else { sdkVersionsEnum = diagnostics.pipe(getSdkEnumWithDiagnostics(context, versionEnum)); } - filterPreviewVersion(context, sdkVersionsEnum, versions?.at(-1) || ""); + filterPreviewVersion(context, sdkVersionsEnum, versions?.at(-1) || "", service); diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.ApiVersionEnum, sdkVersionsEnum)); } } diff --git a/packages/typespec-client-generator-core/test/clients/structure.test.ts b/packages/typespec-client-generator-core/test/clients/structure.test.ts index 8161f925f0..b7508a4d4b 100644 --- a/packages/typespec-client-generator-core/test/clients/structure.test.ts +++ b/packages/typespec-client-generator-core/test/clients/structure.test.ts @@ -1208,7 +1208,7 @@ it("one client from multiple services with `@clientLocation`", async () => { strictEqual(biOperationApiVersionParam.correspondingMethodParams[0], biApiVersionParam); }); -it("one client from multiple services with api-version set to all", async () => { +it("one client from multiple services with api-version set to all (not supported, falls back to latest)", async () => { const { program } = await SimpleBaseTester.compile( createClientCustomizationInput( ` @@ -1277,14 +1277,16 @@ it("one client from multiple services with api-version set to all", async () => ok(aiApiVersionParam); strictEqual(aiApiVersionParam.clientDefaultValue, "av3"); - // With api-version all, both aTest and aTest2 should be included - strictEqual(aiClient.methods.length, 2); + // Multi-service does not support `all`; it is ignored and each service uses + // its latest version. ServiceA projects to av3, where aTest2 is removed. + strictEqual(aiClient.methods.length, 1); const aTest = aiClient.methods.find((m) => m.name === "aTest"); ok(aTest); deepStrictEqual(aTest.apiVersions, ["av1", "av2", "av3"]); - const aTest2 = aiClient.methods.find((m) => m.name === "aTest2"); - ok(aTest2); - deepStrictEqual(aTest2.apiVersions, ["av2"]); + strictEqual( + aiClient.methods.find((m) => m.name === "aTest2"), + undefined, + ); const biClient = client.children!.find((c) => c.name === "BI"); ok(biClient); diff --git a/packages/typespec-client-generator-core/test/package/api-versions-metadata.test.ts b/packages/typespec-client-generator-core/test/package/api-versions-metadata.test.ts index 252edaf930..6edc2ee10a 100644 --- a/packages/typespec-client-generator-core/test/package/api-versions-metadata.test.ts +++ b/packages/typespec-client-generator-core/test/package/api-versions-metadata.test.ts @@ -216,3 +216,118 @@ it("apiVersion 'all' should populate apiVersions with 'all'", async () => { strictEqual(sdkPackage.metadata.apiVersions.size, 1); strictEqual(sdkPackage.metadata.apiVersions.get("WidgetService"), "all"); }); + +const multiServiceSpec = createClientCustomizationInput( + ` + @service + @versioned(VersionsA) + namespace ServiceA { + enum VersionsA { + av1, + av2, + } + interface AI { + @route("/aTest") + aTest(@query("api-version") apiVersion: VersionsA): void; + } + } + @service + @versioned(VersionsB) + namespace ServiceB { + enum VersionsB { + bv1, + bv2, + } + interface BI { + @route("/bTest") + bTest(@query("api-version") apiVersion: VersionsB): void; + } + }`, + ` + @client( + { + name: "CombineClient", + service: [ServiceA, ServiceB], + autoMergeService: true, + } + ) + namespace CombineClient; +`, +); + +it("multiple services with api-version map should apply per-service versions", async () => { + const { program } = await SimpleBaseTester.compile(multiServiceSpec); + + const context = await createSdkContextForTester(program, { + "api-version": { ServiceA: "av1", ServiceB: "bv1" }, + }); + const sdkPackage = context.sdkPackage; + + // For multi-service, deprecated apiVersion should be undefined + strictEqual(sdkPackage.metadata.apiVersion, undefined); + + // Each service should map to its specified version + ok(sdkPackage.metadata.apiVersions); + strictEqual(sdkPackage.metadata.apiVersions.size, 2); + strictEqual(sdkPackage.metadata.apiVersions.get("ServiceA"), "av1"); + strictEqual(sdkPackage.metadata.apiVersions.get("ServiceB"), "bv1"); + + const client = sdkPackage.clients[0]; + const aiClient = client.children!.find((c) => c.name === "AI"); + ok(aiClient); + deepStrictEqual(aiClient.apiVersions, ["av1"]); + strictEqual(aiClient.clientInitialization.parameters[1].clientDefaultValue, "av1"); + + const biClient = client.children!.find((c) => c.name === "BI"); + ok(biClient); + deepStrictEqual(biClient.apiVersions, ["bv1"]); + strictEqual(biClient.clientInitialization.parameters[1].clientDefaultValue, "bv1"); +}); + +it("multiple services with api-version map should fall back to latest for unspecified services", async () => { + const { program } = await SimpleBaseTester.compile(multiServiceSpec); + + const context = await createSdkContextForTester(program, { + "api-version": { ServiceA: "av1" }, + }); + const sdkPackage = context.sdkPackage; + + // ServiceA uses the specified version, ServiceB falls back to its latest + ok(sdkPackage.metadata.apiVersions); + strictEqual(sdkPackage.metadata.apiVersions.size, 2); + strictEqual(sdkPackage.metadata.apiVersions.get("ServiceA"), "av1"); + strictEqual(sdkPackage.metadata.apiVersions.get("ServiceB"), "bv2"); + + const client = sdkPackage.clients[0]; + const aiClient = client.children!.find((c) => c.name === "AI"); + ok(aiClient); + deepStrictEqual(aiClient.apiVersions, ["av1"]); + + const biClient = client.children!.find((c) => c.name === "BI"); + ok(biClient); + deepStrictEqual(biClient.apiVersions, ["bv1", "bv2"]); +}); + +it("multiple services with api-version map does not support 'all' (falls back to latest)", async () => { + const { program } = await SimpleBaseTester.compile(multiServiceSpec); + + const context = await createSdkContextForTester(program, { + "api-version": { ServiceA: "all", ServiceB: "bv1" }, + }); + const sdkPackage = context.sdkPackage; + + // Multi-service does not support `all`; ServiceA falls back to its latest version. + ok(sdkPackage.metadata.apiVersions); + strictEqual(sdkPackage.metadata.apiVersions.size, 2); + strictEqual(sdkPackage.metadata.apiVersions.get("ServiceA"), "av2"); + strictEqual(sdkPackage.metadata.apiVersions.get("ServiceB"), "bv1"); + + const client = sdkPackage.clients[0]; + const aiClient = client.children!.find((c) => c.name === "AI"); + ok(aiClient); + deepStrictEqual(aiClient.apiVersions, ["av1", "av2"]); + + const biClient = client.children!.find((c) => c.name === "BI"); + ok(biClient); + deepStrictEqual(biClient.apiVersions, ["bv1"]); +}); diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md index 8fdfd25184..2c6301dda4 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md @@ -58,7 +58,7 @@ When set to `true`, the emitter will generate convenience methods for each servi **Type:** `string` -Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. +Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version. ### `license` From 8ce09f84ed2a9a6aabdc8f10a3f1033866d78ea4 Mon Sep 17 00:00:00 2001 From: tadelesh Date: Wed, 17 Jun 2026 18:30:49 +0800 Subject: [PATCH 2/3] update --- .../typespec-client-generator-core/README.md | 22 +++++-------------- .../typespec-client-generator-core/src/lib.ts | 6 ++--- .../reference/decorators.md | 8 +++---- .../reference/emitter.md | 14 ++---------- .../reference/index.mdx | 4 ++++ 5 files changed, 18 insertions(+), 36 deletions(-) diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index 5491f1bde0..9c9e097954 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -62,26 +62,16 @@ When set to `true`, the emitter will generate convenience methods for each servi ### `api-version` -**Type:** `string` +**Type:** `undefined` Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version. ### `license` -**Type:** `object { name, company, link, header, description }` +**Type:** `object` License information for the generated client code. -**Properties:** - -| Name | Type | Default | Description | -| ------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | `string` | | License name. The config is required. Predefined license are: MIT License, Apache License 2.0, BSD 3-Clause License, MPL 2.0, GPL-3.0, LGPL-3.0. For other license, you need to configure all the other license config manually. | -| `company` | `string` | | License company name. It will be used in copyright sentences. | -| `link` | `string` | | License link. | -| `header` | `string` | | License header. It will be used in the header comment of generated client code. | -| `description` | `string` | | License description. The full license text. | - ### `examples-dir` **Type:** `string` @@ -306,10 +296,10 @@ The source type to which the alternate type will be applied. ##### Parameters -| Name | Type | Description | -| --------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| alternate | `unknown \| Azure.ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | -| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | +| Name | Type | Description | +| --------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| alternate | `unknown \| ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | +| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | ##### Examples diff --git a/packages/typespec-client-generator-core/src/lib.ts b/packages/typespec-client-generator-core/src/lib.ts index c5df7211fa..8edc50c8be 100644 --- a/packages/typespec-client-generator-core/src/lib.ts +++ b/packages/typespec-client-generator-core/src/lib.ts @@ -6,11 +6,9 @@ import { } from "./internal-utils.js"; // `api-version` accepts either a string (single service / `latest` / `all`) or a -// map from service namespace full name to version (multi-service). ajv rejects a -// top-level `nullable` next to `anyOf`, so it is declared on each branch and the -// schema is cast to satisfy the strongly-typed options schema. +// map from service namespace full name to version (multi-service). const apiVersionSchema = { - anyOf: [ + oneOf: [ { type: "string", nullable: true, diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md index 2201419fb9..bfc01c1f15 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md @@ -169,10 +169,10 @@ The source type to which the alternate type will be applied. #### Parameters -| Name | Type | Description | -| --------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| alternate | `unknown \| Azure.ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | -| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | +| Name | Type | Description | +| --------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| alternate | `unknown \| ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | +| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | #### Examples diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md index 2c6301dda4..550e6bd6ea 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md @@ -56,26 +56,16 @@ When set to `true`, the emitter will generate convenience methods for each servi ### `api-version` -**Type:** `string` +**Type:** `undefined` Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version. ### `license` -**Type:** `object { name, company, link, header, description }` +**Type:** `object` License information for the generated client code. -**Properties:** - -| Name | Type | Default | Description | -| ------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | `string` | | License name. The config is required. Predefined license are: MIT License, Apache License 2.0, BSD 3-Clause License, MPL 2.0, GPL-3.0, LGPL-3.0. For other license, you need to configure all the other license config manually. | -| `company` | `string` | | License company name. It will be used in copyright sentences. | -| `link` | `string` | | License link. | -| `header` | `string` | | License header. It will be used in the header comment of generated client code. | -| `description` | `string` | | License description. The full license text. | - ### `examples-dir` **Type:** `string` diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx index 19e1d15f95..df513a7b2d 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx @@ -32,6 +32,10 @@ npm install --save-peer @azure-tools/typespec-client-generator-core [See documentation](./emitter.md) +## Azure + +## Azure.ClientGenerator + ## Azure.ClientGenerator.Core ### Decorators From ba52aed8178c2267506c71fad99b6c8928d7da4d Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Wed, 17 Jun 2026 10:42:27 -0400 Subject: [PATCH 3/3] regenerate --- .../typespec-client-generator-core/README.md | 27 ++++++++++++++----- .../reference/decorators.md | 8 +++--- .../reference/emitter.md | 19 +++++++++++-- .../reference/index.mdx | 4 --- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index 9c9e097954..d87b25d4e0 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -62,16 +62,31 @@ When set to `true`, the emitter will generate convenience methods for each servi ### `api-version` -**Type:** `undefined` +**Type:** `string | object` Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version. +**Options:** + +- `string` +- `object` + ### `license` -**Type:** `object` +**Type:** `object { name, company, link, header, description }` License information for the generated client code. +**Properties:** + +| Name | Type | Default | Description | +| ------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | `string` | | License name. The config is required. Predefined license are: MIT License, Apache License 2.0, BSD 3-Clause License, MPL 2.0, GPL-3.0, LGPL-3.0. For other license, you need to configure all the other license config manually. | +| `company` | `string` | | License company name. It will be used in copyright sentences. | +| `link` | `string` | | License link. | +| `header` | `string` | | License header. It will be used in the header comment of generated client code. | +| `description` | `string` | | License description. The full license text. | + ### `examples-dir` **Type:** `string` @@ -296,10 +311,10 @@ The source type to which the alternate type will be applied. ##### Parameters -| Name | Type | Description | -| --------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| alternate | `unknown \| ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | -| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | +| Name | Type | Description | +| --------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| alternate | `unknown \| Azure.ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | +| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | ##### Examples diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md index bfc01c1f15..2201419fb9 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md @@ -169,10 +169,10 @@ The source type to which the alternate type will be applied. #### Parameters -| Name | Type | Description | -| --------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| alternate | `unknown \| ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | -| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | +| Name | Type | Description | +| --------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| alternate | `unknown \| Azure.ClientGenerator.Core.ExternalType` | The alternate type to apply to the target. Can be a TypeSpec type or an ExternalType. | +| scope | `valueof string` | Specifies the target language emitters that the decorator should apply. If not set, the decorator will be applied to all language emitters by default.

**Supported language identifiers:** `csharp`, `python`, `java`, `javascript`, `go`, and other language emitter names (derived from the emitter package name, e.g., `@azure-tools/typespec-csharp` → `csharp`).

**Valid patterns:**
- Single language: `"python"`
- Multiple languages (comma-separated): `"python, java"`
- Negation to exclude languages: `"!csharp"` or `"!(java, python)"` | #### Examples diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md index 550e6bd6ea..d4c3e4ab8c 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/emitter.md @@ -56,16 +56,31 @@ When set to `true`, the emitter will generate convenience methods for each servi ### `api-version` -**Type:** `undefined` +**Type:** `string | object` Use this flag if you would like to generate the sdk only for a specific version. Default value is the latest version. Also accepts values `latest` and `all`. For multi-service packages, provide a map from each service namespace's full name to its desired version; services not listed default to their latest version. +**Options:** + +- `string` +- `object` + ### `license` -**Type:** `object` +**Type:** `object { name, company, link, header, description }` License information for the generated client code. +**Properties:** + +| Name | Type | Default | Description | +| ------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | `string` | | License name. The config is required. Predefined license are: MIT License, Apache License 2.0, BSD 3-Clause License, MPL 2.0, GPL-3.0, LGPL-3.0. For other license, you need to configure all the other license config manually. | +| `company` | `string` | | License company name. It will be used in copyright sentences. | +| `link` | `string` | | License link. | +| `header` | `string` | | License header. It will be used in the header comment of generated client code. | +| `description` | `string` | | License description. The full license text. | + ### `examples-dir` **Type:** `string` diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx index df513a7b2d..19e1d15f95 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx @@ -32,10 +32,6 @@ npm install --save-peer @azure-tools/typespec-client-generator-core [See documentation](./emitter.md) -## Azure - -## Azure.ClientGenerator - ## Azure.ClientGenerator.Core ### Decorators