From 54cec35ddfd669ff18e8c908cbb5ff03d65ea646 Mon Sep 17 00:00:00 2001 From: Andras Toth <4157749+tothandras@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:22:40 +0200 Subject: [PATCH 1/4] feat(api): use camelCase for TypeScript SDK --- api/spec/AGENTS.md | 90 +- api/spec/package.json | 4 +- .../packages/aip-client-javascript/.gitignore | 3 + .../packages/aip-client-javascript/.npmignore | 4 + .../packages/aip-client-javascript/README.md | 4 +- .../aip-client-javascript/src/funcs/addons.ts | 117 +- .../aip-client-javascript/src/funcs/apps.ts | 37 +- .../src/funcs/billing.ts | 79 +- .../src/funcs/currencies.ts | 96 +- .../src/funcs/customers.ts | 428 +- .../src/funcs/defaults.ts | 34 +- .../src/funcs/entitlements.ts | 16 +- .../aip-client-javascript/src/funcs/events.ts | 33 +- .../src/funcs/features.ts | 109 +- .../src/funcs/governance.ts | 35 +- .../src/funcs/invoices.ts | 17 +- .../src/funcs/llmCost.ts | 86 +- .../aip-client-javascript/src/funcs/meters.ts | 127 +- .../src/funcs/planAddons.ts | 92 +- .../aip-client-javascript/src/funcs/plans.ts | 113 +- .../src/funcs/subscriptions.ts | 185 +- .../aip-client-javascript/src/funcs/tax.ts | 85 +- .../aip-client-javascript/src/index.ts | 1 + .../aip-client-javascript/src/lib/config.ts | 7 + .../src/lib/encodings.ts | 8 +- .../aip-client-javascript/src/lib/wire.ts | 280 + .../src/models/operations/tax.ts | 2 +- .../src/models/schemas.ts | 6887 ++++++++++++++++- .../aip-client-javascript/src/models/types.ts | 1038 +-- .../tests/meters.spec.ts | 15 +- .../tests/wire-helpers.ts | 146 + .../tests/wire.generated.spec.ts | 59 + .../aip-client-javascript/tests/wire.spec.ts | 379 + .../aip-client-javascript/vitest.config.ts | 22 + .../packages/typespec-typescript/package.json | 3 +- .../typespec-typescript/src/ZodOperations.tsx | 22 +- .../typespec-typescript/src/casing-gate.ts | 209 + .../typespec-typescript/src/casing.ts | 18 + .../src/components/ZodSchema.tsx | 5 +- .../src/components/ZodSchemaDeclaration.tsx | 8 +- .../typespec-typescript/src/emitter.tsx | 63 +- .../src/interface-types.ts | 4 +- .../typespec-typescript/src/readme.ts | 4 +- .../typespec-typescript/src/request-types.ts | 4 +- .../src/runtime-templates.ts | 6 +- .../typespec-typescript/src/runtime/wire.ts | 280 + .../typespec-typescript/src/sdk-files.ts | 132 +- .../typespec-typescript/src/ts-types.ts | 4 +- .../typespec-typescript/src/utils.tsx | 73 +- .../typespec-typescript/src/wire-runtime.ts | 13 + .../typespec-typescript/src/zodBaseSchema.tsx | 50 +- .../typespec-typescript/test/casing.test.ts | 55 + api/spec/pnpm-lock.yaml | 364 +- 53 files changed, 10310 insertions(+), 1645 deletions(-) create mode 100644 api/spec/packages/aip-client-javascript/src/lib/wire.ts create mode 100644 api/spec/packages/aip-client-javascript/tests/wire-helpers.ts create mode 100644 api/spec/packages/aip-client-javascript/tests/wire.generated.spec.ts create mode 100644 api/spec/packages/aip-client-javascript/tests/wire.spec.ts create mode 100644 api/spec/packages/aip-client-javascript/vitest.config.ts create mode 100644 api/spec/packages/typespec-typescript/src/casing-gate.ts create mode 100644 api/spec/packages/typespec-typescript/src/casing.ts create mode 100644 api/spec/packages/typespec-typescript/src/runtime/wire.ts create mode 100644 api/spec/packages/typespec-typescript/src/wire-runtime.ts create mode 100644 api/spec/packages/typespec-typescript/test/casing.test.ts diff --git a/api/spec/AGENTS.md b/api/spec/AGENTS.md index e2ce90fb7c..b23ddab3ad 100644 --- a/api/spec/AGENTS.md +++ b/api/spec/AGENTS.md @@ -150,20 +150,71 @@ The hand-written baseline (kept under a temporary reference folder) defines the exact shape the generator must reproduce. Its tests are the conformance target — the generated SDK is "done" when it passes them. -### Casing: wire-native, no transformation - -The AIP API is **snake_case end to end** (enforced by the AIP casing lint rule). -Request/response bodies are snake_case in TypeSpec, OpenAPI, and the emitted zod -schemas alike. There is **no snake↔camel transform layer** — the JS object shape -is the wire shape. Do not add `.transform()` or case-converting middleware. - -### No runtime validation of responses - -Responses are typed via ky's `.json()`, never `schema.parse()`. Validating -responses turns additive (non-breaking) API fields into client breakage. zod is -retained only for type derivation (`z.input`/`z.output`) and the one -`baseError.safeParse` in the error path. Request bodies are likewise not parsed -(the server owns defaults and validation). +### Casing: camelCase public surface, snake_case wire + +The AIP API is **snake_case on the wire** (TypeSpec, OpenAPI, and the casing lint +rule stay snake). The generated JS SDK exposes a **camelCase** public surface — the +TS interfaces and zod schemas are camelCase — and a boundary mapper +(`src/lib/wire.ts`) translates at the edge: `toWire` (camelCase → snake_case) on +request bodies and query objects, `fromWire` (snake_case → camelCase) on responses. + +camelCase is the **TypeScript-specific** public surface, not a wire change — the +wire stays snake_case for every SDK. Other language generators are expected to apply +their own idiomatic surface transformation over the same snake_case wire: a Go SDK +would use exported UpperCamelCase fields with `json:"snake_case"` tags, a Python SDK +would keep snake_case (already idiomatic), etc. Keep casing decisions in the +per-language emitter; do not push a language's casing into TypeSpec, OpenAPI, or the +wire. + +The translation is a **deterministic casing rule**, not a per-field map: every wire +name round-trips through `toSnakeCase(toCamelCase(name))`, enforced at codegen by a +gate (`assertCasingDerivable`) that fails the build for any non-derivable name. The +public key is `toCamelCase(resolveEncodedName(...))`, so the wire key the mapper +emits is exactly the OpenAPI name. The mapper is **schema-driven**: it walks the zod +schema alongside the data so `Record` keys that are user data (label +names, meter dimension names) are preserved verbatim, while typed field keys +(including AIP `filter[field]` names and `sort.by`) are translated. + +The same gate (`assertCasingDerivable`) also **fails the build for a non-discriminated +union with two or more object variants reachable from a request body or success +response** — the mapper cannot pick a variant without a discriminator, and does not +guess. Use `@discriminated` for such unions (scalar-vs-object unions, and `T | T[]` +single-or-batch bodies, are fine — distinguished at runtime by JS type). Discriminated +unions dispatch via a memoized literal→variant map keyed on the (camel public / snake +wire) discriminator value. + +### Response/request mapping drops unknown fields + +`fromWire`/`toWire` **rename keys only** — they never call `schema.parse()`, never +apply zod defaults, and never coerce values. A field not present in the schema shape +is **dropped**, so the mapped object exactly matches the typed interface (a +server-added field is not in the type and does not survive). This is a deliberate +choice for strict typing over forward-compatibility. zod is retained for type +derivation (`z.input`/`z.output`), query/path coercion, mapper structure, and the +one `baseError.safeParse` in the error path. Error responses bypass the mapper +(`toError` reads the raw snake body; `HTTPError.getField` is a raw, untyped escape +hatch). + +### Optional wire-payload validation (`validate` option) + +`SDKOptions.validate` (default **off**) turns on schema validation of the actual +snake*case wire payload: the request body after `toWire` (before sending) and the +raw response body before `fromWire`. Validation uses the generated **`…Wire` +schemas** in `models/schemas.ts` — every model and per-op body/response is emitted a +second time in a snake_case "wire" pass (`WireModeContext` in the emitter), keyed by +the raw JSON wire name and made `z.strictObject`, so a wrong-shaped or +leaked-camelCase wire field is **rejected, not silently stripped**. Open models +(record spread, `emitsAsIntersection`, e.g. `baseError`) stay non-strict — strict +would defeat the record arm that exists to accept them. Because the wire pass is the +same emitter walk as the camelCase pass (parameterized by key-casing + strictness + +a separate refkey namespace), the two are structurally identical except for casing, +**by construction** — no runtime schema derivation. A failure throws +`ValidationError`, which `request()` surfaces as `Result.error` (request validation +runs \_inside* the `request()` closure so it does not throw synchronously). +**Enabling `validate` re-introduces exactly the rejection the default policy +avoids**: a strict wire schema rejects additive/unknown server fields and unknown +enum values. It is opt-in defense-in-depth, not the default, precisely because the +default contract must not break on additive fields. ### Documented types: generated from TypeSpec, verified against zod @@ -368,8 +419,8 @@ if you rename it, update both the fence declarations and `operationsTable`'s pre together. The table-of-contents anchors and the headings are produced by one `slug()` so TOC links never break. Every code fence is self-contained (constructs its own `client`) and typechecks against the real -generated types; the `meters.create` payload uses wire-native snake_case -(`event_type`, `value_property`) and the lowercase aggregation enum (`'sum'`), +generated types; the `meters.create` payload uses the camelCase public surface +(`eventType`, `valueProperty`) and the lowercase aggregation enum (`'sum'`), matching `CreateMeterRequest`. The README is emitted raw (compact markdown tables); the generated `aip-client-javascript` output and the emitter's own `typespec-typescript/src` are **not** prettier-clean on HEAD (`prettier --check .` @@ -416,8 +467,11 @@ not "correct" them to mainline ky. - scalar `filter[key]=v` is shorthand for `filter[key][eq]=v` - array operands (`oeq`/`ocontains`) are **comma-joined into one param**; the server **rejects repeated** query params. Never emit `k=a&k=b`. -- `sort` is a plain string `" [asc|desc]"` (single space), not an object; - `encodeSort` flattens `{by, order}` to that form. +- `sort` serializes to a plain string `" [asc|desc]"` (single space) on the + wire; the SDK accepts a `{by, order}` object and `encodeSort` flattens it. `by` is + a **camelCase** field name in the SDK and is `toSnakeCase`-translated to the wire + field name (the server validates snake field names; see + `api/v3/handlers/.../convert.go`). ## Tests diff --git a/api/spec/package.json b/api/spec/package.json index 3a4d3663cf..60f6abc33a 100644 --- a/api/spec/package.json +++ b/api/spec/package.json @@ -8,11 +8,13 @@ "format": "prettier --list-different --find-config-path --write . && pnpm --filter @openmeter/api-spec-aip run format", "lint": "prettier --check . && pnpm --filter @openmeter/api-spec-legacy run lint && pnpm --filter @openmeter/api-spec-aip run lint", "lint:fix": "prettier --write .", - "test:sdk": "vitest --run --root packages/aip-client-javascript" + "test:sdk": "vitest --run --root packages/aip-client-javascript", + "test:sdk:coverage": "vitest --run --coverage --root packages/aip-client-javascript" }, "devDependencies": { "@fetch-mock/vitest": "0.2.18", "@typespec/prettier-plugin-typespec": "1.12.0", + "@vitest/coverage-v8": "4.1.8", "prettier": "3.8.3", "vitest": "4.1.8" }, diff --git a/api/spec/packages/aip-client-javascript/.gitignore b/api/spec/packages/aip-client-javascript/.gitignore index a46dacd882..0e9f501696 100644 --- a/api/spec/packages/aip-client-javascript/.gitignore +++ b/api/spec/packages/aip-client-javascript/.gitignore @@ -2,6 +2,9 @@ dist/ tsconfig.tsbuildinfo +# Test coverage output +coverage/ + # Dependencies node_modules/ diff --git a/api/spec/packages/aip-client-javascript/.npmignore b/api/spec/packages/aip-client-javascript/.npmignore index f777fb03e6..8f9fffdd92 100644 --- a/api/spec/packages/aip-client-javascript/.npmignore +++ b/api/spec/packages/aip-client-javascript/.npmignore @@ -11,7 +11,11 @@ src/ tests/ tsconfig.json tsconfig.tsbuildinfo +vitest.config.ts .gitignore +# Test coverage output +coverage/ + # Source maps *.map diff --git a/api/spec/packages/aip-client-javascript/README.md b/api/spec/packages/aip-client-javascript/README.md index f6b1698a4d..b9323ce39d 100644 --- a/api/spec/packages/aip-client-javascript/README.md +++ b/api/spec/packages/aip-client-javascript/README.md @@ -97,8 +97,8 @@ const meter = await client.meters.create({ name: 'Tokens', key: 'tokens', aggregation: 'sum', - event_type: 'request', - value_property: '$.tokens', + eventType: 'request', + valueProperty: '$.tokens', }) const meters = await client.meters.list() diff --git a/api/spec/packages/aip-client-javascript/src/funcs/addons.ts b/api/spec/packages/aip-client-javascript/src/funcs/addons.ts index 656f00be9a..bf93c2659c 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/addons.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/addons.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListAddonsRequest, ListAddonsResponse, @@ -24,15 +26,26 @@ export function listAddons( req: ListAddonsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + }, + schemas.listAddonsQueryParams, + ), + ) return request(() => http(client) .get('openmeter/addons', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listAddonsResponseWire, data) + } + return fromWire(data, schemas.listAddonsResponse) + }), ) } @@ -41,11 +54,21 @@ export function createAddon( req: CreateAddonRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/addons', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createAddonBody) + if (client._options.validate) { + assertValid(schemas.createAddonBodyWire, body) + } + return http(client) + .post('openmeter/addons', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createAddonResponseWire, data) + } + return fromWire(data, schemas.createAddonResponse) + }) + }) } export function updateAddon( @@ -53,14 +76,22 @@ export function updateAddon( req: UpdateAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/addons/{addonId}', { - addonId: req.addonId, + const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}` + return request(() => { + const body = toWire(req.body, schemas.updateAddonBody) + if (client._options.validate) { + assertValid(schemas.updateAddonBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updateAddonResponseWire, data) + } + return fromWire(data, schemas.updateAddonResponse) + }) }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) } export function getAddon( @@ -68,10 +99,18 @@ export function getAddon( req: GetAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/addons/{addonId}', { - addonId: req.addonId, - }) - return request(() => http(client).get(path, options).json()) + const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}` + return request(() => + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getAddonResponseWire, data) + } + return fromWire(data, schemas.getAddonResponse) + }), + ) } export function deleteAddon( @@ -79,9 +118,7 @@ export function deleteAddon( req: DeleteAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/addons/{addonId}', { - addonId: req.addonId, - }) + const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}` return request(async () => { await http(client).delete(path, options) }) @@ -92,11 +129,17 @@ export function archiveAddon( req: ArchiveAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/addons/{addonId}/archive', { - addonId: req.addonId, - }) + const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}/archive` return request(() => - http(client).post(path, options).json(), + http(client) + .post(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.archiveAddonResponseWire, data) + } + return fromWire(data, schemas.archiveAddonResponse) + }), ) } @@ -105,10 +148,16 @@ export function publishAddon( req: PublishAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/addons/{addonId}/publish', { - addonId: req.addonId, - }) + const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}/publish` return request(() => - http(client).post(path, options).json(), + http(client) + .post(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.publishAddonResponseWire, data) + } + return fromWire(data, schemas.publishAddonResponse) + }), ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/apps.ts b/api/spec/packages/aip-client-javascript/src/funcs/apps.ts index c164e34247..5178805ba8 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/apps.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/apps.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListAppsRequest, ListAppsResponse, @@ -14,13 +16,24 @@ export function listApps( req: ListAppsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + }, + schemas.listAppsQueryParams, + ), + ) return request(() => http(client) .get('openmeter/apps', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listAppsResponseWire, data) + } + return fromWire(data, schemas.listAppsResponse) + }), ) } @@ -29,6 +42,16 @@ export function getApp( req: GetAppRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/apps/{appId}', { appId: req.appId }) - return request(() => http(client).get(path, options).json()) + const path = `openmeter/apps/${encodeURIComponent(String(req.appId))}` + return request(() => + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getAppResponseWire, data) + } + return fromWire(data, schemas.getAppResponse) + }), + ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/billing.ts b/api/spec/packages/aip-client-javascript/src/funcs/billing.ts index 2e19bfa773..f64f3c91e8 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/billing.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/billing.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListBillingProfilesRequest, ListBillingProfilesResponse, @@ -20,13 +22,24 @@ export function listBillingProfiles( req: ListBillingProfilesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + }, + schemas.listBillingProfilesQueryParams, + ), + ) return request(() => http(client) .get('openmeter/profiles', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listBillingProfilesResponseWire, data) + } + return fromWire(data, schemas.listBillingProfilesResponse) + }), ) } @@ -35,11 +48,21 @@ export function createBillingProfile( req: CreateBillingProfileRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/profiles', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createBillingProfileBody) + if (client._options.validate) { + assertValid(schemas.createBillingProfileBodyWire, body) + } + return http(client) + .post('openmeter/profiles', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createBillingProfileResponseWire, data) + } + return fromWire(data, schemas.createBillingProfileResponse) + }) + }) } export function getBillingProfile( @@ -47,9 +70,17 @@ export function getBillingProfile( req: GetBillingProfileRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/profiles/{id}', { id: req.id }) + const path = `openmeter/profiles/${encodeURIComponent(String(req.id))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getBillingProfileResponseWire, data) + } + return fromWire(data, schemas.getBillingProfileResponse) + }), ) } @@ -58,12 +89,22 @@ export function updateBillingProfile( req: UpdateBillingProfileRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/profiles/{id}', { id: req.id }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) + const path = `openmeter/profiles/${encodeURIComponent(String(req.id))}` + return request(() => { + const body = toWire(req.body, schemas.updateBillingProfileBody) + if (client._options.validate) { + assertValid(schemas.updateBillingProfileBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updateBillingProfileResponseWire, data) + } + return fromWire(data, schemas.updateBillingProfileResponse) + }) + }) } export function deleteBillingProfile( @@ -71,7 +112,7 @@ export function deleteBillingProfile( req: DeleteBillingProfileRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/profiles/{id}', { id: req.id }) + const path = `openmeter/profiles/${encodeURIComponent(String(req.id))}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts b/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts index c03af41ac8..6411d7b571 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListCurrenciesRequest, ListCurrenciesResponse, @@ -18,15 +20,26 @@ export function listCurrencies( req: ListCurrenciesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + }, + schemas.listCurrenciesQueryParams, + ), + ) return request(() => http(client) .get('openmeter/currencies', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listCurrenciesResponseWire, data) + } + return fromWire(data, schemas.listCurrenciesResponse) + }), ) } @@ -35,11 +48,21 @@ export function createCustomCurrency( req: CreateCustomCurrencyRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/currencies/custom', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createCustomCurrencyBody) + if (client._options.validate) { + assertValid(schemas.createCustomCurrencyBodyWire, body) + } + return http(client) + .post('openmeter/currencies/custom', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createCustomCurrencyResponseWire, data) + } + return fromWire(data, schemas.createCustomCurrencyResponse) + }) + }) } export function listCostBases( @@ -47,18 +70,26 @@ export function listCostBases( req: ListCostBasesRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - filter: req.filter, - page: req.page, - }) - const path = encodePath( - 'openmeter/currencies/custom/{currencyId}/cost-bases', - { currencyId: req.currencyId }, + const searchParams = toURLSearchParams( + toWire( + { + filter: req.filter, + page: req.page, + }, + schemas.listCostBasesQueryParams, + ), ) + const path = `openmeter/currencies/custom/${encodeURIComponent(String(req.currencyId))}/cost-bases` return request(() => http(client) .get(path, { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listCostBasesResponseWire, data) + } + return fromWire(data, schemas.listCostBasesResponse) + }), ) } @@ -67,13 +98,20 @@ export function createCostBasis( req: CreateCostBasisRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/currencies/custom/{currencyId}/cost-bases', - { currencyId: req.currencyId }, - ) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) + const path = `openmeter/currencies/custom/${encodeURIComponent(String(req.currencyId))}/cost-bases` + return request(() => { + const body = toWire(req.body, schemas.createCostBasisBody) + if (client._options.validate) { + assertValid(schemas.createCostBasisBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createCostBasisResponseWire, data) + } + return fromWire(data, schemas.createCostBasisResponse) + }) + }) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts index 51fb2666a7..e7c4db71ec 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { CreateCustomerRequest, CreateCustomerResponse, @@ -48,11 +50,21 @@ export function createCustomer( req: CreateCustomerRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/customers', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createCustomerBody) + if (client._options.validate) { + assertValid(schemas.createCustomerBodyWire, body) + } + return http(client) + .post('openmeter/customers', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createCustomerResponseWire, data) + } + return fromWire(data, schemas.createCustomerResponse) + }) + }) } export function getCustomer( @@ -60,11 +72,17 @@ export function getCustomer( req: GetCustomerRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}', { - customerId: req.customerId, - }) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getCustomerResponseWire, data) + } + return fromWire(data, schemas.getCustomerResponse) + }), ) } @@ -73,15 +91,26 @@ export function listCustomers( req: ListCustomersRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + }, + schemas.listCustomersQueryParams, + ), + ) return request(() => http(client) .get('openmeter/customers', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listCustomersResponseWire, data) + } + return fromWire(data, schemas.listCustomersResponse) + }), ) } @@ -90,14 +119,22 @@ export function upsertCustomer( req: UpsertCustomerRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}', { - customerId: req.customerId, + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}` + return request(() => { + const body = toWire(req.body, schemas.upsertCustomerBody) + if (client._options.validate) { + assertValid(schemas.upsertCustomerBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.upsertCustomerResponseWire, data) + } + return fromWire(data, schemas.upsertCustomerResponse) + }) }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) } export function deleteCustomer( @@ -105,9 +142,7 @@ export function deleteCustomer( req: DeleteCustomerRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}', { - customerId: req.customerId, - }) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}` return request(async () => { await http(client).delete(path, options) }) @@ -118,11 +153,17 @@ export function getCustomerBilling( req: GetCustomerBillingRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}/billing', { - customerId: req.customerId, - }) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getCustomerBillingResponseWire, data) + } + return fromWire(data, schemas.getCustomerBillingResponse) + }), ) } @@ -131,14 +172,22 @@ export function updateCustomerBilling( req: UpdateCustomerBillingRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}/billing', { - customerId: req.customerId, + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing` + return request(() => { + const body = toWire(req.body, schemas.updateCustomerBillingBody) + if (client._options.validate) { + assertValid(schemas.updateCustomerBillingBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updateCustomerBillingResponseWire, data) + } + return fromWire(data, schemas.updateCustomerBillingResponse) + }) }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) } export function updateCustomerBillingAppData( @@ -146,14 +195,22 @@ export function updateCustomerBillingAppData( req: UpdateCustomerBillingAppDataRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}/billing/app-data', { - customerId: req.customerId, + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing/app-data` + return request(() => { + const body = toWire(req.body, schemas.updateCustomerBillingAppDataBody) + if (client._options.validate) { + assertValid(schemas.updateCustomerBillingAppDataBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updateCustomerBillingAppDataResponseWire, data) + } + return fromWire(data, schemas.updateCustomerBillingAppDataResponse) + }) }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) } export function createCustomerStripeCheckoutSession( @@ -161,15 +218,31 @@ export function createCustomerStripeCheckoutSession( req: CreateCustomerStripeCheckoutSessionRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/customers/{customerId}/billing/stripe/checkout-sessions', - { customerId: req.customerId }, - ) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing/stripe/checkout-sessions` + return request(() => { + const body = toWire( + req.body, + schemas.createCustomerStripeCheckoutSessionBody, + ) + if (client._options.validate) { + assertValid(schemas.createCustomerStripeCheckoutSessionBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid( + schemas.createCustomerStripeCheckoutSessionResponseWire, + data, + ) + } + return fromWire( + data, + schemas.createCustomerStripeCheckoutSessionResponse, + ) + }) + }) } export function createCustomerStripePortalSession( @@ -177,15 +250,25 @@ export function createCustomerStripePortalSession( req: CreateCustomerStripePortalSessionRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/customers/{customerId}/billing/stripe/portal-sessions', - { customerId: req.customerId }, - ) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing/stripe/portal-sessions` + return request(() => { + const body = toWire(req.body, schemas.createCustomerStripePortalSessionBody) + if (client._options.validate) { + assertValid(schemas.createCustomerStripePortalSessionBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid( + schemas.createCustomerStripePortalSessionResponseWire, + data, + ) + } + return fromWire(data, schemas.createCustomerStripePortalSessionResponse) + }) + }) } export function createCreditGrant( @@ -193,14 +276,22 @@ export function createCreditGrant( req: CreateCreditGrantRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}/credits/grants', { - customerId: req.customerId, + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants` + return request(() => { + const body = toWire(req.body, schemas.createCreditGrantBody) + if (client._options.validate) { + assertValid(schemas.createCreditGrantBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createCreditGrantResponseWire, data) + } + return fromWire(data, schemas.createCreditGrantResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } export function getCreditGrant( @@ -208,12 +299,17 @@ export function getCreditGrant( req: GetCreditGrantRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/customers/{customerId}/credits/grants/{creditGrantId}', - { customerId: req.customerId, creditGrantId: req.creditGrantId }, - ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants/${encodeURIComponent(String(req.creditGrantId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getCreditGrantResponseWire, data) + } + return fromWire(data, schemas.getCreditGrantResponse) + }), ) } @@ -222,17 +318,26 @@ export function listCreditGrants( req: ListCreditGrantsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - filter: req.filter, - }) - const path = encodePath('openmeter/customers/{customerId}/credits/grants', { - customerId: req.customerId, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + filter: req.filter, + }, + schemas.listCreditGrantsQueryParams, + ), + ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants` return request(() => http(client) .get(path, { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listCreditGrantsResponseWire, data) + } + return fromWire(data, schemas.listCreditGrantsResponse) + }), ) } @@ -241,17 +346,26 @@ export function getCustomerCreditBalance( req: GetCustomerCreditBalanceRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - timestamp: req.timestamp, - filter: req.filter, - }) - const path = encodePath('openmeter/customers/{customerId}/credits/balance', { - customerId: req.customerId, - }) + const searchParams = toURLSearchParams( + toWire( + { + timestamp: req.timestamp, + filter: req.filter, + }, + schemas.getCustomerCreditBalanceQueryParams, + ), + ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/balance` return request(() => http(client) .get(path, { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getCustomerCreditBalanceResponseWire, data) + } + return fromWire(data, schemas.getCustomerCreditBalanceResponse) + }), ) } @@ -260,15 +374,22 @@ export function createCreditAdjustment( req: CreateCreditAdjustmentRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/customers/{customerId}/credits/adjustments', - { customerId: req.customerId }, - ) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/adjustments` + return request(() => { + const body = toWire(req.body, schemas.createCreditAdjustmentBody) + if (client._options.validate) { + assertValid(schemas.createCreditAdjustmentBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createCreditAdjustmentResponseWire, data) + } + return fromWire(data, schemas.createCreditAdjustmentResponse) + }) + }) } export function updateCreditGrantExternalSettlement( @@ -276,15 +397,31 @@ export function updateCreditGrantExternalSettlement( req: UpdateCreditGrantExternalSettlementRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/customers/{customerId}/credits/grants/{creditGrantId}/settlement/external', - { customerId: req.customerId, creditGrantId: req.creditGrantId }, - ) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants/${encodeURIComponent(String(req.creditGrantId))}/settlement/external` + return request(() => { + const body = toWire( + req.body, + schemas.updateCreditGrantExternalSettlementBody, + ) + if (client._options.validate) { + assertValid(schemas.updateCreditGrantExternalSettlementBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid( + schemas.updateCreditGrantExternalSettlementResponseWire, + data, + ) + } + return fromWire( + data, + schemas.updateCreditGrantExternalSettlementResponse, + ) + }) + }) } export function listCreditTransactions( @@ -292,18 +429,26 @@ export function listCreditTransactions( req: ListCreditTransactionsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - filter: req.filter, - }) - const path = encodePath( - 'openmeter/customers/{customerId}/credits/transactions', - { customerId: req.customerId }, + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + filter: req.filter, + }, + schemas.listCreditTransactionsQueryParams, + ), ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/transactions` return request(() => http(client) .get(path, { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listCreditTransactionsResponseWire, data) + } + return fromWire(data, schemas.listCreditTransactionsResponse) + }), ) } @@ -312,19 +457,28 @@ export function listCustomerCharges( req: ListCustomerChargesRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - expand: req.expand, - }) - const path = encodePath('openmeter/customers/{customerId}/charges', { - customerId: req.customerId, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + expand: req.expand, + }, + schemas.listCustomerChargesQueryParams, + ), + ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/charges` return request(() => http(client) .get(path, { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listCustomerChargesResponseWire, data) + } + return fromWire(data, schemas.listCustomerChargesResponse) + }), ) } @@ -333,12 +487,20 @@ export function createCustomerCharges( req: CreateCustomerChargesRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/customers/{customerId}/charges', { - customerId: req.customerId, + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/charges` + return request(() => { + const body = toWire(req.body, schemas.createCustomerChargesBody) + if (client._options.validate) { + assertValid(schemas.createCustomerChargesBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createCustomerChargesResponseWire, data) + } + return fromWire(data, schemas.createCustomerChargesResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/defaults.ts b/api/spec/packages/aip-client-javascript/src/funcs/defaults.ts index 78e129cf84..b61b13d19d 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/defaults.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/defaults.ts @@ -1,7 +1,8 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { GetOrganizationDefaultTaxCodesRequest, GetOrganizationDefaultTaxCodesResponse, @@ -17,7 +18,13 @@ export function getOrganizationDefaultTaxCodes( return request(() => http(client) .get('openmeter/defaults/tax-codes', options) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getOrganizationDefaultTaxCodesResponseWire, data) + } + return fromWire(data, schemas.getOrganizationDefaultTaxCodesResponse) + }), ) } @@ -26,9 +33,22 @@ export function updateOrganizationDefaultTaxCodes( req: UpdateOrganizationDefaultTaxCodesRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .put('openmeter/defaults/tax-codes', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.updateOrganizationDefaultTaxCodesBody) + if (client._options.validate) { + assertValid(schemas.updateOrganizationDefaultTaxCodesBodyWire, body) + } + return http(client) + .put('openmeter/defaults/tax-codes', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid( + schemas.updateOrganizationDefaultTaxCodesResponseWire, + data, + ) + } + return fromWire(data, schemas.updateOrganizationDefaultTaxCodesResponse) + }) + }) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts b/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts index 73cee46cc0..e67f53e92f 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts @@ -1,7 +1,8 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListCustomerEntitlementAccessRequest, ListCustomerEntitlementAccessResponse, @@ -12,13 +13,16 @@ export function listCustomerEntitlementAccess( req: ListCustomerEntitlementAccessRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/customers/{customerId}/entitlement-access', - { customerId: req.customerId }, - ) + const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/entitlement-access` return request(() => http(client) .get(path, options) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listCustomerEntitlementAccessResponseWire, data) + } + return fromWire(data, schemas.listCustomerEntitlementAccessResponse) + }), ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/events.ts b/api/spec/packages/aip-client-javascript/src/funcs/events.ts index 3ce55bd39e..ffde9dc695 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/events.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/events.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListMeteringEventsRequest, ListMeteringEventsResponse, @@ -14,15 +16,26 @@ export function listMeteringEvents( req: ListMeteringEventsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - filter: req.filter, - sort: encodeSort(req.sort), - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + filter: req.filter, + sort: encodeSort(req.sort, toSnakeCase), + }, + schemas.listMeteringEventsQueryParams, + ), + ) return request(() => http(client) .get('openmeter/events', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listMeteringEventsResponseWire, data) + } + return fromWire(data, schemas.listMeteringEventsResponse) + }), ) } @@ -32,6 +45,10 @@ export function ingestMeteringEvents( options?: RequestOptions, ): Promise> { return request(async () => { - await http(client).post('openmeter/events', { ...options, json: req }) + const body = toWire(req, schemas.ingestMeteringEventsBody) + if (client._options.validate) { + assertValid(schemas.ingestMeteringEventsBodyWire, body) + } + await http(client).post('openmeter/events', { ...options, json: body }) }) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/features.ts b/api/spec/packages/aip-client-javascript/src/funcs/features.ts index ef00c62a66..cdeea7cec0 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/features.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/features.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListFeaturesRequest, ListFeaturesResponse, @@ -22,15 +24,26 @@ export function listFeatures( req: ListFeaturesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + }, + schemas.listFeaturesQueryParams, + ), + ) return request(() => http(client) .get('openmeter/features', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listFeaturesResponseWire, data) + } + return fromWire(data, schemas.listFeaturesResponse) + }), ) } @@ -39,11 +52,21 @@ export function createFeature( req: CreateFeatureRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/features', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createFeatureBody) + if (client._options.validate) { + assertValid(schemas.createFeatureBodyWire, body) + } + return http(client) + .post('openmeter/features', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createFeatureResponseWire, data) + } + return fromWire(data, schemas.createFeatureResponse) + }) + }) } export function getFeature( @@ -51,11 +74,17 @@ export function getFeature( req: GetFeatureRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/features/{featureId}', { - featureId: req.featureId, - }) + const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getFeatureResponseWire, data) + } + return fromWire(data, schemas.getFeatureResponse) + }), ) } @@ -64,14 +93,22 @@ export function updateFeature( req: UpdateFeatureRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/features/{featureId}', { - featureId: req.featureId, + const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}` + return request(() => { + const body = toWire(req.body, schemas.updateFeatureBody) + if (client._options.validate) { + assertValid(schemas.updateFeatureBodyWire, body) + } + return http(client) + .patch(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updateFeatureResponseWire, data) + } + return fromWire(data, schemas.updateFeatureResponse) + }) }) - return request(() => - http(client) - .patch(path, { ...options, json: req.body }) - .json(), - ) } export function deleteFeature( @@ -79,9 +116,7 @@ export function deleteFeature( req: DeleteFeatureRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/features/{featureId}', { - featureId: req.featureId, - }) + const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}` return request(async () => { await http(client).delete(path, options) }) @@ -92,12 +127,20 @@ export function queryFeatureCost( req: QueryFeatureCostRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/features/{featureId}/cost/query', { - featureId: req.featureId, + const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}/cost/query` + return request(() => { + const body = toWire(req.body, schemas.queryFeatureCostBody) + if (client._options.validate) { + assertValid(schemas.queryFeatureCostBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.queryFeatureCostResponseWire, data) + } + return fromWire(data, schemas.queryFeatureCostResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/governance.ts b/api/spec/packages/aip-client-javascript/src/funcs/governance.ts index 86866589d7..42b2354a7c 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/governance.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/governance.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { QueryGovernanceAccessRequest, QueryGovernanceAccessResponse, @@ -12,16 +14,31 @@ export function queryGovernanceAccess( req: QueryGovernanceAccessRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - }) - return request(() => - http(client) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + }, + schemas.queryGovernanceAccessQueryParams, + ), + ) + return request(() => { + const body = toWire(req.body, schemas.queryGovernanceAccessBody) + if (client._options.validate) { + assertValid(schemas.queryGovernanceAccessBodyWire, body) + } + return http(client) .post('openmeter/governance/query', { ...options, searchParams, - json: req.body, + json: body, }) - .json(), - ) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.queryGovernanceAccessResponseWire, data) + } + return fromWire(data, schemas.queryGovernanceAccessResponse) + }) + }) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts b/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts index 88e4f13b7e..ffd21a0244 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts @@ -1,7 +1,8 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { GetInvoiceRequest, GetInvoiceResponse, @@ -12,10 +13,16 @@ export function getInvoice( req: GetInvoiceRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/billing/invoices/{invoiceId}', { - invoiceId: req.invoiceId, - }) + const path = `openmeter/billing/invoices/${encodeURIComponent(String(req.invoiceId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getInvoiceResponseWire, data) + } + return fromWire(data, schemas.getInvoiceResponse) + }), ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts b/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts index 32548f2f5d..cef8d8ba02 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListLlmCostPricesRequest, ListLlmCostPricesResponse, @@ -20,15 +22,26 @@ export function listLlmCostPrices( req: ListLlmCostPricesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - filter: req.filter, - sort: encodeSort(req.sort), - page: req.page, - }) + const searchParams = toURLSearchParams( + toWire( + { + filter: req.filter, + sort: encodeSort(req.sort, toSnakeCase), + page: req.page, + }, + schemas.listLlmCostPricesQueryParams, + ), + ) return request(() => http(client) .get('openmeter/llm-cost/prices', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listLlmCostPricesResponseWire, data) + } + return fromWire(data, schemas.listLlmCostPricesResponse) + }), ) } @@ -37,11 +50,17 @@ export function getLlmCostPrice( req: GetLlmCostPriceRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/llm-cost/prices/{priceId}', { - priceId: req.priceId, - }) + const path = `openmeter/llm-cost/prices/${encodeURIComponent(String(req.priceId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getLlmCostPriceResponseWire, data) + } + return fromWire(data, schemas.getLlmCostPriceResponse) + }), ) } @@ -50,14 +69,25 @@ export function listLlmCostOverrides( req: ListLlmCostOverridesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - filter: req.filter, - page: req.page, - }) + const searchParams = toURLSearchParams( + toWire( + { + filter: req.filter, + page: req.page, + }, + schemas.listLlmCostOverridesQueryParams, + ), + ) return request(() => http(client) .get('openmeter/llm-cost/overrides', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listLlmCostOverridesResponseWire, data) + } + return fromWire(data, schemas.listLlmCostOverridesResponse) + }), ) } @@ -66,11 +96,21 @@ export function createLlmCostOverride( req: CreateLlmCostOverrideRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/llm-cost/overrides', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createLlmCostOverrideBody) + if (client._options.validate) { + assertValid(schemas.createLlmCostOverrideBodyWire, body) + } + return http(client) + .post('openmeter/llm-cost/overrides', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createLlmCostOverrideResponseWire, data) + } + return fromWire(data, schemas.createLlmCostOverrideResponse) + }) + }) } export function deleteLlmCostOverride( @@ -78,9 +118,7 @@ export function deleteLlmCostOverride( req: DeleteLlmCostOverrideRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/llm-cost/overrides/{priceId}', { - priceId: req.priceId, - }) + const path = `openmeter/llm-cost/overrides/${encodeURIComponent(String(req.priceId))}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/meters.ts b/api/spec/packages/aip-client-javascript/src/funcs/meters.ts index 59476a7bd3..3a42187132 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/meters.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/meters.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { CreateMeterRequest, CreateMeterResponse, @@ -24,11 +26,21 @@ export function createMeter( req: CreateMeterRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/meters', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createMeterBody) + if (client._options.validate) { + assertValid(schemas.createMeterBodyWire, body) + } + return http(client) + .post('openmeter/meters', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createMeterResponseWire, data) + } + return fromWire(data, schemas.createMeterResponse) + }) + }) } export function getMeter( @@ -36,10 +48,18 @@ export function getMeter( req: GetMeterRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/meters/{meterId}', { - meterId: req.meterId, - }) - return request(() => http(client).get(path, options).json()) + const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}` + return request(() => + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getMeterResponseWire, data) + } + return fromWire(data, schemas.getMeterResponse) + }), + ) } export function listMeters( @@ -47,15 +67,26 @@ export function listMeters( req: ListMetersRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + }, + schemas.listMetersQueryParams, + ), + ) return request(() => http(client) .get('openmeter/meters', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listMetersResponseWire, data) + } + return fromWire(data, schemas.listMetersResponse) + }), ) } @@ -64,14 +95,22 @@ export function updateMeter( req: UpdateMeterRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/meters/{meterId}', { - meterId: req.meterId, + const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}` + return request(() => { + const body = toWire(req.body, schemas.updateMeterBody) + if (client._options.validate) { + assertValid(schemas.updateMeterBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updateMeterResponseWire, data) + } + return fromWire(data, schemas.updateMeterResponse) + }) }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) } export function deleteMeter( @@ -79,9 +118,7 @@ export function deleteMeter( req: DeleteMeterRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/meters/{meterId}', { - meterId: req.meterId, - }) + const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}` return request(async () => { await http(client).delete(path, options) }) @@ -92,14 +129,22 @@ export function queryMeter( req: QueryMeterRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/meters/{meterId}/query', { - meterId: req.meterId, + const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}/query` + return request(() => { + const body = toWire(req.body, schemas.queryMeterBody) + if (client._options.validate) { + assertValid(schemas.queryMeterBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.queryMeterResponseWire, data) + } + return fromWire(data, schemas.queryMeterResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } export function queryMeterCsv( @@ -109,12 +154,14 @@ export function queryMeterCsv( ): Promise> { const headers = new Headers(options?.headers as HeadersInit | undefined) headers.set('accept', 'text/csv') - const path = encodePath('openmeter/meters/{meterId}/query', { - meterId: req.meterId, + const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}/query` + return request(() => { + const body = toWire(req.body, schemas.queryMeterCsvBody) + if (client._options.validate) { + assertValid(schemas.queryMeterCsvBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body, headers }) + .text() }) - return request(() => - http(client) - .post(path, { ...options, json: req.body, headers }) - .text(), - ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts b/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts index 3bdbdf8f89..d2bff19ead 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListPlanAddonsRequest, ListPlanAddonsResponse, @@ -20,16 +22,25 @@ export function listPlanAddons( req: ListPlanAddonsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - }) - const path = encodePath('openmeter/plans/{planId}/addons', { - planId: req.planId, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + }, + schemas.listPlanAddonsQueryParams, + ), + ) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons` return request(() => http(client) .get(path, { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listPlanAddonsResponseWire, data) + } + return fromWire(data, schemas.listPlanAddonsResponse) + }), ) } @@ -38,14 +49,22 @@ export function createPlanAddon( req: CreatePlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}/addons', { - planId: req.planId, + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons` + return request(() => { + const body = toWire(req.body, schemas.createPlanAddonBody) + if (client._options.validate) { + assertValid(schemas.createPlanAddonBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createPlanAddonResponseWire, data) + } + return fromWire(data, schemas.createPlanAddonResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } export function getPlanAddon( @@ -53,12 +72,17 @@ export function getPlanAddon( req: GetPlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}/addons/{planAddonId}', { - planId: req.planId, - planAddonId: req.planAddonId, - }) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons/${encodeURIComponent(String(req.planAddonId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getPlanAddonResponseWire, data) + } + return fromWire(data, schemas.getPlanAddonResponse) + }), ) } @@ -67,15 +91,22 @@ export function updatePlanAddon( req: UpdatePlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}/addons/{planAddonId}', { - planId: req.planId, - planAddonId: req.planAddonId, + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons/${encodeURIComponent(String(req.planAddonId))}` + return request(() => { + const body = toWire(req.body, schemas.updatePlanAddonBody) + if (client._options.validate) { + assertValid(schemas.updatePlanAddonBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updatePlanAddonResponseWire, data) + } + return fromWire(data, schemas.updatePlanAddonResponse) + }) }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) } export function deletePlanAddon( @@ -83,10 +114,7 @@ export function deletePlanAddon( req: DeletePlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}/addons/{planAddonId}', { - planId: req.planId, - planAddonId: req.planAddonId, - }) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons/${encodeURIComponent(String(req.planAddonId))}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/plans.ts b/api/spec/packages/aip-client-javascript/src/funcs/plans.ts index 49090224c9..7bd75f381d 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/plans.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/plans.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { ListPlansRequest, ListPlansResponse, @@ -24,15 +26,26 @@ export function listPlans( req: ListPlansRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + }, + schemas.listPlansQueryParams, + ), + ) return request(() => http(client) .get('openmeter/plans', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listPlansResponseWire, data) + } + return fromWire(data, schemas.listPlansResponse) + }), ) } @@ -41,11 +54,21 @@ export function createPlan( req: CreatePlanRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/plans', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createPlanBody) + if (client._options.validate) { + assertValid(schemas.createPlanBodyWire, body) + } + return http(client) + .post('openmeter/plans', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createPlanResponseWire, data) + } + return fromWire(data, schemas.createPlanResponse) + }) + }) } export function updatePlan( @@ -53,12 +76,22 @@ export function updatePlan( req: UpdatePlanRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}', { planId: req.planId }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}` + return request(() => { + const body = toWire(req.body, schemas.updatePlanBody) + if (client._options.validate) { + assertValid(schemas.updatePlanBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.updatePlanResponseWire, data) + } + return fromWire(data, schemas.updatePlanResponse) + }) + }) } export function getPlan( @@ -66,8 +99,18 @@ export function getPlan( req: GetPlanRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}', { planId: req.planId }) - return request(() => http(client).get(path, options).json()) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}` + return request(() => + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getPlanResponseWire, data) + } + return fromWire(data, schemas.getPlanResponse) + }), + ) } export function deletePlan( @@ -75,7 +118,7 @@ export function deletePlan( req: DeletePlanRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}', { planId: req.planId }) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}` return request(async () => { await http(client).delete(path, options) }) @@ -86,11 +129,17 @@ export function archivePlan( req: ArchivePlanRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}/archive', { - planId: req.planId, - }) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/archive` return request(() => - http(client).post(path, options).json(), + http(client) + .post(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.archivePlanResponseWire, data) + } + return fromWire(data, schemas.archivePlanResponse) + }), ) } @@ -99,10 +148,16 @@ export function publishPlan( req: PublishPlanRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/plans/{planId}/publish', { - planId: req.planId, - }) + const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/publish` return request(() => - http(client).post(path, options).json(), + http(client) + .post(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.publishPlanResponseWire, data) + } + return fromWire(data, schemas.publishPlanResponse) + }), ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts b/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts index 5dc2bccf02..119ad380c4 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid, toSnakeCase } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { CreateSubscriptionRequest, CreateSubscriptionResponse, @@ -28,11 +30,21 @@ export function createSubscription( req: CreateSubscriptionRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/subscriptions', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createSubscriptionBody) + if (client._options.validate) { + assertValid(schemas.createSubscriptionBodyWire, body) + } + return http(client) + .post('openmeter/subscriptions', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createSubscriptionResponseWire, data) + } + return fromWire(data, schemas.createSubscriptionResponse) + }) + }) } export function listSubscriptions( @@ -40,15 +52,26 @@ export function listSubscriptions( req: ListSubscriptionsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - filter: req.filter, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + filter: req.filter, + }, + schemas.listSubscriptionsQueryParams, + ), + ) return request(() => http(client) .get('openmeter/subscriptions', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listSubscriptionsResponseWire, data) + } + return fromWire(data, schemas.listSubscriptionsResponse) + }), ) } @@ -57,11 +80,17 @@ export function getSubscription( req: GetSubscriptionRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/subscriptions/{subscriptionId}', { - subscriptionId: req.subscriptionId, - }) + const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getSubscriptionResponseWire, data) + } + return fromWire(data, schemas.getSubscriptionResponse) + }), ) } @@ -70,14 +99,22 @@ export function cancelSubscription( req: CancelSubscriptionRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/subscriptions/{subscriptionId}/cancel', { - subscriptionId: req.subscriptionId, + const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/cancel` + return request(() => { + const body = toWire(req.body, schemas.cancelSubscriptionBody) + if (client._options.validate) { + assertValid(schemas.cancelSubscriptionBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.cancelSubscriptionResponseWire, data) + } + return fromWire(data, schemas.cancelSubscriptionResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } export function unscheduleCancelation( @@ -85,12 +122,17 @@ export function unscheduleCancelation( req: UnscheduleCancelationRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/subscriptions/{subscriptionId}/unschedule-cancelation', - { subscriptionId: req.subscriptionId }, - ) + const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/unschedule-cancelation` return request(() => - http(client).post(path, options).json(), + http(client) + .post(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.unscheduleCancelationResponseWire, data) + } + return fromWire(data, schemas.unscheduleCancelationResponse) + }), ) } @@ -99,14 +141,22 @@ export function changeSubscription( req: ChangeSubscriptionRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/subscriptions/{subscriptionId}/change', { - subscriptionId: req.subscriptionId, + const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/change` + return request(() => { + const body = toWire(req.body, schemas.changeSubscriptionBody) + if (client._options.validate) { + assertValid(schemas.changeSubscriptionBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.changeSubscriptionResponseWire, data) + } + return fromWire(data, schemas.changeSubscriptionResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } export function createSubscriptionAddon( @@ -114,14 +164,22 @@ export function createSubscriptionAddon( req: CreateSubscriptionAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/subscriptions/{subscriptionId}/addons', { - subscriptionId: req.subscriptionId, + const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/addons` + return request(() => { + const body = toWire(req.body, schemas.createSubscriptionAddonBody) + if (client._options.validate) { + assertValid(schemas.createSubscriptionAddonBodyWire, body) + } + return http(client) + .post(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createSubscriptionAddonResponseWire, data) + } + return fromWire(data, schemas.createSubscriptionAddonResponse) + }) }) - return request(() => - http(client) - .post(path, { ...options, json: req.body }) - .json(), - ) } export function listSubscriptionAddons( @@ -129,17 +187,26 @@ export function listSubscriptionAddons( req: ListSubscriptionAddonsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - sort: encodeSort(req.sort), - }) - const path = encodePath('openmeter/subscriptions/{subscriptionId}/addons', { - subscriptionId: req.subscriptionId, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + }, + schemas.listSubscriptionAddonsQueryParams, + ), + ) + const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/addons` return request(() => http(client) .get(path, { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listSubscriptionAddonsResponseWire, data) + } + return fromWire(data, schemas.listSubscriptionAddonsResponse) + }), ) } @@ -148,14 +215,16 @@ export function getSubscriptionAddon( req: GetSubscriptionAddonRequest, options?: RequestOptions, ): Promise> { - const path = encodePath( - 'openmeter/subscriptions/{subscriptionId}/addons/{subscriptionAddonId}', - { - subscriptionId: req.subscriptionId, - subscriptionAddonId: req.subscriptionAddonId, - }, - ) + const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/addons/${encodeURIComponent(String(req.subscriptionAddonId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getSubscriptionAddonResponseWire, data) + } + return fromWire(data, schemas.getSubscriptionAddonResponse) + }), ) } diff --git a/api/spec/packages/aip-client-javascript/src/funcs/tax.ts b/api/spec/packages/aip-client-javascript/src/funcs/tax.ts index 1ac00010a8..382b4a1a53 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/tax.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/tax.ts @@ -1,7 +1,9 @@ import { type Client, http } from '../core.js' import { type Result, type RequestOptions } from '../lib/types.js' import { request } from '../lib/request.js' -import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toURLSearchParams, encodeSort } from '../lib/encodings.js' +import { toWire, fromWire, assertValid } from '../lib/wire.js' +import * as schemas from '../models/schemas.js' import type { CreateTaxCodeRequest, CreateTaxCodeResponse, @@ -20,11 +22,21 @@ export function createTaxCode( req: CreateTaxCodeRequest, options?: RequestOptions, ): Promise> { - return request(() => - http(client) - .post('openmeter/tax-codes', { ...options, json: req }) - .json(), - ) + return request(() => { + const body = toWire(req, schemas.createTaxCodeBody) + if (client._options.validate) { + assertValid(schemas.createTaxCodeBodyWire, body) + } + return http(client) + .post('openmeter/tax-codes', { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.createTaxCodeResponseWire, data) + } + return fromWire(data, schemas.createTaxCodeResponse) + }) + }) } export function getTaxCode( @@ -32,11 +44,17 @@ export function getTaxCode( req: GetTaxCodeRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/tax-codes/{taxCodeId}', { - taxCodeId: req.taxCodeId, - }) + const path = `openmeter/tax-codes/${encodeURIComponent(String(req.taxCodeId))}` return request(() => - http(client).get(path, options).json(), + http(client) + .get(path, options) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.getTaxCodeResponseWire, data) + } + return fromWire(data, schemas.getTaxCodeResponse) + }), ) } @@ -45,14 +63,25 @@ export function listTaxCodes( req: ListTaxCodesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams({ - page: req.page, - include_deleted: req.include_deleted, - }) + const searchParams = toURLSearchParams( + toWire( + { + page: req.page, + includeDeleted: req.includeDeleted, + }, + schemas.listTaxCodesQueryParams, + ), + ) return request(() => http(client) .get('openmeter/tax-codes', { ...options, searchParams }) - .json(), + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.listTaxCodesResponseWire, data) + } + return fromWire(data, schemas.listTaxCodesResponse) + }), ) } @@ -61,14 +90,22 @@ export function upsertTaxCode( req: UpsertTaxCodeRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/tax-codes/{taxCodeId}', { - taxCodeId: req.taxCodeId, + const path = `openmeter/tax-codes/${encodeURIComponent(String(req.taxCodeId))}` + return request(() => { + const body = toWire(req.body, schemas.upsertTaxCodeBody) + if (client._options.validate) { + assertValid(schemas.upsertTaxCodeBodyWire, body) + } + return http(client) + .put(path, { ...options, json: body }) + .json() + .then((data) => { + if (client._options.validate) { + assertValid(schemas.upsertTaxCodeResponseWire, data) + } + return fromWire(data, schemas.upsertTaxCodeResponse) + }) }) - return request(() => - http(client) - .put(path, { ...options, json: req.body }) - .json(), - ) } export function deleteTaxCode( @@ -76,9 +113,7 @@ export function deleteTaxCode( req: DeleteTaxCodeRequest, options?: RequestOptions, ): Promise> { - const path = encodePath('openmeter/tax-codes/{taxCodeId}', { - taxCodeId: req.taxCodeId, - }) + const path = `openmeter/tax-codes/${encodeURIComponent(String(req.taxCodeId))}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/index.ts b/api/spec/packages/aip-client-javascript/src/index.ts index d53c519d79..9f0313c375 100644 --- a/api/spec/packages/aip-client-javascript/src/index.ts +++ b/api/spec/packages/aip-client-javascript/src/index.ts @@ -18,6 +18,7 @@ export { Defaults } from './sdk/defaults.js' export { Governance } from './sdk/governance.js' export { Client } from './core.js' export { HTTPError } from './models/errors.js' +export { ValidationError } from './lib/wire.js' export { ServerList, Regions } from './lib/config.js' export type { SDKOptions, Region, ServerVariables } from './lib/config.js' diff --git a/api/spec/packages/aip-client-javascript/src/lib/config.ts b/api/spec/packages/aip-client-javascript/src/lib/config.ts index 571d9b7e22..02987e2eab 100644 --- a/api/spec/packages/aip-client-javascript/src/lib/config.ts +++ b/api/spec/packages/aip-client-javascript/src/lib/config.ts @@ -19,4 +19,11 @@ export interface SDKOptions extends Omit { baseUrl: (typeof ServerList)[number] | URL | string serverVariables?: ServerVariables apiKey?: string | (() => string | Promise) + /** + * Validate request bodies and response payloads against their schemas. Off by + * default: the SDK maps casing but does not validate, so additive server fields + * never break clients. When on, a request body or response that fails its schema + * (missing/wrong-typed field, unknown enum value) rejects with a ValidationError. + */ + validate?: boolean } diff --git a/api/spec/packages/aip-client-javascript/src/lib/encodings.ts b/api/spec/packages/aip-client-javascript/src/lib/encodings.ts index f051df138c..0c86120e27 100644 --- a/api/spec/packages/aip-client-javascript/src/lib/encodings.ts +++ b/api/spec/packages/aip-client-javascript/src/lib/encodings.ts @@ -59,15 +59,17 @@ export function toURLSearchParams( export function encodeSort( sort: { by?: string; order?: 'asc' | 'desc' } | undefined, + encodeField: (field: string) => string = (field) => field, ): string | undefined { if (!sort?.by) { return undefined } + const by = encodeField(sort.by) if (sort.order === 'desc') { - return `${sort.by} desc` + return `${by} desc` } if (sort.order === 'asc') { - return `${sort.by} asc` + return `${by} asc` } - return sort.by + return by } diff --git a/api/spec/packages/aip-client-javascript/src/lib/wire.ts b/api/spec/packages/aip-client-javascript/src/lib/wire.ts new file mode 100644 index 0000000000..3e24a49869 --- /dev/null +++ b/api/spec/packages/aip-client-javascript/src/lib/wire.ts @@ -0,0 +1,280 @@ +import type { output, ZodType } from 'zod' + +export function toCamelCase(name: string): string { + return name.replace(/_([a-z0-9])/g, (_m, c: string) => c.toUpperCase()) +} + +export function toSnakeCase(name: string): string { + return name.replace(/([A-Z])/g, (_m, c: string) => `_${c.toLowerCase()}`) +} + +type ZodDef = { + type: string + innerType?: ZodType + valueType?: ZodType + element?: ZodType + discriminator?: string + options?: ZodType[] +} + +function def(schema: ZodType | undefined): ZodDef | undefined { + return (schema as { def?: ZodDef } | undefined)?.def +} + +// Unwrap optional/nullable/default to the schema that describes the value's +// shape. These wrappers never change keys, so the walker looks through them +// before classifying a node. +function unwrap(schema: ZodType | undefined): ZodType | undefined { + let current = schema + for (let i = 0; i < 100 && current; i++) { + const d = def(current) + if ( + d && + (d.type === 'optional' || + d.type === 'nullable' || + d.type === 'default') && + d.innerType + ) { + current = d.innerType + continue + } + return current + } + /* v8 ignore next -- loop returns inside; reached only past the cycle guard */ + return current +} + +function shapeOf( + schema: ZodType | undefined, +): Record | undefined { + return (schema as { shape?: Record } | undefined)?.shape +} + +// The element schema for array data: the schema's own element when it is an +// array, or the array variant's element when it is a union of T and T[] +// (the single-or-batch body shape). +function arrayElement(schema: ZodType | undefined): ZodType | undefined { + const d = def(schema) + if (d?.type === 'array') { + return d.element + } + if (d?.type === 'union') { + for (const option of d.options ?? []) { + const od = def(unwrap(option)) + if (od?.type === 'array') { + return od.element + } + } + } + return undefined +} + +// Whether a value schema carries renamable fields (object/record/array/union of +// such), so a record value is recursed only when it is model-shaped. Scalars, +// literals, unknown, and any are left untouched. +function hasRenamableShape(schema: ZodType | undefined): boolean { + const s = unwrap(schema) + const d = def(s) + /* v8 ignore next 3 -- a record always has a value schema; defensive only */ + if (!d) { + return false + } + if (d.type === 'object' || d.type === 'record') { + return true + } + if (d.type === 'array') { + return hasRenamableShape(d.element) + } + if (d.type === 'union') { + return (d.options ?? []).some(hasRenamableShape) + } + return false +} + +type Direction = { + // The wire→public or public→wire key rename for object fields. + rename: (key: string) => string + // The data key holding a discriminated union's discriminator, given the + // schema's (camelCase) discriminator key. + discriminatorKey: (camelKey: string) => string +} + +function walk( + data: unknown, + schema: ZodType | undefined, + dir: Direction, +): unknown { + if (data === null || data === undefined) { + return data + } + const s = unwrap(schema) + const d = def(s) + if (Array.isArray(data)) { + // The schema may be the array itself or a union with an array variant + // (e.g. a single-or-batch body `T | T[]`); resolve the element schema from + // whichever applies so array items are still walked with their shape. + const element = arrayElement(s) + return data.map((item) => walk(item, element, dir)) + } + if (typeof data !== 'object') { + return data + } + const record = data as Record + + if (d?.type === 'record') { + // Record keys are user data (label/dimension names) — preserved verbatim. + // Only the value is walked, and only when it is model-shaped. + const valueSchema = hasRenamableShape(d.valueType) ? d.valueType : undefined + const out: Record = {} + for (const [key, value] of Object.entries(record)) { + out[key] = valueSchema ? walk(value, valueSchema, dir) : value + } + return out + } + + if (d?.type === 'union') { + const variant = selectVariant(record, s, dir) + if (!variant) { + // No confident match: leave keys untransformed rather than guess. + return data + } + return walk(data, variant, dir) + } + + if (d?.type === 'object') { + const shape = shapeOf(s) ?? {} + const out: Record = {} + for (const [key, value] of Object.entries(record)) { + const fieldSchema = fieldFor(shape, key) + // Keys the schema does not declare are dropped, so the result matches the + // typed shape exactly (a server-added field has no place in the type). + if (fieldSchema === undefined) { + continue + } + out[dir.rename(key)] = walk(value, fieldSchema, dir) + } + return out + } + + // Scalar or unknown schema: pass through untransformed. + return data +} + +// Resolve a data key to its field schema. The schema is camelCase-keyed; a +// wire→public data key is snake, so it is camelized to index the shape. +function fieldFor( + shape: Record, + dataKey: string, +): ZodType | undefined { + return shape[dataKey] ?? shape[toCamelCase(dataKey)] +} + +function selectVariant( + data: Record, + schema: ZodType | undefined, + dir: Direction, +): ZodType | undefined { + const d = def(schema) + const options = d?.options ?? [] + if (d?.discriminator && schema) { + // O(1) dispatch on the discriminator literal. The data key is the wire-name in + // fromWire (snake) and the public name in toWire (camel); the variant map is + // keyed by the literal value, which is identical in both directions. + const dataKey = dir.discriminatorKey(d.discriminator) + return variantsByDiscriminator(schema, d).get(data[dataKey]) + } + // Non-discriminated union: the codegen gate guarantees at most one object + // variant (it fails the build for a mapped union with two or more), so the single + // object-shaped option is unambiguous. Other variants (scalars, arrays) reach the + // walk through their own data-kind branches, not here. + return options.find((option) => def(unwrap(option))?.type === 'object') +} + +// Memoized literal→variant map for a discriminated union, built once per schema. +const variantMapCache = new WeakMap>() + +function variantsByDiscriminator( + schema: ZodType, + d: ZodDef, +): Map { + const cached = variantMapCache.get(schema) + if (cached) { + return cached + } + const map = new Map() + for (const option of d.options ?? []) { + const shape = shapeOf(unwrap(option)) + const literal = literalValue(shape?.[d.discriminator as string]) + if (literal !== undefined) { + map.set(literal, option) + } + } + variantMapCache.set(schema, map) + return map +} + +function literalValue(schema: ZodType | undefined): unknown { + const s = unwrap(schema) + if (def(s)?.type === 'literal') { + return (s as { value?: unknown }).value + } + /* v8 ignore next -- a discriminated-union variant's discriminator is a literal */ + return undefined +} + +const toWireDirection: Direction = { + rename: toSnakeCase, + discriminatorKey: (camelKey) => camelKey, +} + +const fromWireDirection: Direction = { + rename: toCamelCase, + discriminatorKey: (camelKey) => toSnakeCase(camelKey), +} + +// Rewrite a request body or query object from the camelCase public shape to the +// snake_case wire shape, driven by its schema. Record keys (label/dimension names) +// are preserved. The return is typed as the input `T` so call sites stay cast-free +// (the runtime object has snake keys, but the value is write-only — it flows +// straight into `json:`/`toURLSearchParams`, both of which accept any object). +export function toWire(data: T, schema: ZodType): T { + return walk(data, schema, toWireDirection) as T +} + +// Rewrite a response body from the snake_case wire shape to the camelCase public +// shape. Renames keys only — never coerces values or applies defaults. The result +// is the schema's output shape: `walk` produces exactly the schema's known fields +// in camelCase, so the inferred `output` type describes the runtime value (the +// same wire-trust boundary as a plain `.json()`, with no `.parse()`). +export function fromWire( + data: unknown, + schema: S, +): output { + return walk(data, schema, fromWireDirection) as output +} + +// Thrown by assertValid when the optional `validate` client option is on and data +// fails its schema. request() catches it like any Error and surfaces it as +// Result.error. +export class ValidationError extends Error { + constructor( + message: string, + public readonly issues: unknown, + ) { + super(message) + this.name = 'ValidationError' + } +} + +// Opt-in schema check used by the funcs (when the validate option is on) against +// the snake_case wire payload: the request body after toWire, the raw response +// before fromWire, each against its generated `…Wire` schema. It is a GATE, not a +// transform — the safeParse output (coercions/defaults) is discarded, so validation +// never mutates the payload or return value. Off by default; the SDK does not +// validate by default (additive server fields must not break clients). +export function assertValid(schema: ZodType, data: unknown): void { + const result = schema.safeParse(data) + if (!result.success) { + throw new ValidationError('schema validation failed', result.error.issues) + } +} diff --git a/api/spec/packages/aip-client-javascript/src/models/operations/tax.ts b/api/spec/packages/aip-client-javascript/src/models/operations/tax.ts index 9b0c9e908e..1e7a982233 100644 --- a/api/spec/packages/aip-client-javascript/src/models/operations/tax.ts +++ b/api/spec/packages/aip-client-javascript/src/models/operations/tax.ts @@ -19,7 +19,7 @@ export interface ListTaxCodesQuery { /** Determines which page of the collection to retrieve. */ page?: { size?: number; number?: number } /** Include deleted tax codes in the response. */ - include_deleted?: boolean + includeDeleted?: boolean } export type ListTaxCodesRequest = ListTaxCodesQuery diff --git a/api/spec/packages/aip-client-javascript/src/models/schemas.ts b/api/spec/packages/aip-client-javascript/src/models/schemas.ts index 0a5ae1f5e5..211371adc3 100644 --- a/api/spec/packages/aip-client-javascript/src/models/schemas.ts +++ b/api/spec/packages/aip-client-javascript/src/models/schemas.ts @@ -397,7 +397,7 @@ export const appStripeCreateCheckoutSessionConsentCollectionTermsOfService = z export const appStripeCheckoutSessionCustomTextParams = z .object({ - after_submit: z + afterSubmit: z .object({ message: z .string() @@ -407,7 +407,7 @@ export const appStripeCheckoutSessionCustomTextParams = z }) .optional() .describe('Text displayed after the payment confirmation button.'), - shipping_address: z + shippingAddress: z .object({ message: z .string() @@ -427,7 +427,7 @@ export const appStripeCheckoutSessionCustomTextParams = z }) .optional() .describe('Text displayed alongside the payment confirmation button.'), - terms_of_service_acceptance: z + termsOfServiceAcceptance: z .object({ message: z .string() @@ -461,7 +461,7 @@ export const appStripeCheckoutSessionMode = z export const appStripeCreateCustomerPortalSessionOptions = z .object({ - configuration_id: z + configurationId: z .string() .optional() @@ -475,7 +475,7 @@ export const appStripeCreateCustomerPortalSessionOptions = z .describe( "The IETF [language tag](https://docs.stripe.com/api/customer_portal/sessions/create#create_portal_session-locale) of the locale customer portal is displayed in. If blank or `auto`, the customer's preferred_locales or browser's locale is used.", ), - return_url: z + returnUrl: z .string() .optional() @@ -734,7 +734,7 @@ export const workflowInvoicingSubscriptionEndProrationMode = z export const workflowPaymentChargeAutomaticallySettings = z .object({ - collection_method: z + collectionMethod: z .literal('charge_automatically') .describe('The collection method for the invoice.'), }) @@ -745,10 +745,10 @@ export const workflowPaymentChargeAutomaticallySettings = z export const workflowPaymentSendInvoiceSettings = z .object({ - collection_method: z + collectionMethod: z .literal('send_invoice') .describe('The collection method for the invoice.'), - due_after: z + dueAfter: z .string() .optional() .default('P30D') @@ -777,14 +777,14 @@ export const invoiceValidationIssueSeverity = z export const invoiceExternalReferences = z .object({ - invoicing_id: z + invoicingId: z .string() .optional() .describe( 'The ID assigned by the external invoicing app (e.g. Stripe invoice ID).', ), - payment_id: z + paymentId: z .string() .optional() @@ -812,7 +812,7 @@ export const invoiceStandardStatus = z export const invoiceAvailableActionDetails = z .object({ - resulting_state: z + resultingState: z .string() .describe( @@ -826,7 +826,7 @@ export const invoiceAvailableActionDetails = z export const invoiceWorkflowInvoicingSettings = z .object({ - auto_advance: z + autoAdvance: z .boolean() .optional() .default(true) @@ -834,7 +834,7 @@ export const invoiceWorkflowInvoicingSettings = z .describe( 'Whether to automatically issue the invoice after the draft_period has passed.', ), - draft_period: z + draftPeriod: z .string() .optional() .default('P0D') @@ -854,7 +854,7 @@ export const invoiceDiscountReason = z export const invoiceLineExternalReferences = z .object({ - invoicing_id: z + invoicingId: z .string() .optional() .describe('The ID assigned by the external invoicing app.'), @@ -1295,8 +1295,8 @@ export const updateMeterRequest = z export const appCustomerDataStripe = z .object({ - customer_id: z.string().optional().describe('The Stripe customer ID used.'), - default_payment_method_id: z + customerId: z.string().optional().describe('The Stripe customer ID used.'), + defaultPaymentMethodId: z .string() .optional() .describe('The Stripe default payment method ID.'), @@ -1320,7 +1320,7 @@ export const createCurrencyCode = z export const listCostBasesParamsFilter = z .object({ - fiat_code: currencyCode.optional(), + fiatCode: currencyCode.optional(), }) .describe('Filter options for listing cost bases.') @@ -1363,12 +1363,12 @@ export const rateCardDiscounts = z export const totals = z .object({ amount: numeric, - taxes_total: numeric, - taxes_inclusive_total: numeric, - taxes_exclusive_total: numeric, - charges_total: numeric, - discounts_total: numeric, - credits_total: numeric, + taxesTotal: numeric, + taxesInclusiveTotal: numeric, + taxesExclusiveTotal: numeric, + chargesTotal: numeric, + discountsTotal: numeric, + creditsTotal: numeric, total: numeric, }) @@ -1378,8 +1378,8 @@ export const totals = z export const spendCommitments = z .object({ - minimum_amount: numeric.optional(), - maximum_amount: numeric.optional(), + minimumAmount: numeric.optional(), + maximumAmount: numeric.optional(), }) .describe( @@ -1410,21 +1410,21 @@ export const featureManualUnitCost = z export const featureLlmUnitCostPricing = z .object({ - input_per_token: numeric, - output_per_token: numeric, - cache_read_per_token: numeric.optional(), - reasoning_per_token: numeric.optional(), - cache_write_per_token: numeric.optional(), + inputPerToken: numeric, + outputPerToken: numeric, + cacheReadPerToken: numeric.optional(), + reasoningPerToken: numeric.optional(), + cacheWritePerToken: numeric.optional(), }) .describe('Resolved per-token pricing from the LLM cost database.') export const llmCostModelPricing = z .object({ - input_per_token: numeric, - output_per_token: numeric, - cache_read_per_token: numeric.optional(), - cache_write_per_token: numeric.optional(), - reasoning_per_token: numeric.optional(), + inputPerToken: numeric, + outputPerToken: numeric, + cacheReadPerToken: numeric.optional(), + cacheWritePerToken: numeric.optional(), + reasoningPerToken: numeric.optional(), }) .describe('Token pricing for an LLM model, denominated per token.') @@ -1472,8 +1472,8 @@ export const listMetersParamsFilter = z export const listLlmCostPricesParamsFilter = z .object({ provider: stringFieldFilter.optional(), - model_id: stringFieldFilter.optional(), - model_name: stringFieldFilter.optional(), + modelId: stringFieldFilter.optional(), + modelName: stringFieldFilter.optional(), currency: stringFieldFilter.optional(), source: stringFieldFilter.optional(), }) @@ -1716,8 +1716,8 @@ export const appStripeCreateCustomerPortalSessionResult = z .describe( 'The ID of the customer portal session. See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-id', ), - stripe_customer_id: z.string().describe('The ID of the stripe customer.'), - configuration_id: z + stripeCustomerId: z.string().describe('The ID of the stripe customer.'), + configurationId: z .string() .describe( @@ -1729,8 +1729,8 @@ export const appStripeCreateCustomerPortalSessionResult = z .describe( 'Livemode. See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-livemode', ), - created_at: dateTime, - return_url: z + createdAt: dateTime, + returnUrl: z .string() .describe( @@ -1766,8 +1766,8 @@ export const closedPeriod = z export const subscriptionAddonTimelineSegment = z .object({ - active_from: dateTime, - active_to: dateTime.optional(), + activeFrom: dateTime, + activeTo: dateTime.optional(), quantity: z .number() .int() @@ -1779,18 +1779,18 @@ export const subscriptionAddonTimelineSegment = z export const costBasis = z .object({ id: ulid, - fiat_code: currencyCode, + fiatCode: currencyCode, rate: numeric, - effective_from: dateTime.optional(), - created_at: dateTime, + effectiveFrom: dateTime.optional(), + createdAt: dateTime, }) .describe('Describes currency basis supported by billing system.') export const createCostBasisRequest = z .object({ - fiat_code: currencyCode, + fiatCode: currencyCode, rate: numeric, - effective_from: dateTime.optional(), + effectiveFrom: dateTime.optional(), }) .describe('CostBasis create request.') @@ -1839,9 +1839,9 @@ export const resource = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), }) .describe('Represents common fields of resources.') @@ -1862,7 +1862,7 @@ export const resourceImmutable = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, + createdAt: dateTime, }) .describe('Represents common fields of immutable resources.') @@ -2081,8 +2081,8 @@ export const upsertPlanAddonRequest = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - from_plan_phase: resourceKey, - max_quantity: z + fromPlanPhase: resourceKey, + maxQuantity: z .number() .int() .gte(1) @@ -2111,9 +2111,9 @@ export const resourceWithKey = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), key: resourceKey, }) .describe('Represents common fields of resources with a key.') @@ -2140,12 +2140,12 @@ export const createMeterRequest = z labels: labels.optional(), key: resourceKey, aggregation: meterAggregation, - event_type: z + eventType: z .string() .min(1) .describe('The event type to include in the aggregation.'), - events_from: dateTime.optional(), - value_property: z + eventsFrom: dateTime.optional(), + valueProperty: z .string() .min(1) .optional() @@ -2180,17 +2180,17 @@ export const meter = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), key: resourceKey, aggregation: meterAggregation, - event_type: z + eventType: z .string() .min(1) .describe('The event type to include in the aggregation.'), - events_from: dateTime.optional(), - value_property: z + eventsFrom: dateTime.optional(), + valueProperty: z .string() .min(1) .optional() @@ -2278,7 +2278,7 @@ export const customerKeyReference = z export const customerUsageAttribution = z .object({ - subject_keys: z + subjectKeys: z .array(usageAttributionSubjectKey) .describe( @@ -2293,12 +2293,12 @@ export const customerUsageAttribution = z export const address = z .object({ country: countryCode.optional(), - postal_code: z.string().optional().describe('Postal code.'), + postalCode: z.string().optional().describe('Postal code.'), state: z.string().optional().describe('State or province.'), city: z.string().optional().describe('City.'), line1: z.string().optional().describe('First line of the address.'), line2: z.string().optional().describe('Second line of the address.'), - phone_number: z.string().optional().describe('Phone number.'), + phoneNumber: z.string().optional().describe('Phone number.'), }) .describe('Address') @@ -2342,36 +2342,36 @@ export const appStripeCreateCheckoutSessionTaxIdCollection = z export const appStripeCreateCheckoutSessionResult = z .object({ - customer_id: ulid, - stripe_customer_id: z.string().describe('The Stripe customer ID.'), - session_id: z.string().describe('The Stripe checkout session ID.'), - setup_intent_id: z + customerId: ulid, + stripeCustomerId: z.string().describe('The Stripe customer ID.'), + sessionId: z.string().describe('The Stripe checkout session ID.'), + setupIntentId: z .string() .describe( 'The setup intent ID created for collecting the payment method.', ), - client_secret: z + clientSecret: z .string() .optional() .describe( 'Client secret for initializing Stripe.js on the client side. Required for embedded checkout sessions. See: https://docs.stripe.com/payments/checkout/custom-success-page', ), - client_reference_id: z + clientReferenceId: z .string() .optional() .describe( 'The client reference ID provided in the request. Useful for reconciling the session with your internal systems.', ), - customer_email: z + customerEmail: z .string() .optional() .describe("Customer's email address if provided to Stripe."), currency: currencyCode.optional(), - created_at: dateTime, - expires_at: dateTime.optional(), + createdAt: dateTime, + expiresAt: dateTime.optional(), metadata: z .record(z.string(), z.string()) .optional() @@ -2391,21 +2391,21 @@ export const appStripeCreateCheckoutSessionResult = z 'URL to redirect customers to the checkout page (for hosted mode).', ), mode: appStripeCheckoutSessionMode, - cancel_url: z + cancelUrl: z .string() .optional() .describe( 'The cancel URL where customers are redirected if they cancel.', ), - success_url: z + successUrl: z .string() .optional() .describe( 'The success URL where customers are redirected after completion.', ), - return_url: z + returnUrl: z .string() .optional() .describe('The return URL for embedded sessions after authentication.'), @@ -2417,7 +2417,7 @@ export const appStripeCreateCheckoutSessionResult = z export const customerStripeCreateCustomerPortalSessionRequest = z .object({ - stripe_options: appStripeCreateCustomerPortalSessionOptions, + stripeOptions: appStripeCreateCustomerPortalSessionOptions, }) .describe( @@ -2427,8 +2427,8 @@ export const customerStripeCreateCustomerPortalSessionRequest = z export const entitlementAccessResult = z .object({ type: entitlementType, - feature_key: resourceKey, - has_access: z + featureKey: resourceKey, + hasAccess: z .boolean() .describe( @@ -2447,8 +2447,8 @@ export const entitlementAccessResult = z export const createCreditGrantPurchase = z .object({ currency: currencyCode, - per_unit_cost_basis: numeric.optional().default('1.0'), - availability_policy: creditAvailabilityPolicy + perUnitCostBasis: numeric.optional().default('1.0'), + availabilityPolicy: creditAvailabilityPolicy .optional() .default('on_creation'), }) @@ -2459,7 +2459,7 @@ export const rateCardMeteredEntitlement = z type: z .literal('metered') .describe('The type of the entitlement template.'), - is_soft_limit: z + isSoftLimit: z .boolean() .optional() .default(false) @@ -2475,7 +2475,7 @@ export const rateCardMeteredEntitlement = z .describe( "The amount of usage granted each usage period, in the feature's unit. Usage is counted against this allowance and the balance resets every usage period. When `is_soft_limit` is true the subject keeps access after the limit is reached; otherwise access is denied once the allowance is exhausted.", ), - usage_period: iso8601Duration.optional(), + usagePeriod: iso8601Duration.optional(), }) .describe('The entitlement template of a metered entitlement.') @@ -2489,12 +2489,12 @@ export const recurringPeriod = z export const creditGrantPurchase = z .object({ currency: currencyCode, - per_unit_cost_basis: numeric.optional().default('1.0'), + perUnitCostBasis: numeric.optional().default('1.0'), amount: numeric, - availability_policy: creditAvailabilityPolicy + availabilityPolicy: creditAvailabilityPolicy .optional() .default('on_creation'), - settlement_status: creditPurchasePaymentSettlementStatus.optional(), + settlementStatus: creditPurchasePaymentSettlementStatus.optional(), }) .describe('Purchase and payment terms of the grant.') @@ -2518,7 +2518,7 @@ export const listCreditGrantsParamsFilter = z export const getCreditBalanceParamsFilter = z .object({ currency: stringFieldFilterExact.optional(), - feature_key: stringFieldFilter.optional(), + featureKey: stringFieldFilter.optional(), }) .describe('Filter options for getting a credit balance.') @@ -2540,7 +2540,7 @@ export const listPlansParamsFilter = z export const subscriptionCreate = z .object({ labels: labels.optional(), - settlement_mode: settlementMode.optional(), + settlementMode: settlementMode.optional(), customer: z .object({ id: ulid.optional(), @@ -2561,7 +2561,7 @@ export const subscriptionCreate = z ), }) .describe('The plan reference of the subscription.'), - billing_anchor: dateTime.optional(), + billingAnchor: dateTime.optional(), }) .describe('Subscription create request.') @@ -2575,14 +2575,14 @@ export const subscription = z .object({ id: ulid, labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), - customer_id: ulid, - plan_id: ulid.optional(), - billing_anchor: dateTime, + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), + customerId: ulid, + planId: ulid.optional(), + billingAnchor: dateTime, status: subscriptionStatus, - settlement_mode: settlementMode.optional(), + settlementMode: settlementMode.optional(), }) .describe('Subscription.') @@ -2596,7 +2596,7 @@ export const subscriptionEditTiming = z export const unitConfig = z .object({ operation: unitConfigOperation, - conversion_factor: numeric, + conversionFactor: numeric, rounding: unitConfigRoundingMode.optional().default('none'), precision: z .number() @@ -2607,7 +2607,7 @@ export const unitConfig = z .describe( 'The number of decimal places to retain after rounding. Only meaningful when rounding is not "none". Defaults to 0 (round to whole numbers).', ), - display_unit: z + displayUnit: z .string() .optional() @@ -2633,8 +2633,8 @@ export const appCatalogItem = z export const taxCodeAppMapping = z .object({ - app_type: appType, - tax_code: z.string().describe('Tax code.'), + appType: appType, + taxCode: z.string().describe('Tax code.'), }) .describe('Mapping of app types to tax codes.') @@ -2649,7 +2649,7 @@ export const partyTaxIdentity = z export const workflowInvoicingSettings = z .object({ - auto_advance: z + autoAdvance: z .boolean() .optional() .default(true) @@ -2657,7 +2657,7 @@ export const workflowInvoicingSettings = z .describe( 'Whether to automatically issue the invoice after the draftPeriod has passed.', ), - draft_period: z + draftPeriod: z .string() .optional() .default('P0D') @@ -2665,20 +2665,19 @@ export const workflowInvoicingSettings = z .describe( 'The period for the invoice to be kept in draft status for manual reviews.', ), - progressive_billing: z + progressiveBilling: z .boolean() .optional() .default(true) .describe('Should progressive billing be allowed for this workflow?'), - subscription_end_proration_mode: - workflowInvoicingSubscriptionEndProrationMode - .optional() - .default('bill_actual_period'), + subscriptionEndProrationMode: workflowInvoicingSubscriptionEndProrationMode + .optional() + .default('bill_actual_period'), }) .describe('Invoice settings for a billing workflow.') export const workflowPaymentSettings = z - .discriminatedUnion('collection_method', [ + .discriminatedUnion('collectionMethod', [ workflowPaymentChargeAutomaticallySettings, workflowPaymentSendInvoiceSettings, ]) @@ -2712,7 +2711,7 @@ export const invoiceAvailableActions = z approve: invoiceAvailableActionDetails.optional(), delete: invoiceAvailableActionDetails.optional(), retry: invoiceAvailableActionDetails.optional(), - snapshot_quantities: invoiceAvailableActionDetails.optional(), + snapshotQuantities: invoiceAvailableActionDetails.optional(), }) .describe( @@ -2727,7 +2726,7 @@ export const invoiceLineAmountDiscount = z .string() .optional() .describe('Optional human-readable description of the discount.'), - external_references: invoiceLineExternalReferences.optional(), + externalReferences: invoiceLineExternalReferences.optional(), amount: numeric, }) .describe('A monetary amount discount applied to an invoice line item.') @@ -2740,7 +2739,7 @@ export const invoiceLineUsageDiscount = z .string() .optional() .describe('Optional human-readable description of the discount.'), - external_references: invoiceLineExternalReferences.optional(), + externalReferences: invoiceLineExternalReferences.optional(), quantity: numeric, }) .describe('A usage quantity discount applied to an invoice line item.') @@ -2753,7 +2752,7 @@ export const invoiceLineBaseDiscount = z .string() .optional() .describe('Optional human-readable description of the discount.'), - external_references: invoiceLineExternalReferences.optional(), + externalReferences: invoiceLineExternalReferences.optional(), }) .describe('Base fields shared by all invoice line item discounts.') @@ -2791,7 +2790,7 @@ export const currencyCustom = z 'The symbol of the currency. It should be a string that represents the symbol of the currency, such as "$" for US Dollar or "€" for Euro.', ), code: currencyCodeCustom, - created_at: dateTime, + createdAt: dateTime, }) .describe('Describes custom currency.') @@ -2825,7 +2824,7 @@ export const createCurrencyCustomRequest = z export const governanceQueryRequest = z .object({ - include_credits: z + includeCredits: z .boolean() .optional() .default(false) @@ -2873,14 +2872,14 @@ export const governanceQueryError = z export const appCustomerData = z .object({ stripe: appCustomerDataStripe.optional(), - external_invoicing: appCustomerDataExternalInvoicing.optional(), + externalInvoicing: appCustomerDataExternalInvoicing.optional(), }) .describe('App customer data.') export const upsertAppCustomerDataRequest = z .object({ stripe: appCustomerDataStripe.optional(), - external_invoicing: appCustomerDataExternalInvoicing.optional(), + externalInvoicing: appCustomerDataExternalInvoicing.optional(), }) .describe('AppCustomerData upsert request.') @@ -2940,7 +2939,7 @@ export const listCreditTransactionsParamsFilter = z .object({ type: creditTransactionType.optional(), currency: billingCurrencyCode.optional(), - feature_key: stringFieldFilter.optional(), + featureKey: stringFieldFilter.optional(), }) .describe('Filter options for listing credit transactions.') @@ -2961,12 +2960,12 @@ export const creditTransaction = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - booked_at: dateTime, + createdAt: dateTime, + bookedAt: dateTime, type: creditTransactionType, currency: billingCurrencyCode, amount: numeric, - available_balance: z + availableBalance: z .object({ before: numeric, after: numeric, @@ -2980,9 +2979,9 @@ export const creditTransaction = z export const priceTier = z .object({ - up_to_amount: numeric.optional(), - flat_price: priceFlat.optional(), - unit_price: priceUnit.optional(), + upToAmount: numeric.optional(), + flatPrice: priceFlat.optional(), + unitPrice: priceUnit.optional(), }) .describe( @@ -3004,7 +3003,7 @@ export const featureLlmUnitCost = z type: z .literal('llm') .describe('The type discriminator for LLM unit cost.'), - provider_property: z + providerProperty: z .string() .optional() @@ -3018,7 +3017,7 @@ export const featureLlmUnitCost = z .describe( 'Static LLM provider value (e.g., "openai", "anthropic"). Use this when the feature tracks a single provider. Mutually exclusive with `provider_property`.', ), - model_property: z + modelProperty: z .string() .optional() @@ -3032,14 +3031,14 @@ export const featureLlmUnitCost = z .describe( 'Static model ID value (e.g., "gpt-4", "claude-3-5-sonnet"). Use this when the feature tracks a single model. Mutually exclusive with `model_property`.', ), - token_type_property: z + tokenTypeProperty: z .string() .optional() .describe( 'Meter group-by property that holds the token type. Use this when the meter has a group-by dimension for token type. Mutually exclusive with `token_type`.', ), - token_type: featureLlmTokenType.optional(), + tokenType: featureLlmTokenType.optional(), pricing: featureLlmUnitCostPricing.optional(), }) @@ -3055,10 +3054,10 @@ export const llmCostPrice = z pricing: llmCostModelPricing, currency: currencyCode, source: llmCostPriceSource, - effective_from: dateTime, - effective_to: dateTime.optional(), - created_at: dateTime, - updated_at: dateTime, + effectiveFrom: dateTime, + effectiveTo: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, }) .describe( @@ -3068,12 +3067,12 @@ export const llmCostPrice = z export const llmCostOverrideCreate = z .object({ provider: z.string().describe('Provider/vendor of the model.'), - model_id: z.string().describe('Canonical model identifier.'), - model_name: z.string().optional().describe('Human-readable model name.'), + modelId: z.string().describe('Canonical model identifier.'), + modelName: z.string().optional().describe('Human-readable model name.'), pricing: llmCostModelPricing, currency: currencyCode, - effective_from: dateTime, - effective_to: dateTime.optional(), + effectiveFrom: dateTime, + effectiveTo: dateTime.optional(), }) .describe( @@ -3084,26 +3083,26 @@ export const listCustomersParamsFilter = z .object({ key: stringFieldFilter.optional(), name: stringFieldFilter.optional(), - primary_email: stringFieldFilter.optional(), - usage_attribution_subject_key: stringFieldFilter.optional(), - plan_key: stringFieldFilter.optional(), - billing_profile_id: ulidFieldFilter.optional(), + primaryEmail: stringFieldFilter.optional(), + usageAttributionSubjectKey: stringFieldFilter.optional(), + planKey: stringFieldFilter.optional(), + billingProfileId: ulidFieldFilter.optional(), }) .describe('Filter options for listing customers.') export const listSubscriptionsParamsFilter = z .object({ id: ulidFieldFilter.optional(), - customer_id: ulidFieldFilter.optional(), + customerId: ulidFieldFilter.optional(), status: stringFieldFilterExact.optional(), - plan_id: ulidFieldFilter.optional(), - plan_key: stringFieldFilterExact.optional(), + planId: ulidFieldFilter.optional(), + planKey: stringFieldFilterExact.optional(), }) .describe('Filter options for listing subscriptions.') export const listFeatureParamsFilter = z .object({ - meter_id: ulidFieldFilter.optional(), + meterId: ulidFieldFilter.optional(), key: stringFieldFilter.optional(), name: stringFieldFilter.optional(), }) @@ -3122,7 +3121,7 @@ export const listAddonsParamsFilter = z export const createCreditGrantTaxConfig = z .object({ behavior: taxBehavior.optional(), - tax_code: createResourceReference.optional(), + taxCode: createResourceReference.optional(), }) .describe( @@ -3132,7 +3131,7 @@ export const createCreditGrantTaxConfig = z export const creditGrantTaxConfig = z .object({ behavior: taxBehavior.optional(), - tax_code: taxCodeReference.optional(), + taxCode: taxCodeReference.optional(), }) .describe( @@ -3143,9 +3142,9 @@ export const taxConfig = z .object({ behavior: taxBehavior.optional(), stripe: taxConfigStripe.optional(), - external_invoicing: taxConfigExternalInvoicing.optional(), - tax_code_id: ulid.optional(), - tax_code: taxCodeReference.optional(), + externalInvoicing: taxConfigExternalInvoicing.optional(), + taxCodeId: ulid.optional(), + taxCode: taxCodeReference.optional(), }) .describe('Set of provider specific tax configs.') @@ -3158,10 +3157,10 @@ export const rateCardTaxConfig = z export const organizationDefaultTaxCodes = z .object({ - invoicing_tax_code: taxCodeReference, - credit_grant_tax_code: taxCodeReference, - created_at: dateTime, - updated_at: dateTime, + invoicingTaxCode: taxCodeReference, + creditGrantTaxCode: taxCodeReference, + createdAt: dateTime, + updatedAt: dateTime, }) .describe( @@ -3170,8 +3169,8 @@ export const organizationDefaultTaxCodes = z export const updateOrganizationDefaultTaxCodesRequest = z .object({ - invoicing_tax_code: taxCodeReference.optional(), - credit_grant_tax_code: taxCodeReference.optional(), + invoicingTaxCode: taxCodeReference.optional(), + creditGrantTaxCode: taxCodeReference.optional(), }) .describe('OrganizationDefaultTaxCodes update request.') @@ -3192,12 +3191,12 @@ export const planAddon = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), addon: addonReference, - from_plan_phase: resourceKey, - max_quantity: z + fromPlanPhase: resourceKey, + maxQuantity: z .number() .int() .gte(1) @@ -3206,7 +3205,7 @@ export const planAddon = z .describe( 'The maximum number of times the add-on can be purchased for the plan. For single-instance add-ons this field must be omitted. For multi-instance add-ons when omitted, unlimited quantity can be purchased.', ), - validation_errors: z + validationErrors: z .array(productCatalogValidationError) .optional() .describe('List of validation errors.'), @@ -3233,8 +3232,8 @@ export const createPlanAddonRequest = z ), labels: labels.optional(), addon: addonReference, - from_plan_phase: resourceKey, - max_quantity: z + fromPlanPhase: resourceKey, + maxQuantity: z .number() .int() .gte(1) @@ -3271,10 +3270,10 @@ export const listEventsParamsFilter = z source: stringFieldFilter.optional(), subject: stringFieldFilter.optional(), type: stringFieldFilter.optional(), - customer_id: ulidFieldFilter.optional(), + customerId: ulidFieldFilter.optional(), time: dateTimeFieldFilter.optional(), - ingested_at: dateTimeFieldFilter.optional(), - stored_at: dateTimeFieldFilter.optional(), + ingestedAt: dateTimeFieldFilter.optional(), + storedAt: dateTimeFieldFilter.optional(), }) .describe('Filter options for listing ingested events.') @@ -3282,10 +3281,10 @@ export const resourceFilters = z .object({ name: stringFieldFilter.optional(), labels: labelsFieldFilter.optional(), - public_labels: labelsFieldFilter.optional(), - created_at: dateTimeFieldFilter.optional(), - updated_at: dateTimeFieldFilter.optional(), - deleted_at: dateTimeFieldFilter.optional(), + publicLabels: labelsFieldFilter.optional(), + createdAt: dateTimeFieldFilter.optional(), + updatedAt: dateTimeFieldFilter.optional(), + deletedAt: dateTimeFieldFilter.optional(), }) .describe('Resource filters.') @@ -3294,7 +3293,7 @@ export const fieldFilters = z boolean: booleanFieldFilter.optional(), numeric: numericFieldFilter.optional(), string: stringFieldFilter.optional(), - string_exact: stringFieldFilterExact.optional(), + stringExact: stringFieldFilterExact.optional(), ulid: ulidFieldFilter.optional(), datetime: dateTimeFieldFilter.optional(), labels: labelsFieldFilter.optional(), @@ -3305,9 +3304,9 @@ export const ingestedEvent = z .object({ event: event, customer: customerReference.optional(), - ingested_at: dateTime, - stored_at: dateTime, - validation_errors: z + ingestedAt: dateTime, + storedAt: dateTime, + validationErrors: z .array(ingestedEventValidationError) .optional() .describe('The validation errors of the ingested event.'), @@ -3398,13 +3397,13 @@ export const createCustomerRequest = z ), labels: labels.optional(), key: externalResourceKey, - usage_attribution: customerUsageAttribution.optional(), - primary_email: z + usageAttribution: customerUsageAttribution.optional(), + primaryEmail: z .string() .optional() .describe('The primary email address of the customer.'), currency: currencyCode.optional(), - billing_address: address.optional(), + billingAddress: address.optional(), }) .describe('Customer create request.') @@ -3425,17 +3424,17 @@ export const customer = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), key: externalResourceKey, - usage_attribution: customerUsageAttribution.optional(), - primary_email: z + usageAttribution: customerUsageAttribution.optional(), + primaryEmail: z .string() .optional() .describe('The primary email address of the customer.'), currency: currencyCode.optional(), - billing_address: address.optional(), + billingAddress: address.optional(), }) .describe( @@ -3458,19 +3457,19 @@ export const upsertCustomerRequest = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - usage_attribution: customerUsageAttribution.optional(), - primary_email: z + usageAttribution: customerUsageAttribution.optional(), + primaryEmail: z .string() .optional() .describe('The primary email address of the customer.'), currency: currencyCode.optional(), - billing_address: address.optional(), + billingAddress: address.optional(), }) .describe('Customer upsert request.') export const partyAddresses = z .object({ - billing_address: address, + billingAddress: address, }) .describe('A collection of addresses for the party.') @@ -3482,8 +3481,8 @@ export const invoiceCustomer = z .min(1) .max(256) .describe('Display name of the resource. Between 1 and 256 characters.'), - usage_attribution: customerUsageAttribution.optional(), - billing_address: address.optional(), + usageAttribution: customerUsageAttribution.optional(), + billingAddress: address.optional(), key: externalResourceKey.optional(), }) @@ -3493,11 +3492,11 @@ export const invoiceCustomer = z export const appStripeCreateCheckoutSessionConsentCollection = z .object({ - payment_method_reuse_agreement: + paymentMethodReuseAgreement: appStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreement.optional(), promotions: appStripeCreateCheckoutSessionConsentCollectionPromotions.optional(), - terms_of_service: + termsOfService: appStripeCreateCheckoutSessionConsentCollectionTermsOfService.optional(), }) .describe('Checkout Session consent collection configuration.') @@ -3524,7 +3523,7 @@ export const rateCardEntitlement = z export const workflowCollectionAlignmentAnchored = z .object({ type: z.literal('anchored').describe('The type of alignment.'), - recurring_period: recurringPeriod, + recurringPeriod: recurringPeriod, }) .describe( @@ -3554,7 +3553,7 @@ export const subscriptionCancel = z export const subscriptionChange = z .object({ labels: labels.optional(), - settlement_mode: settlementMode.optional(), + settlementMode: settlementMode.optional(), customer: z .object({ id: ulid.optional(), @@ -3575,7 +3574,7 @@ export const subscriptionChange = z ), }) .describe('The plan reference of the subscription.'), - billing_anchor: dateTime.optional(), + billingAnchor: dateTime.optional(), timing: subscriptionEditTiming, }) .describe('Request for changing a subscription.') @@ -3598,15 +3597,15 @@ export const createSubscriptionAddonRequest = z export const invoiceUsageQuantityDetail = z .object({ - raw_quantity: numeric, - converted_quantity: numeric, - invoiced_quantity: numeric, - display_unit: z + rawQuantity: numeric, + convertedQuantity: numeric, + invoicedQuantity: numeric, + displayUnit: z .string() .optional() .describe('The display unit label (e.g., "GB", "hours", "M tokens").'), - applied_unit_config: unitConfig, + appliedUnitConfig: unitConfig, }) .describe( @@ -3630,13 +3629,13 @@ export const appStripe = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), type: z.literal('stripe').describe('The app type.'), definition: appCatalogItem, status: appStatus, - account_id: z + accountId: z .string() .describe( @@ -3648,7 +3647,7 @@ export const appStripe = z .describe( 'Indicates whether the app is connected to a live Stripe account.', ), - masked_api_key: z + maskedApiKey: z .string() .describe( @@ -3674,9 +3673,9 @@ export const appSandbox = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), type: z.literal('sandbox').describe('The app type.'), definition: appCatalogItem, status: appStatus, @@ -3700,19 +3699,19 @@ export const appExternalInvoicing = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), type: z.literal('external_invoicing').describe('The app type.'), definition: appCatalogItem, status: appStatus, - enable_draft_sync_hook: z + enableDraftSyncHook: z .boolean() .describe( 'Enable draft synchronization hook. When enabled, invoices will pause at the draft state and wait for the integration to call the draft synchronized endpoint before progressing to the issuing state. This allows the external system to validate and prepare the invoice data. When disabled, invoices automatically progress through the draft state based on the configured workflow timing.', ), - enable_issuing_sync_hook: z + enableIssuingSyncHook: z .boolean() .describe( @@ -3741,7 +3740,7 @@ export const createTaxCodeRequest = z ), labels: labels.optional(), key: resourceKey, - app_mappings: z + appMappings: z .array(taxCodeAppMapping) .describe('Mapping of app types to tax codes.'), }) @@ -3764,11 +3763,11 @@ export const taxCode = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), key: resourceKey, - app_mappings: z + appMappings: z .array(taxCodeAppMapping) .describe('Mapping of app types to tax codes.'), }) @@ -3790,7 +3789,7 @@ export const upsertTaxCodeRequest = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - app_mappings: z + appMappings: z .array(taxCodeAppMapping) .describe('Mapping of app types to tax codes.'), }) @@ -3815,13 +3814,13 @@ export const invoiceStatusDetails = z 'Whether the invoice is immutable (i.e. cannot be modified or deleted).', ), failed: z.boolean().describe('Whether the invoice is in a failed state.'), - extended_status: z + extendedStatus: z .string() .describe( 'Fine-grained internal status string providing additional workflow detail beyond the top-level status enum.', ), - available_actions: invoiceAvailableActions, + availableActions: invoiceAvailableActions, }) .describe('Detailed status information for a standard invoice.') @@ -3847,7 +3846,7 @@ export const currency = z export const governanceFeatureAccess = z .object({ - has_access: z + hasAccess: z .boolean() .describe( @@ -3859,21 +3858,21 @@ export const governanceFeatureAccess = z export const customerData = z .object({ - billing_profile: profileReference.optional(), - app_data: appCustomerData.optional(), + billingProfile: profileReference.optional(), + appData: appCustomerData.optional(), }) .describe('Billing customer data.') export const upsertCustomerBillingDataRequest = z .object({ - billing_profile: profileReference.optional(), - app_data: appCustomerData.optional(), + billingProfile: profileReference.optional(), + appData: appCustomerData.optional(), }) .describe('CustomerBillingData upsert request.') export const creditBalances = z .object({ - retrieved_at: dateTime, + retrievedAt: dateTime, balances: z.array(creditBalance).describe('The balances by currencies.'), }) .describe('The balances of the credits of a customer.') @@ -3947,11 +3946,11 @@ export const createCreditGrantRequest = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: createLabels.optional(), - funding_method: creditFundingMethod, + fundingMethod: creditFundingMethod, currency: createCurrencyCode, amount: numeric, purchase: createCreditGrantPurchase.optional(), - tax_config: createCreditGrantTaxConfig.optional(), + taxConfig: createCreditGrantTaxConfig.optional(), filters: createCreditGrantFilters.optional(), priority: z .number() @@ -3964,8 +3963,8 @@ export const createCreditGrantRequest = z .describe( 'Draw-down priority of the grant. Lower values have higher priority.', ), - effective_at: dateTime.optional(), - expires_after: iso8601Duration.optional(), + effectiveAt: dateTime.optional(), + expiresAfter: iso8601Duration.optional(), key: externalResourceKey.optional(), }) .describe('CreditGrant create request.') @@ -3987,14 +3986,14 @@ export const creditGrant = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), - funding_method: creditFundingMethod, + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), + fundingMethod: creditFundingMethod, currency: billingCurrencyCode, amount: numeric, purchase: creditGrantPurchase.optional(), - tax_config: creditGrantTaxConfig.optional(), + taxConfig: creditGrantTaxConfig.optional(), invoice: creditGrantInvoiceReference.optional(), filters: creditGrantFilters.optional(), priority: z @@ -4008,10 +4007,10 @@ export const creditGrant = z .describe( 'Draw-down priority of the grant. Lower values have higher priority.', ), - effective_at: dateTime.optional(), + effectiveAt: dateTime.optional(), key: externalResourceKey.optional(), - expires_at: dateTime.optional(), - voided_at: dateTime.optional(), + expiresAt: dateTime.optional(), + voidedAt: dateTime.optional(), status: creditGrantStatus, }) @@ -4037,24 +4036,24 @@ export const createChargeFlatFeeRequest = z labels: labels.optional(), type: z.literal('flat_fee').describe('The type of the charge.'), currency: currencyCode, - invoice_at: dateTime, - service_period: closedPeriod, - unique_reference_id: z + invoiceAt: dateTime, + servicePeriod: closedPeriod, + uniqueReferenceId: z .string() .optional() .describe('Unique reference ID of the charge.'), - settlement_mode: settlementMode, - tax_config: taxConfig.optional(), - payment_term: pricePaymentTerm, + settlementMode: settlementMode, + taxConfig: taxConfig.optional(), + paymentTerm: pricePaymentTerm, discounts: chargeFlatFeeDiscounts.optional(), - feature_key: z + featureKey: z .string() .optional() .describe('The feature associated with the charge, when applicable.'), - proration_configuration: rateCardProrationConfiguration, - amount_before_proration: currencyAmount, - full_service_period: closedPeriod.optional(), - billing_period: closedPeriod.optional(), + prorationConfiguration: rateCardProrationConfiguration, + amountBeforeProration: currencyAmount, + fullServicePeriod: closedPeriod.optional(), + billingPeriod: closedPeriod.optional(), }) .describe('Flat fee charge create request.') @@ -4076,7 +4075,7 @@ export const workflowTaxSettings = z .describe( 'Enforce tax calculation when tax is supported by the app. When enabled, the billing system will not allow to create an invoice without tax calculation. Enforcement is different per apps, for example, Stripe app requires customer to have a tax location when starting a paid subscription.', ), - default_tax_config: taxConfig.optional(), + defaultTaxConfig: taxConfig.optional(), }) .describe('Tax settings for a billing workflow.') @@ -4104,7 +4103,7 @@ export const meterQueryRequest = z from: dateTime.optional(), to: dateTime.optional(), granularity: meterQueryGranularity.optional(), - time_zone: z + timeZone: z .string() .optional() .default('UTC') @@ -4112,7 +4111,7 @@ export const meterQueryRequest = z .describe( 'The value is the name of the time zone as defined in the IANA Time Zone Database (http://www.iana.org/time-zones). The time zone is used to determine the start and end of the time buckets. If not specified, the UTC timezone will be used.', ), - group_by_dimensions: z + groupByDimensions: z .array(z.string()) .max(100) .optional() @@ -4136,7 +4135,7 @@ export const party = z .string() .optional() .describe('Legal name or representation of the party.'), - tax_id: partyTaxIdentity.optional(), + taxId: partyTaxIdentity.optional(), addresses: partyAddresses.optional(), }) .describe('Party represents a person or business entity.') @@ -4148,7 +4147,7 @@ export const supplier = z .string() .optional() .describe('Legal name or representation of the party.'), - tax_id: partyTaxIdentity.optional(), + taxId: partyTaxIdentity.optional(), addresses: partyAddresses.optional(), }) @@ -4158,30 +4157,30 @@ export const supplier = z export const appStripeCreateCheckoutSessionRequestOptions = z .object({ - billing_address_collection: + billingAddressCollection: appStripeCreateCheckoutSessionBillingAddressCollection .optional() .default('auto'), - cancel_url: z + cancelUrl: z .string() .optional() .describe( 'URL to redirect customers who cancel the checkout session. Not allowed when ui_mode is "embedded".', ), - client_reference_id: z + clientReferenceId: z .string() .optional() .describe( 'Unique reference string for reconciling sessions with internal systems. Can be a customer ID, cart ID, or any other identifier.', ), - customer_update: appStripeCreateCheckoutSessionCustomerUpdate.optional(), - consent_collection: + customerUpdate: appStripeCreateCheckoutSessionCustomerUpdate.optional(), + consentCollection: appStripeCreateCheckoutSessionConsentCollection.optional(), currency: currencyCode.optional(), - custom_text: appStripeCheckoutSessionCustomTextParams.optional(), - expires_at: z.coerce + customText: appStripeCheckoutSessionCustomTextParams.optional(), + expiresAt: z.coerce .bigint() .gte(-9223372036854775808n) .lte(9223372036854775807n) @@ -4204,31 +4203,31 @@ export const appStripeCreateCheckoutSessionRequestOptions = z .describe( 'Set of key-value pairs to attach to the checkout session. Useful for storing additional structured information.', ), - return_url: z + returnUrl: z .string() .optional() .describe( 'Return URL for embedded checkout sessions after payment authentication. Required if ui_mode is "embedded" and redirect-based payment methods are enabled.', ), - success_url: z + successUrl: z .string() .optional() .describe( 'Success URL to redirect customers after completing payment or setup. Not allowed when ui_mode is "embedded". See: https://docs.stripe.com/payments/checkout/custom-success-page', ), - ui_mode: appStripeCheckoutSessionUiMode.optional().default('hosted'), - payment_method_types: z + uiMode: appStripeCheckoutSessionUiMode.optional().default('hosted'), + paymentMethodTypes: z .array(z.string()) .optional() .describe( 'List of payment method types to enable (e.g., "card", "us_bank_account"). If not specified, Stripe enables all relevant payment methods.', ), - redirect_on_completion: + redirectOnCompletion: appStripeCreateCheckoutSessionRedirectOnCompletion.optional(), - tax_id_collection: appStripeCreateCheckoutSessionTaxIdCollection.optional(), + taxIdCollection: appStripeCreateCheckoutSessionTaxIdCollection.optional(), }) .describe( @@ -4259,7 +4258,7 @@ export const taxCodePagePaginatedResponse = z export const invoiceWorkflowSettings = z .object({ apps: invoiceWorkflowAppsReferences.optional(), - source_billing_profile: profileReference, + sourceBillingProfile: profileReference, workflow: invoiceWorkflow, }) @@ -4284,20 +4283,20 @@ export const invoiceDetailedLine = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), - service_period: closedPeriod, + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), + servicePeriod: closedPeriod, totals: totals, category: invoiceDetailedLineCostCategory.default('regular'), discounts: invoiceLineDiscounts.optional(), - credits_applied: z + creditsApplied: z .array(invoiceLineCreditsApplied) .optional() .describe('Credit applied to this detailed line.'), - external_references: invoiceLineExternalReferences.optional(), + externalReferences: invoiceLineExternalReferences.optional(), quantity: numeric, - unit_price: numeric, + unitPrice: numeric, }) .describe( @@ -4326,7 +4325,7 @@ export const governanceQueryResult = z .describe( 'Map of features with their access status. Map keys are the feature keys requested in `feature.keys`, or every feature `key` available in the organization when the feature filter was omitted.', ), - updated_at: dateTime, + updatedAt: dateTime, }) .describe('Access evaluation result for a single resolved customer.') @@ -4364,12 +4363,12 @@ export const feature = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), key: resourceKey, meter: featureMeterReference.optional(), - unit_cost: featureUnitCost.optional(), + unitCost: featureUnitCost.optional(), }) .describe('A capability or billable dimension offered by a provider.') @@ -4391,13 +4390,13 @@ export const createFeatureRequest = z labels: labels.optional(), key: resourceKey, meter: featureMeterReference.optional(), - unit_cost: featureUnitCost.optional(), + unitCost: featureUnitCost.optional(), }) .describe('Feature create request.') export const updateFeatureRequest = z .object({ - unit_cost: z + unitCost: z .union([featureUnitCost, z.null()]) .optional() @@ -4421,7 +4420,7 @@ export const badRequest = z .intersection( baseError, z.object({ - invalid_parameters: invalidParameters, + invalidParameters: invalidParameters, }), ) .describe('Bad Request.') @@ -4438,23 +4437,23 @@ export const invoiceBase = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), number: invoiceNumber, currency: currencyCode, supplier: supplier, customer: invoiceCustomer, totals: totals, - service_period: closedPeriod, - validation_issues: z + servicePeriod: closedPeriod, + validationIssues: z .array(invoiceValidationIssue) .optional() .describe( 'Validation issues found during invoice processing. Present only when there are one or more validation findings. An empty list is omitted.', ), - external_references: invoiceExternalReferences.optional(), + externalReferences: invoiceExternalReferences.optional(), }) .describe( @@ -4463,7 +4462,7 @@ export const invoiceBase = z export const customerStripeCreateCheckoutSessionRequest = z .object({ - stripe_options: appStripeCreateCheckoutSessionRequestOptions, + stripeOptions: appStripeCreateCheckoutSessionRequestOptions, }) .describe( @@ -4533,34 +4532,34 @@ export const chargeFlatFee = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), type: z.literal('flat_fee').describe('The type of the charge.'), customer: billingCustomerReference, - lifecycle_controller: lifecycleController, + lifecycleController: lifecycleController, subscription: subscriptionReference.optional(), currency: currencyCode, status: chargeStatus, - invoice_at: dateTime, - service_period: closedPeriod, - full_service_period: closedPeriod, - billing_period: closedPeriod, - advance_after: dateTime.optional(), - unique_reference_id: z + invoiceAt: dateTime, + servicePeriod: closedPeriod, + fullServicePeriod: closedPeriod, + billingPeriod: closedPeriod, + advanceAfter: dateTime.optional(), + uniqueReferenceId: z .string() .optional() .describe('Unique reference ID of the charge.'), - settlement_mode: settlementMode, - tax_config: taxConfig.optional(), - payment_term: pricePaymentTerm, + settlementMode: settlementMode, + taxConfig: taxConfig.optional(), + paymentTerm: pricePaymentTerm, discounts: chargeFlatFeeDiscounts.optional(), - feature_key: z + featureKey: z .string() .optional() .describe('The feature associated with the charge, when applicable.'), - proration_configuration: rateCardProrationConfiguration, - amount_after_proration: currencyAmount, + prorationConfiguration: rateCardProrationConfiguration, + amountAfterProration: currencyAmount, price: price, }) .describe('A flat fee charge for a customer.') @@ -4582,28 +4581,28 @@ export const chargeUsageBased = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), type: z.literal('usage_based').describe('The type of the charge.'), customer: billingCustomerReference, - lifecycle_controller: lifecycleController, + lifecycleController: lifecycleController, subscription: subscriptionReference.optional(), currency: currencyCode, status: chargeStatus, - invoice_at: dateTime, - service_period: closedPeriod, - full_service_period: closedPeriod, - billing_period: closedPeriod, - advance_after: dateTime.optional(), - unique_reference_id: z + invoiceAt: dateTime, + servicePeriod: closedPeriod, + fullServicePeriod: closedPeriod, + billingPeriod: closedPeriod, + advanceAfter: dateTime.optional(), + uniqueReferenceId: z .string() .optional() .describe('Unique reference ID of the charge.'), - settlement_mode: settlementMode, - tax_config: taxConfig.optional(), + settlementMode: settlementMode, + taxConfig: taxConfig.optional(), discounts: rateCardDiscounts.optional(), - feature_key: z.string().describe('The feature associated with the charge.'), + featureKey: z.string().describe('The feature associated with the charge.'), totals: chargeTotals, price: price, }) @@ -4627,19 +4626,19 @@ export const createChargeUsageBasedRequest = z labels: labels.optional(), type: z.literal('usage_based').describe('The type of the charge.'), currency: currencyCode, - invoice_at: dateTime, - service_period: closedPeriod, - unique_reference_id: z + invoiceAt: dateTime, + servicePeriod: closedPeriod, + uniqueReferenceId: z .string() .optional() .describe('Unique reference ID of the charge.'), - settlement_mode: settlementMode, - tax_config: taxConfig.optional(), + settlementMode: settlementMode, + taxConfig: taxConfig.optional(), discounts: rateCardDiscounts.optional(), - feature_key: z.string().describe('The feature associated with the charge.'), + featureKey: z.string().describe('The feature associated with the charge.'), price: price, - full_service_period: closedPeriod.optional(), - billing_period: closedPeriod.optional(), + fullServicePeriod: closedPeriod.optional(), + billingPeriod: closedPeriod.optional(), }) .describe('Usage-based charge create request.') @@ -4661,13 +4660,13 @@ export const rateCard = z labels: labels.optional(), key: resourceKey, feature: featureReference.optional(), - billing_cadence: iso8601Duration.optional(), + billingCadence: iso8601Duration.optional(), price: price, - unit_config: unitConfig.optional(), - payment_term: pricePaymentTerm.optional().default('in_arrears'), + unitConfig: unitConfig.optional(), + paymentTerm: pricePaymentTerm.optional().default('in_arrears'), commitments: spendCommitments.optional(), discounts: rateCardDiscounts.optional(), - tax_config: rateCardTaxConfig.optional(), + taxConfig: rateCardTaxConfig.optional(), entitlement: rateCardEntitlement.optional(), }) @@ -4678,8 +4677,8 @@ export const rateCard = z export const invoiceLineRateCard = z .object({ price: price, - tax_config: rateCardTaxConfig.optional(), - feature_key: resourceKey.optional(), + taxConfig: rateCardTaxConfig.optional(), + featureKey: resourceKey.optional(), discounts: rateCardDiscounts.optional(), }) .describe('Rate card configuration snapshot for a usage-based invoice line.') @@ -4713,8 +4712,8 @@ export const createChargeRequest = z export const subscriptionAddonRateCard = z .object({ - rate_card: rateCard, - affected_subscription_item_ids: z + rateCard: rateCard, + affectedSubscriptionItemIds: z .array(ulid) .describe( @@ -4741,7 +4740,7 @@ export const planPhase = z labels: labels.optional(), key: resourceKey, duration: iso8601Duration.optional(), - rate_cards: z.array(rateCard).describe('The rate cards of the plan.'), + rateCards: z.array(rateCard).describe('The rate cards of the plan.'), }) .describe( @@ -4765,9 +4764,9 @@ export const addon = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), key: resourceKey, version: z .number() @@ -4778,13 +4777,13 @@ export const addon = z .describe( 'Version of the add-on. Incremented when the add-on is updated.', ), - instance_type: addonInstanceType, + instanceType: addonInstanceType, currency: billingCurrencyCode, - effective_from: dateTime.optional(), - effective_to: dateTime.optional(), + effectiveFrom: dateTime.optional(), + effectiveTo: dateTime.optional(), status: addonStatus, - rate_cards: z.array(rateCard).describe('The rate cards of the add-on.'), - validation_errors: z + rateCards: z.array(rateCard).describe('The rate cards of the add-on.'), + validationErrors: z .array(productCatalogValidationError) .optional() .describe('List of validation errors.'), @@ -4811,9 +4810,9 @@ export const createAddonRequest = z ), labels: labels.optional(), key: resourceKey, - instance_type: addonInstanceType, + instanceType: addonInstanceType, currency: billingCurrencyCode, - rate_cards: z.array(rateCard).describe('The rate cards of the add-on.'), + rateCards: z.array(rateCard).describe('The rate cards of the add-on.'), }) .describe('Addon create request.') @@ -4833,8 +4832,8 @@ export const upsertAddonRequest = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - instance_type: addonInstanceType, - rate_cards: z.array(rateCard).describe('The rate cards of the add-on.'), + instanceType: addonInstanceType, + rateCards: z.array(rateCard).describe('The rate cards of the add-on.'), }) .describe('Addon upsert request.') @@ -4855,24 +4854,24 @@ export const invoiceStandardLine = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), type: z .literal('standard_line') .describe('The type of charge this line item represents.'), - lifecycle_controller: lifecycleController, - service_period: closedPeriod, + lifecycleController: lifecycleController, + servicePeriod: closedPeriod, totals: totals, discounts: invoiceLineDiscounts.optional(), - credits_applied: z + creditsApplied: z .array(invoiceLineCreditsApplied) .optional() .describe('Credit applied to this line item.'), - external_references: invoiceLineExternalReferences.optional(), + externalReferences: invoiceLineExternalReferences.optional(), subscription: subscriptionReference.optional(), - rate_card: invoiceLineRateCard, - detailed_lines: z + rateCard: invoiceLineRateCard, + detailedLines: z .array(invoiceDetailedLine) .describe( @@ -4902,9 +4901,9 @@ export const profile = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), supplier: party, workflow: workflow, apps: profileAppReferences, @@ -4971,9 +4970,9 @@ export const subscriptionAddon = z .object({ id: ulid, labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), name: z .string() .min(1) @@ -4996,16 +4995,16 @@ export const subscriptionAddon = z .describe( 'The quantity of the add-on. Always 1 for single instance add-ons.', ), - quantity_at: dateTime, - active_from: dateTime, - active_to: dateTime.optional(), + quantityAt: dateTime, + activeFrom: dateTime, + activeTo: dateTime.optional(), timeline: z .array(subscriptionAddonTimelineSegment) .describe( 'The timeline of the add-on. The returned periods are sorted and continuous.', ), - rate_cards: z + rateCards: z .array(subscriptionAddonRateCard) .describe('The rate cards of the add-on.'), }) @@ -5028,9 +5027,9 @@ export const plan = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), key: resourceKey, version: z .number() @@ -5042,14 +5041,14 @@ export const plan = z 'Plans are versioned to allow you to make changes without affecting running subscriptions.', ), currency: currencyCode, - billing_cadence: iso8601Duration, - pro_rating_enabled: z + billingCadence: iso8601Duration, + proRatingEnabled: z .boolean() .optional() .default(true) .describe('Whether pro-rating is enabled for this plan.'), - effective_from: dateTime.optional(), - effective_to: dateTime.optional(), + effectiveFrom: dateTime.optional(), + effectiveTo: dateTime.optional(), status: planStatus, phases: z .array(planPhase) @@ -5058,8 +5057,8 @@ export const plan = z .describe( 'The plan phases define the pricing ramp for a subscription. A phase switch occurs only at the end of a billing period. At least one phase is required.', ), - settlement_mode: settlementMode.optional().default('credit_then_invoice'), - validation_errors: z + settlementMode: settlementMode.optional().default('credit_then_invoice'), + validationErrors: z .array(productCatalogValidationError) .optional() @@ -5087,8 +5086,8 @@ export const createPlanRequest = z labels: labels.optional(), key: resourceKey, currency: currencyCode, - billing_cadence: iso8601Duration, - pro_rating_enabled: z + billingCadence: iso8601Duration, + proRatingEnabled: z .boolean() .optional() .default(true) @@ -5119,7 +5118,7 @@ export const upsertPlanRequest = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - pro_rating_enabled: z + proRatingEnabled: z .boolean() .optional() .default(true) @@ -5181,34 +5180,34 @@ export const invoiceStandard = z 'Optional description of the resource. Maximum 1024 characters.', ), labels: labels.optional(), - created_at: dateTime, - updated_at: dateTime, - deleted_at: dateTime.optional(), + createdAt: dateTime, + updatedAt: dateTime, + deletedAt: dateTime.optional(), number: invoiceNumber, currency: currencyCode, supplier: supplier, customer: invoiceCustomer, totals: totals, - service_period: closedPeriod, - validation_issues: z + servicePeriod: closedPeriod, + validationIssues: z .array(invoiceValidationIssue) .optional() .describe( 'Validation issues found during invoice processing. Present only when there are one or more validation findings. An empty list is omitted.', ), - external_references: invoiceExternalReferences.optional(), + externalReferences: invoiceExternalReferences.optional(), type: z .literal('standard') .describe('Discriminator field identifying this as a standard invoice.'), status: invoiceStandardStatus, - status_details: invoiceStatusDetails, - issued_at: dateTime.optional(), - draft_until: dateTime.optional(), - quantity_snapshotted_at: dateTime.optional(), - collection_at: dateTime.optional(), - due_at: dateTime.optional(), - sent_to_customer_at: dateTime.optional(), + statusDetails: invoiceStatusDetails, + issuedAt: dateTime.optional(), + draftUntil: dateTime.optional(), + quantitySnapshottedAt: dateTime.optional(), + collectionAt: dateTime.optional(), + dueAt: dateTime.optional(), + sentToCustomerAt: dateTime.optional(), workflow: invoiceWorkflowSettings, lines: z .array(invoiceLine) @@ -5238,7 +5237,7 @@ export const listMeteringEventsResponse = z.object({ meta: cursorMeta, }) -export const ingestMeteringEventsBody = event +export const ingestMeteringEventsBody = z.union([event, z.array(event)]) export const createMeterBody = createMeterRequest @@ -5295,6 +5294,8 @@ export const queryMeterCsvPathParams = z.object({ meterId: ulid, }) +export const queryMeterCsvBody = meterQueryRequest + export const queryMeterCsvResponse = z.string() export const createCustomerBody = createCustomerRequest @@ -5697,7 +5698,7 @@ export const listTaxCodesQueryParams = z.object({ }) .optional() .describe('Determines which page of the collection to retrieve.'), - include_deleted: z.coerce + includeDeleted: z.coerce .boolean() .optional() .describe('Include deleted tax codes in the response.'), @@ -6061,3 +6062,6093 @@ export const queryGovernanceAccessQueryParams = z.object({ export const queryGovernanceAccessBody = governanceQueryRequest export const queryGovernanceAccessResponse = governanceQueryResponse + +export const labelsWire = z + .record(z.string(), z.string()) + + .describe( + 'Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "\\_".', + ) + +export const currencyCodeWire = z + .string() + .min(3) + .max(3) + .regex(new RegExp('^[A-Z]{3}$')) + + .describe( + 'Three-letter [ISO4217](https://www.iso.org/iso-4217-currency-codes.html) currency code. Custom three-letter currency codes are also supported for convenience.', + ) + +export const numericWire = z + .string() + .regex(new RegExp('^\\-?[0-9]+(\\.[0-9]+)?$')) + .describe('Numeric represents an arbitrary precision number.') + +export const cursorPaginationQueryPageWire = z + .strictObject({ + size: z + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + after: z + .string() + .optional() + + .describe( + 'Request the next page of data, starting with the item after this parameter.', + ), + before: z + .string() + .optional() + + .describe( + 'Request the previous page of data, starting with the item before this parameter.', + ), + }) + .describe('Determines which page of the collection to retrieve.') + +export const stringFieldFilterWire = z + .union([ + z.string(), + z.strictObject({ + eq: z + .string() + .optional() + .describe('Value strictly equals the given string value.'), + neq: z + .string() + .optional() + .describe('Value does not equal the given string value.'), + contains: z + .string() + .optional() + .describe('Value contains the given string value (fuzzy match).'), + ocontains: z + .array(z.string()) + .optional() + + .describe( + 'Returns entities that fuzzy-match any of the comma-delimited phrases in the filter string.', + ), + oeq: z + .array(z.string()) + .optional() + + .describe( + 'Returns entities that exact match any of the comma-delimited phrases in the filter string.', + ), + gt: z + .string() + .optional() + + .describe( + 'Value is greater than the given string value (lexicographic compare).', + ), + gte: z + .string() + .optional() + + .describe( + 'Value is greater than or equal to the given string value (lexicographic compare).', + ), + lt: z + .string() + .optional() + + .describe( + 'Value is less than the given string value (lexicographic compare).', + ), + lte: z + .string() + .optional() + + .describe( + 'Value is less than or equal to the given string value (lexicographic compare).', + ), + exists: z + .boolean() + .optional() + + .describe( + 'When true, the field must be present (non-null); when false, the field must be absent (null).', + ), + }), + ]) + + .describe( + 'Filters on the given string field value by either exact or fuzzy match. All properties are optional; provide exactly one to specify the comparison.', + ) + +export const ulidWire = z + .string() + .regex(new RegExp('^[0-7][0-9A-HJKMNP-TV-Z]{25}$')) + .describe('ULID (Universally Unique Lexicographically Sortable Identifier).') + +export const dateTimeWire = z + .string() + .datetime() + + .describe( + '[RFC3339](https://tools.ietf.org/html/rfc3339) formatted date-time string in UTC.', + ) + +export const sortQueryWire = z + .strictObject({ + by: z.string().describe('The attribute to sort by.'), + order: z + .union([z.literal('asc'), z.literal('desc')]) + .optional() + .default('asc') + .describe('The sort order. `asc` for ascending, `desc` for descending.'), + }) + + .describe( + 'Sort query. The `asc` suffix is optional as the default sort order is ascending. The `desc` suffix is used to specify a descending order.', + ) + +export const ingestedEventValidationErrorWire = z + .strictObject({ + code: z.string().describe('The machine readable code of the error.'), + message: z + .string() + .describe('The human readable description of the error.'), + attributes: z + .record(z.string(), z.unknown()) + .optional() + .describe('Additional attributes.'), + }) + .describe('Event validation errors.') + +export const cursorMetaPageWire = z + .strictObject({ + first: z.string().optional().describe('URI to the first page.'), + last: z.string().optional().describe('URI to the last page.'), + next: z.string().optional().describe('URI to the next page.'), + previous: z.string().optional().describe('URI to the previous page.'), + size: z.number().int().optional().describe('Requested page size.'), + }) + .describe('Cursor pagination metadata.') + +export const invalidRulesWire = z + .enum([ + 'required', + 'is_array', + 'is_base64', + 'is_boolean', + 'is_date_time', + 'is_integer', + 'is_null', + 'is_number', + 'is_object', + 'is_string', + 'is_uuid', + 'is_fqdn', + 'is_arn', + 'unknown_property', + 'missing_reference', + 'is_label', + 'matches_regex', + 'invalid', + 'is_supported_network_availability_zone_list', + 'is_supported_network_cidr_block', + 'is_supported_provider_region', + 'type', + ]) + .describe('The validation rule that a parameter failed.') + +export const invalidParameterMinimumRuleWire = z + .enum([ + 'min_length', + 'min_digits', + 'min_lowercase', + 'min_uppercase', + 'min_symbols', + 'min_items', + 'min', + ]) + .describe('Minimum-length (or minimum-value) validation rules.') + +export const invalidParameterMaximumRuleWire = z + .enum(['max_length', 'max_items', 'max']) + .describe('Maximum-length (or maximum-value) validation rules.') + +export const invalidParameterChoiceRuleWire = z + .enum(['enum']) + .describe('The enum validation rule.') + +export const invalidParameterDependentRuleWire = z + .enum(['dependent_fields']) + .describe('The dependent-fields validation rule.') + +export const baseErrorWire = z + .intersection( + z.object({ + type: z + .string() + .default('about:blank') + .describe('Type contains a URI that identifies the problem type.'), + status: z + .number() + .int() + + .describe( + 'The HTTP status code generated by the origin server for this occurrence of the problem.', + ), + title: z + .string() + .describe('A a short, human-readable summary of the problem type.'), + detail: z + .string() + + .describe( + 'A human-readable explanation specific to this occurrence of the problem.', + ), + instance: z + .string() + + .describe( + 'A URI reference that identifies the specific occurrence of the problem.', + ), + }), + z.record(z.string(), z.unknown()), + ) + .describe('Standard error response.') + +export const resourceKeyWire = z + .string() + .min(1) + .max(64) + .regex(new RegExp('^[a-z0-9]+(?:_[a-z0-9]+)*$')) + .describe('A key is a unique string that is used to identify a resource.') + +export const meterAggregationWire = z + .union([ + z.literal('sum'), + z.literal('count'), + z.literal('unique_count'), + z.literal('avg'), + z.literal('min'), + z.literal('max'), + z.literal('latest'), + ]) + .describe('The aggregation type to use for the meter.') + +export const pageMetaWire = z + .strictObject({ + number: z.number().int().describe('Page number.'), + size: z.number().int().describe('Page size.'), + total: z + .number() + .int() + .describe('Total number of items in the collection.'), + }) + .describe('Pagination information.') + +export const meterQueryGranularityWire = z + .union([ + z.literal('PT1M'), + z.literal('PT1H'), + z.literal('P1D'), + z.literal('P1M'), + ]) + + .describe( + 'The granularity of the time grouping. Time durations are specified in ISO 8601 format.', + ) + +export const queryFilterStringWire = z + .strictObject({ + eq: z + .string() + .optional() + .describe('The attribute equals the provided value.'), + neq: z + .string() + .optional() + .describe('The attribute does not equal the provided value.'), + in: z + .array(z.string()) + .min(1) + .max(100) + .optional() + .describe('The attribute is one of the provided values.'), + nin: z + .array(z.string()) + .min(1) + .max(100) + .optional() + .describe('The attribute is not one of the provided values.'), + contains: z + .string() + .optional() + .describe('The attribute contains the provided value.'), + ncontains: z + .string() + .optional() + .describe('The attribute does not contain the provided value.'), + get and() { + return z + .array(queryFilterStringWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical AND.') + }, + get or() { + return z + .array(queryFilterStringWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical OR.') + }, + }) + + .describe( + 'A query filter for a string attribute. Operators are mutually exclusive, only one operator is allowed at a time.', + ) + +export const externalResourceKeyWire = z + .string() + .min(1) + .max(256) + + .describe( + 'ExternalResourceKey is a unique string that is used to identify a resource in an external system.', + ) + +export const usageAttributionSubjectKeyWire = z + .string() + .min(1) + .describe('Subject key.') + +export const countryCodeWire = z + .string() + .min(2) + .max(2) + .regex(new RegExp('^[A-Z]{2}$')) + + .describe( + '[ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) alpha-2 country code. Custom two-letter country codes are also supported for convenience.', + ) + +export const appStripeCreateCheckoutSessionBillingAddressCollectionWire = z + .enum(['auto', 'required']) + + .describe( + "Controls whether Checkout collects the customer's billing address.", + ) + +export const appStripeCreateCheckoutSessionCustomerUpdateBehaviorWire = z + .enum(['auto', 'never']) + .describe('Behavior for updating customer fields from checkout session.') + +export const appStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreementPositionWire = + z + .enum(['auto', 'hidden']) + .describe('Position of payment method reuse agreement in the UI.') + +export const appStripeCreateCheckoutSessionConsentCollectionPromotionsWire = z + .enum(['auto', 'none']) + .describe('Promotional communication consent collection setting.') + +export const appStripeCreateCheckoutSessionConsentCollectionTermsOfServiceWire = + z + .enum(['none', 'required']) + .describe('Terms of service acceptance requirement.') + +export const appStripeCheckoutSessionCustomTextParamsWire = z + .strictObject({ + after_submit: z + .strictObject({ + message: z + .string() + .max(1200) + .optional() + .describe('The custom message text (max 1200 characters).'), + }) + .optional() + .describe('Text displayed after the payment confirmation button.'), + shipping_address: z + .strictObject({ + message: z + .string() + .max(1200) + .optional() + .describe('The custom message text (max 1200 characters).'), + }) + .optional() + .describe('Text displayed alongside shipping address collection.'), + submit: z + .strictObject({ + message: z + .string() + .max(1200) + .optional() + .describe('The custom message text (max 1200 characters).'), + }) + .optional() + .describe('Text displayed alongside the payment confirmation button.'), + terms_of_service_acceptance: z + .strictObject({ + message: z + .string() + .max(1200) + .optional() + .describe('The custom message text (max 1200 characters).'), + }) + .optional() + .describe('Text replacing the default terms of service agreement text.'), + }) + .describe('Custom text displayed at various stages of the checkout flow.') + +export const appStripeCheckoutSessionUiModeWire = z + .enum(['embedded', 'hosted']) + .describe('Checkout Session UI mode.') + +export const appStripeCreateCheckoutSessionRedirectOnCompletionWire = z + .enum(['always', 'if_required', 'never']) + .describe('Redirect behavior for embedded checkout sessions.') + +export const appStripeCreateCheckoutSessionTaxIdCollectionRequiredWire = z + .enum(['if_supported', 'never']) + .describe('Tax ID collection requirement level.') + +export const appStripeCheckoutSessionModeWire = z + .enum(['setup']) + + .describe( + 'Stripe Checkout Session mode. Determines the primary purpose of the checkout session.', + ) + +export const appStripeCreateCustomerPortalSessionOptionsWire = z + .strictObject({ + configuration_id: z + .string() + .optional() + + .describe( + 'The ID of an existing [Stripe configuration](https://docs.stripe.com/api/customer_portal/configurations) to use for this session, describing its functionality and features. If not specified, the session uses the default configuration.', + ), + locale: z + .string() + .optional() + + .describe( + "The IETF [language tag](https://docs.stripe.com/api/customer_portal/sessions/create#create_portal_session-locale) of the locale customer portal is displayed in. If blank or `auto`, the customer's preferred_locales or browser's locale is used.", + ), + return_url: z + .string() + .optional() + + .describe( + 'The [URL to redirect](https://docs.stripe.com/api/customer_portal/sessions/create#create_portal_session-return_url) the customer to after they have completed their requested actions.', + ), + }) + .describe('Request to create a Stripe Customer Portal Session.') + +export const entitlementTypeWire = z + .enum(['metered', 'static', 'boolean']) + .describe('The type of the entitlement.') + +export const createLabelsWire = z + .record(z.string(), z.string()) + + .describe( + 'Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "\\_".', + ) + +export const creditFundingMethodWire = z + .enum(['none', 'invoice', 'external']) + + .describe( + 'The funding method describes how the grant is funded. - `none`: No funding workflow applies, for example promotional grants - `invoice`: The grant is funded by an in-system invoice flow - `external`: The grant is funded outside the system (e.g., wire transfer, external invoice, or manual reconciliation)', + ) + +export const creditAvailabilityPolicyWire = z + .enum(['on_creation']) + + .describe( + 'When credits become available for consumption. - `on_creation`: Credits are available as soon as the grant is created. - `on_authorization`: Credits are available once the payment is authorized. - `on_settlement`: Credits are available once the payment is settled.', + ) + +export const taxBehaviorWire = z + .enum(['inclusive', 'exclusive']) + + .describe( + 'Tax behavior. This enum is used to specify whether tax is included in the price or excluded from the price.', + ) + +export const iso8601DurationWire = z + .string() + + .regex( + new RegExp( + '^P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$', + ), + ) + + .describe( + '[ISO 8601 Duration](https://docs.digi.com/resources/documentation/digidocs/90001488-13/reference/r_iso_8601_duration_format.htm) string.', + ) + +export const creditPurchasePaymentSettlementStatusWire = z + .enum(['pending', 'authorized', 'settled']) + + .describe( + 'Credit purchase payment settlement status. - `pending`: Payment has been initiated and is not yet authorized. - `authorized`: Payment has been authorized. - `settled`: Payment has been settled.', + ) + +export const creditGrantStatusWire = z + .enum(['pending', 'active', 'expired', 'voided']) + + .describe( + 'Credit grant lifecycle status. - `pending`: The credit block has been created but is not yet valid. (`effective_at` is in the future or availability_policy is not met) - `active`: The credit block is currently valid and eligible for consumption. (`effective_at` is in the past, `expires_at` is in the future and availability_policy is met) - `expired`: The credit block expired with remaining unused balance, `expires_at` time has passed. - `voided`: The credit block was voided. Remaining balance is forfeited.', + ) + +export const stringFieldFilterExactWire = z + .union([ + z.string(), + z.strictObject({ + eq: z + .string() + .optional() + .describe('Value strictly equals the given string value.'), + oeq: z + .array(z.string()) + .optional() + + .describe( + 'Returns entities that exact match any of the comma-delimited phrases in the filter string.', + ), + neq: z + .string() + .optional() + .describe('Value does not equal the given string value.'), + }), + ]) + + .describe( + 'Filters on the given string field value by exact match. All properties are optional; provide exactly one to specify the comparison.', + ) + +export const creditTransactionTypeWire = z + .enum(['funded', 'consumed', 'expired']) + + .describe( + 'The type of the credit transaction. - `funded`: Credit granted and available for consumption. - `consumed`: Credit consumed by usage or fees. - `expired`: Credit removed because it expired before being used.', + ) + +export const chargesExpandWire = z + .enum(['real_time_usage']) + + .describe( + "Expands for customer charges. Values: - `real_time_usage`: The charge's real-time usage.", + ) + +export const lifecycleControllerWire = z + .enum(['system', 'manual']) + + .describe( + 'Identifies whether a resource lifecycle is controlled by OpenMeter or manually overridden by the API user. Values: - `system`: The resource lifecycle is controlled by OpenMeter. - `manual`: The resource lifecycle was manually overridden by the API user.', + ) + +export const chargeStatusWire = z + .enum(['created', 'active', 'final', 'deleted']) + + .describe( + 'Lifecycle status of a charge. Values: - `created`: The charge has been created but is not active yet. - `active`: The charge is active. - `final`: The charge is fully finalized and no further changes are expected. - `deleted`: The charge has been deleted.', + ) + +export const settlementModeWire = z + .enum(['credit_then_invoice', 'credit_only']) + + .describe( + 'Settlement mode for billing. Values: - `credit_then_invoice`: Credits are applied first, then any remainder is invoiced. - `credit_only`: Usage is settled exclusively against credits.', + ) + +export const taxConfigStripeWire = z + .strictObject({ + code: z + .string() + .regex(new RegExp('^txcd_\\d{8}$')) + .describe('Product [tax code](https://docs.stripe.com/tax/tax-codes).'), + }) + .describe('The tax config for Stripe.') + +export const taxConfigExternalInvoicingWire = z + .strictObject({ + code: z + .string() + .max(64) + + .describe( + 'The tax code should be interpreted by the external invoicing provider.', + ), + }) + .describe('External invoicing tax config.') + +export const pricePaymentTermWire = z + .union([z.literal('in_advance'), z.literal('in_arrears')]) + .describe('The payment term of a flat price.') + +export const chargeFlatFeeDiscountsWire = z + .strictObject({ + percentage: z + .number() + .nonnegative() + .lte(100) + .optional() + .describe('Percentage discount applied to the price (0–100).'), + }) + + .describe( + 'Discounts applicable to flat fee charges. This is the same as `ProductCatalog.Discounts` but without the `usage` field, which is not applicable to flat fee charges.', + ) + +export const rateCardProrationModeWire = z + .enum(['no_proration', 'prorate_prices']) + + .describe( + 'The proration mode of the rate card. Values: - `no_proration`: No proration. - `prorate_prices`: Prorate the price based on the time remaining in the billing period.', + ) + +export const priceFreeWire = z + .strictObject({ + type: z.literal('free').describe('The type of the price.'), + }) + .describe('Free price.') + +export const subscriptionStatusWire = z + .enum(['active', 'inactive', 'canceled', 'scheduled']) + .describe('Subscription status.') + +export const subscriptionEditTimingEnumWire = z + .enum(['immediate', 'next_billing_cycle']) + + .describe( + 'Subscription edit timing. When immediate, the requested changes take effect immediately. When next_billing_cycle, the requested changes take effect at the next billing cycle.', + ) + +export const unitConfigOperationWire = z + .enum(['divide', 'multiply']) + + .describe( + 'The arithmetic operation used to convert raw metered units into billing units. - `divide`: Divide the metered quantity by the conversion factor (e.g., bytes ÷ 1e9 = GB). - `multiply`: Multiply the metered quantity by the conversion factor (e.g., cost × 1.2 = cost + 20% margin).', + ) + +export const unitConfigRoundingModeWire = z + .enum(['ceiling', 'floor', 'half_up', 'none']) + + .describe( + 'The rounding mode applied to the converted quantity for invoicing. Rounding is applied only to the invoiced quantity. Entitlement balance checks use the precise decimal value after conversion. - `ceiling`: Round up to the next integer (typical for package-style billing). - `floor`: Round down to the previous integer. - `half_up`: Round to the nearest integer, with 0.5 rounding up. - `none`: No rounding; the converted value is used as-is.', + ) + +export const rateCardStaticEntitlementWire = z + .strictObject({ + type: z.literal('static').describe('The type of the entitlement template.'), + config: z + .unknown() + + .describe( + 'The entitlement config as a JSON object. Returned when checking entitlement access; useful for configuring fine-grained access settings implemented in your own system.', + ), + }) + .describe('The entitlement template of a static entitlement.') + +export const rateCardBooleanEntitlementWire = z + .strictObject({ + type: z + .literal('boolean') + .describe('The type of the entitlement template.'), + }) + .describe('The entitlement template of a boolean entitlement.') + +export const appTypeWire = z + .enum(['sandbox', 'stripe', 'external_invoicing']) + .describe('The type of the app.') + +export const appStatusWire = z + .enum(['ready', 'unauthorized']) + .describe('Connection status of an installed app.') + +export const taxIdentificationCodeWire = z + .string() + .min(1) + .max(32) + + .describe( + 'Tax identifier code is a normalized tax code shown on the original identity document.', + ) + +export const workflowCollectionAlignmentSubscriptionWire = z + .strictObject({ + type: z.literal('subscription').describe('The type of alignment.'), + }) + + .describe( + 'BillingWorkflowCollectionAlignmentSubscription specifies the alignment for collecting the pending line items into an invoice.', + ) + +export const workflowInvoicingSubscriptionEndProrationModeWire = z + .enum(['bill_full_period', 'bill_actual_period']) + .describe('Billing workflow subscription end proration mode.') + +export const workflowPaymentChargeAutomaticallySettingsWire = z + .strictObject({ + collection_method: z + .literal('charge_automatically') + .describe('The collection method for the invoice.'), + }) + + .describe( + 'Payment settings for a billing workflow when the collection method is charge automatically.', + ) + +export const workflowPaymentSendInvoiceSettingsWire = z + .strictObject({ + collection_method: z + .literal('send_invoice') + .describe('The collection method for the invoice.'), + due_after: z + .string() + .optional() + .default('P30D') + + .describe( + "The period after which the invoice is due. With some payment solutions it's only applicable for manual collection method.", + ), + }) + + .describe( + 'Payment settings for a billing workflow when the collection method is send invoice.', + ) + +export const invoiceNumberWire = z + .string() + .min(1) + .max(256) + + .describe( + 'InvoiceNumber is a unique identifier for the invoice, generated by the invoicing app. The uniqueness depends on a lot of factors: - app setting (unique per app or unique per customer) - multiple app scenarios (multiple apps generating invoices with the same prefix)', + ) + +export const invoiceValidationIssueSeverityWire = z + .enum(['critical', 'warning']) + .describe('Severity level of an invoice validation issue.') + +export const invoiceExternalReferencesWire = z + .strictObject({ + invoicing_id: z + .string() + .optional() + + .describe( + 'The ID assigned by the external invoicing app (e.g. Stripe invoice ID).', + ), + payment_id: z + .string() + .optional() + + .describe( + 'The ID assigned by the external payment app (e.g. Stripe payment intent ID).', + ), + }) + + .describe( + 'External identifiers assigned to an invoice by third-party systems.', + ) + +export const invoiceStandardStatusWire = z + .enum([ + 'draft', + 'issuing', + 'issued', + 'payment_processing', + 'overdue', + 'paid', + 'uncollectible', + 'voided', + ]) + .describe('Lifecycle status of a standard invoice.') + +export const invoiceAvailableActionDetailsWire = z + .strictObject({ + resulting_state: z + .string() + + .describe( + 'The extended status the invoice will transition to after performing this action.', + ), + }) + + .describe( + 'Details about an available invoice action including the resulting state.', + ) + +export const invoiceWorkflowInvoicingSettingsWire = z + .strictObject({ + auto_advance: z + .boolean() + .optional() + .default(true) + + .describe( + 'Whether to automatically issue the invoice after the draft_period has passed.', + ), + draft_period: z + .string() + .optional() + .default('P0D') + + .describe( + 'The period for the invoice to be kept in draft status for manual reviews.', + ), + }) + + .describe( + 'Invoice-level invoicing settings. A subset of BillingWorkflowInvoicingSettings limited to fields that are meaningful per-invoice. progressive_billing is omitted as it is a gather-time / profile-level decision.', + ) + +export const invoiceDiscountReasonWire = z + .enum(['maximum_spend', 'ratecard_percentage', 'ratecard_usage']) + .describe('The reason a discount was applied to an invoice line.') + +export const invoiceLineExternalReferencesWire = z + .strictObject({ + invoicing_id: z + .string() + .optional() + .describe('The ID assigned by the external invoicing app.'), + }) + + .describe( + 'External identifiers for an invoice line item assigned by third-party systems.', + ) + +export const invoiceDetailedLineCostCategoryWire = z + .enum(['regular', 'commitment']) + .describe('Cost category of a detailed invoice line item.') + +export const currencyTypeWire = z + .enum(['fiat', 'custom']) + + .describe( + 'Currency type for custom currencies. It should be a unique code but not conflicting with any existing standard currency codes.', + ) + +export const currencyCodeCustomWire = z + .string() + .min(3) + .max(24) + + .describe( + 'Custom currency code. It should be a unique code but not conflicting with any existing fiat currency codes.', + ) + +export const featureLlmTokenTypeWire = z + .enum([ + 'input', + 'output', + 'cache_read', + 'cache_write', + 'reasoning', + 'request', + 'response', + ]) + .describe('Token type for LLM cost lookup.') + +export const llmCostProviderWire = z + .strictObject({ + id: z + .string() + .describe('Identifier of the provider, e.g., "openai", "anthropic".'), + name: z + .string() + .describe('Name of the provider, e.g., "OpenAI", "Anthropic".'), + }) + .describe('LLM Provider') + +export const llmCostModelWire = z + .strictObject({ + id: z + .string() + + .describe('Identifier of the model, e.g., "gpt-4", "claude-3-5-sonnet".'), + name: z + .string() + .describe('Name of the model, e.g., "GPT-4", "Claude 3.5 Sonnet".'), + }) + .describe('LLM Model') + +export const llmCostPriceSourceWire = z + .enum(['manual', 'system']) + .describe('Identifies where an LLM cost price came from.') + +export const planStatusWire = z + .enum(['draft', 'active', 'archived', 'scheduled']) + + .describe( + 'The status of a plan. - `draft`: The plan has not yet been published and can be edited. - `active`: The plan is published and can be used in subscriptions. - `archived`: The plan is no longer available for use. - `scheduled`: The plan is scheduled to be published at a future date.', + ) + +export const productCatalogValidationErrorWire = z + .strictObject({ + code: z.string().describe('Machine-readable error code.'), + message: z.string().describe('Human-readable description of the error.'), + attributes: z + .record(z.string(), z.unknown()) + .optional() + .describe('Additional structured context.'), + field: z.string().describe('The path to the field.'), + }) + .describe('Validation errors providing detailed description of the issue.') + +export const addonInstanceTypeWire = z + .enum(['single', 'multiple']) + + .describe( + 'The instanceType of the add-on. - `single`: Can be added to a subscription only once. - `multiple`: Can be added to a subscription more than once.', + ) + +export const addonStatusWire = z + .enum(['draft', 'active', 'archived']) + + .describe( + 'The status of the add-on defined by the `effective_from` and `effective_to` properties. - `draft`: The add-on has not yet been published and can be edited. - `active`: The add-on is published and available for use. - `archived`: The add-on is no longer available for use.', + ) + +export const governanceQueryRequestCustomersWire = z + .strictObject({ + keys: z + .array(z.string()) + .min(1) + .max(100) + + .describe( + 'Each entry can be a customer `key` or a usage-attribution subject `key`. Identifiers that cannot be resolved to a customer are reported in the response `errors` array.', + ), + }) + .describe('List of customer identifiers to evaluate access for.') + +export const governanceQueryRequestFeaturesWire = z + .strictObject({ + keys: z + .array(z.string()) + .min(1) + .max(100) + .describe('List of feature keys to evaluate access for.'), + }) + + .describe( + 'Optional list of feature keys to evaluate access for. If omitted, all features available in the organization are returned. Providing this list is recommended to reduce the response size and the load on the backend services.', + ) + +export const governanceFeatureAccessReasonCodeWire = z + .enum([ + 'unknown', + 'usage_limit_reached', + 'feature_unavailable', + 'feature_not_found', + 'no_credit_available', + ]) + .describe('Machine-readable reason code for denied feature access.') + +export const governanceQueryErrorCodeWire = z + .enum(['unknown', 'customer_not_found']) + .describe('Error code for a governance query failure.') + +export const queryFilterIntegerWire = z + .strictObject({ + eq: z + .number() + .int() + .optional() + .describe('The attribute equals the provided value.'), + neq: z + .number() + .int() + .optional() + .describe('The attribute does not equal the provided value.'), + in: z + .array(z.number().int()) + .min(1) + .max(100) + .optional() + .describe('The attribute is one of the provided values.'), + nin: z + .array(z.number().int()) + .min(1) + .max(100) + .optional() + .describe('The attribute is not one of the provided values.'), + gt: z + .number() + .int() + .optional() + .describe('The attribute is greater than the provided value.'), + gte: z + .number() + .int() + .optional() + + .describe( + 'The attribute is greater than or equal to the provided value.', + ), + lt: z + .number() + .int() + .optional() + .describe('The attribute is less than the provided value.'), + lte: z + .number() + .int() + .optional() + .describe('The attribute is less than or equal to the provided value.'), + get and() { + return z + .array(queryFilterIntegerWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical AND.') + }, + get or() { + return z + .array(queryFilterIntegerWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical OR.') + }, + }) + + .describe( + 'A query filter for an integer attribute. Operators are mutually exclusive, only one operator is allowed at a time.', + ) + +export const queryFilterFloatWire = z + .strictObject({ + gt: z + .number() + .optional() + .describe('The attribute is greater than the provided value.'), + gte: z + .number() + .optional() + + .describe( + 'The attribute is greater than or equal to the provided value.', + ), + lt: z + .number() + .optional() + .describe('The attribute is less than the provided value.'), + lte: z + .number() + .optional() + .describe('The attribute is less than or equal to the provided value.'), + get and() { + return z + .array(queryFilterFloatWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical AND.') + }, + get or() { + return z + .array(queryFilterFloatWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical OR.') + }, + }) + + .describe( + 'A query filter for a float attribute. Operators are mutually exclusive, only one operator is allowed at a time.', + ) + +export const queryFilterBooleanWire = z + .strictObject({ + eq: z + .boolean() + .optional() + .describe('The attribute equals the provided value.'), + }) + + .describe( + 'A query filter for a boolean attribute. Operators are mutually exclusive, only one operator is allowed at a time.', + ) + +export const pagePaginationQueryWire = z + .strictObject({ + page: z + .strictObject({ + size: z + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + }) + .describe('Page pagination query.') + +export const publicLabelsWire = z + .record(z.string(), z.string()) + + .describe( + 'Public labels store information about an entity that can be used for filtering a list of objects.', + ) + +export const booleanFieldFilterWire = z + .union([ + z.boolean(), + z.strictObject({ + eq: z + .boolean() + .describe('Value strictly equals the given boolean value.'), + }), + ]) + .describe('Filter by a boolean value (true/false).') + +export const numericFieldFilterWire = z + .union([ + z.number(), + z.strictObject({ + eq: z + .number() + .optional() + .describe('Value strictly equals the given numeric value.'), + neq: z + .number() + .optional() + .describe('Value does not equal the given numeric value.'), + oeq: z + .array(z.number()) + .optional() + + .describe( + 'Returns entities that match any of the comma-delimited numeric values.', + ), + lt: z + .number() + .optional() + .describe('Value is less than the given numeric value.'), + lte: z + .number() + .optional() + .describe('Value is less than or equal to the given numeric value.'), + gt: z + .number() + .optional() + .describe('Value is greater than the given numeric value.'), + gte: z + .number() + .optional() + .describe('Value is greater than or equal to the given numeric value.'), + }), + ]) + + .describe( + 'Filter by a numeric value. All properties are optional; provide exactly one to specify the comparison.', + ) + +export const chargeTypeWire = z + .enum(['flat_fee', 'usage_based']) + + .describe( + 'Type of a charge. Values: - `flat_fee`: A fixed-amount charge. - `usage_based`: A usage-priced charge.', + ) + +export const invoiceTypeWire = z + .enum(['standard']) + .describe('The type of a billing invoice.') + +export const invoiceLineTypeWire = z + .enum(['standard_line']) + .describe('Line item type discriminator.') + +export const priceTypeWire = z + .enum(['free', 'flat', 'unit', 'graduated', 'volume']) + + .describe( + "The type of the price. - `free`: No charge, the rate card is included at no cost. - `flat`: A fixed amount charged once per billing period, regardless of usage. - `unit`: A fixed rate charged per billing unit consumed. - `graduated`: Tiered pricing where each tier's rate applies only to usage within that tier. - `volume`: Tiered pricing where the rate for the highest tier reached applies to all units in the period.", + ) + +export const collectionAlignmentWire = z + .enum(['subscription', 'anchored']) + + .describe( + 'BillingCollectionAlignment specifies when the pending line items should be collected into an invoice.', + ) + +export const collectionMethodWire = z + .enum(['charge_automatically', 'send_invoice']) + + .describe( + 'Collection method specifies how the invoice should be collected (automatic or manual).', + ) + +export const featureUnitCostTypeWire = z + .enum(['llm', 'manual']) + .describe('The type of unit cost.') + +export const systemAccountAccessTokenWire = z + .strictObject({ + type: z.literal('http').describe('Http authentication'), + scheme: z.literal('Bearer').describe('bearer auth scheme'), + }) + + .describe( + 'The system account access token is meant for automations and integrations that are not directly associated with a human identity.', + ) + +export const personalAccessTokenWire = z + .strictObject({ + type: z.literal('http').describe('Http authentication'), + scheme: z.literal('Bearer').describe('bearer auth scheme'), + }) + + .describe( + 'The personal access token is meant to be used as an alternative to basic-auth when accessing Konnect via APIs.', + ) + +export const konnectAccessTokenWire = z + .strictObject({ + type: z.literal('http').describe('Http authentication'), + scheme: z.literal('Bearer').describe('bearer auth scheme'), + }) + + .describe( + 'The Konnect access token is meant to be used by the Konnect dashboard and the decK CLI authenticate with.', + ) + +export const updateMeterRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .optional() + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + dimensions: z + .record(z.string(), z.string()) + .optional() + + .describe( + 'Named JSONPath expressions to extract the group by values from the event data. Keys must be unique and consist only alphanumeric and underscore characters.', + ), + }) + .describe('Meter update request.') + +export const appCustomerDataStripeWire = z + .strictObject({ + customer_id: z.string().optional().describe('The Stripe customer ID used.'), + default_payment_method_id: z + .string() + .optional() + .describe('The Stripe default payment method ID.'), + labels: labelsWire.optional(), + }) + .describe('Stripe customer data.') + +export const appCustomerDataExternalInvoicingWire = z + .strictObject({ + labels: labelsWire.optional(), + }) + .describe('External invoicing customer data.') + +export const billingCurrencyCodeWire = z + .union([currencyCodeWire]) + .describe('Fiat or custom currency code.') + +export const createCurrencyCodeWire = z + .union([currencyCodeWire]) + .describe('Fiat or custom currency code.') + +export const listCostBasesParamsFilterWire = z + .strictObject({ + fiat_code: currencyCodeWire.optional(), + }) + .describe('Filter options for listing cost bases.') + +export const currencyAmountWire = z + .strictObject({ + amount: numericWire, + currency: currencyCodeWire, + }) + .describe('Monetary amount in a specific currency.') + +export const priceFlatWire = z + .strictObject({ + type: z.literal('flat').describe('The type of the price.'), + amount: numericWire, + }) + .describe('Flat price.') + +export const priceUnitWire = z + .strictObject({ + type: z.literal('unit').describe('The type of the price.'), + amount: numericWire, + }) + + .describe( + 'Unit price. Charges a fixed rate per billing unit. When UnitConfig is present on the rate card, billing units are the converted quantities (e.g. GB instead of bytes).', + ) + +export const rateCardDiscountsWire = z + .strictObject({ + percentage: z + .number() + .nonnegative() + .lte(100) + .optional() + .describe('Percentage discount applied to the price (0–100).'), + usage: numericWire.optional(), + }) + .describe('Discount configuration for a rate card.') + +export const totalsWire = z + .strictObject({ + amount: numericWire, + taxes_total: numericWire, + taxes_inclusive_total: numericWire, + taxes_exclusive_total: numericWire, + charges_total: numericWire, + discounts_total: numericWire, + credits_total: numericWire, + total: numericWire, + }) + + .describe( + 'Totals contains the summaries of all calculations for a billing resource.', + ) + +export const spendCommitmentsWire = z + .strictObject({ + minimum_amount: numericWire.optional(), + maximum_amount: numericWire.optional(), + }) + + .describe( + 'Spend commitments for a rate card. The customer is committed to spend at least the minimum amount and at most the maximum amount.', + ) + +export const invoiceLineCreditsAppliedWire = z + .strictObject({ + amount: numericWire, + description: z + .string() + .optional() + + .describe( + 'Optional human-readable description of the credit allocation.', + ), + }) + .describe('A credit allocation applied to an invoice line item.') + +export const featureManualUnitCostWire = z + .strictObject({ + type: z + .literal('manual') + .describe('The type discriminator for manual unit cost.'), + amount: numericWire, + }) + .describe('A fixed per-unit cost amount.') + +export const featureLlmUnitCostPricingWire = z + .strictObject({ + input_per_token: numericWire, + output_per_token: numericWire, + cache_read_per_token: numericWire.optional(), + reasoning_per_token: numericWire.optional(), + cache_write_per_token: numericWire.optional(), + }) + .describe('Resolved per-token pricing from the LLM cost database.') + +export const llmCostModelPricingWire = z + .strictObject({ + input_per_token: numericWire, + output_per_token: numericWire, + cache_read_per_token: numericWire.optional(), + cache_write_per_token: numericWire.optional(), + reasoning_per_token: numericWire.optional(), + }) + .describe('Token pricing for an LLM model, denominated per token.') + +export const queryFilterNumericWire = z + .strictObject({ + gt: numericWire.optional(), + gte: numericWire.optional(), + lt: numericWire.optional(), + lte: numericWire.optional(), + get and() { + return z + .array(queryFilterNumericWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical AND.') + }, + get or() { + return z + .array(queryFilterNumericWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical OR.') + }, + }) + + .describe( + 'A query filter for a numeric attribute. Operators are mutually exclusive, only one operator is allowed at a time.', + ) + +export const cursorPaginationQueryWire = z + .strictObject({ + page: cursorPaginationQueryPageWire.optional(), + }) + .describe('Cursor page query.') + +export const listMetersParamsFilterWire = z + .strictObject({ + key: stringFieldFilterWire.optional(), + name: stringFieldFilterWire.optional(), + }) + .describe('Filter options for listing meters.') + +export const listLlmCostPricesParamsFilterWire = z + .strictObject({ + provider: stringFieldFilterWire.optional(), + model_id: stringFieldFilterWire.optional(), + model_name: stringFieldFilterWire.optional(), + currency: stringFieldFilterWire.optional(), + source: stringFieldFilterWire.optional(), + }) + .describe('Filter options for listing LLM cost prices.') + +export const labelsFieldFilterWire = z + .record(z.string(), stringFieldFilterWire) + + .describe( + "Filters on the resource's `labels` field. The schema is a map keyed by the label name; each value is a `StringFieldFilter`. Both deepObject forms are accepted: `filter[labels][key]=value` (nested) and `filter[labels.key]=value` (dot-notation).", + ) + +export const ulidFieldFilterWire = z + .union([ + ulidWire, + z.strictObject({ + eq: ulidWire.optional(), + oeq: z + .array(ulidWire) + .optional() + + .describe( + 'Returns entities that exact match any of the comma-delimited ULIDs in the filter string.', + ), + neq: ulidWire.optional(), + }), + ]) + + .describe( + 'Filters on the given ULID field value by exact match. All properties are optional; provide exactly one to specify the comparison.', + ) + +export const customerReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('Customer reference.') + +export const profileReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('Billing profile reference.') + +export const createResourceReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('TaxCode reference.') + +export const taxCodeReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('TaxCode reference.') + +export const creditGrantInvoiceReferenceWire = z + .strictObject({ + id: ulidWire.optional(), + line: z + .strictObject({ + id: ulidWire, + }) + .optional() + .describe('Identifier of the invoice line associated with the grant.'), + }) + .describe('Invoice references for the grant.') + +export const billingCustomerReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('Customer reference.') + +export const subscriptionReferenceWire = z + .strictObject({ + id: ulidWire, + phase: z + .strictObject({ + id: ulidWire, + item: z + .strictObject({ + id: ulidWire, + }) + .describe('The item of the phase.'), + }) + .describe('The phase of the subscription.'), + }) + + .describe( + 'Subscription reference represents a reference to the specific subscription item this entity represents.', + ) + +export const addonReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('Addon reference.') + +export const featureReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('Feature reference.') + +export const appReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('App reference.') + +export const chargeReferenceWire = z + .strictObject({ + id: ulidWire, + }) + .describe('Reference to a charge associated with an invoice line.') + +export const currencyFiatWire = z + .strictObject({ + id: ulidWire, + type: z.literal('fiat').describe('The type of the currency.'), + name: z + .string() + .min(1) + .max(256) + + .describe( + 'The name of the currency. It should be a human-readable string that represents the name of the currency, such as "US Dollar" or "Euro".', + ), + description: z + .string() + .min(1) + .max(256) + .optional() + .describe('Description of the currency.'), + symbol: z + .string() + .min(1) + .optional() + + .describe( + 'The symbol of the currency. It should be a string that represents the symbol of the currency, such as "$" for US Dollar or "€" for Euro.', + ), + code: currencyCodeWire, + }) + .describe('Currency describes a currency supported by the billing system.') + +export const dateTimeFieldFilterWire = z + .union([ + dateTimeWire, + z.strictObject({ + eq: dateTimeWire.optional(), + lt: dateTimeWire.optional(), + lte: dateTimeWire.optional(), + gt: dateTimeWire.optional(), + gte: dateTimeWire.optional(), + }), + ]) + + .describe( + 'Filters on the given datetime (RFC-3339) field value. All properties are optional; provide exactly one to specify the comparison.', + ) + +export const eventWire = z + .strictObject({ + id: z.string().min(1).describe('Identifies the event.'), + source: z + .string() + .min(1) + .describe('Identifies the context in which an event happened.'), + specversion: z + .string() + .min(1) + .default('1.0') + + .describe( + 'The version of the CloudEvents specification which the event uses.', + ), + type: z + .string() + .min(1) + + .describe( + 'Contains a value describing the type of event related to the originating occurrence.', + ), + datacontenttype: z + .union([z.literal('application/json'), z.null()]) + .optional() + + .describe( + 'Content type of the CloudEvents data value. Only the value "application/json" is allowed over HTTP.', + ), + dataschema: z + .union([z.string(), z.null()]) + .optional() + .describe('Identifies the schema that data adheres to.'), + subject: z + .string() + .min(1) + + .describe( + 'Describes the subject of the event in the context of the event producer (identified by source).', + ), + time: z + .union([dateTimeWire, z.null()]) + .optional() + + .describe( + 'Timestamp of when the occurrence happened. Must adhere to RFC 3339.', + ), + data: z + .union([z.record(z.string(), z.unknown()), z.null()]) + .optional() + + .describe( + 'The event payload. Optional, if present it must be a JSON object.', + ), + }) + .describe('Metering event following the CloudEvents specification.') + +export const meterQueryRowWire = z + .strictObject({ + value: numericWire, + from: dateTimeWire, + to: dateTimeWire, + dimensions: z + .record(z.string(), z.string()) + + .describe( + 'The dimensions the value is aggregated over. `subject` and `customer_id` are reserved dimensions.', + ), + }) + .describe('A row in the result of a meter query.') + +export const appStripeCreateCustomerPortalSessionResultWire = z + .strictObject({ + id: z + .string() + + .describe( + 'The ID of the customer portal session. See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-id', + ), + stripe_customer_id: z.string().describe('The ID of the stripe customer.'), + configuration_id: z + .string() + + .describe( + 'Configuration used to customize the customer portal. See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-configuration', + ), + livemode: z + .boolean() + + .describe( + 'Livemode. See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-livemode', + ), + created_at: dateTimeWire, + return_url: z + .string() + + .describe( + 'Return URL. See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-return_url', + ), + locale: z + .string() + + .describe( + 'The IETF language tag of the locale customer portal is displayed in. See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-locale', + ), + url: z + .string() + + .describe( + 'The URL to redirect the customer to after they have completed their requested actions.', + ), + }) + + .describe( + 'Result of creating a [Stripe Customer Portal Session](https://docs.stripe.com/api/customer_portal/sessions/object). Contains all the information needed to redirect the customer to the Stripe Customer Portal.', + ) + +export const closedPeriodWire = z + .strictObject({ + from: dateTimeWire, + to: dateTimeWire, + }) + + .describe( + 'A period with defined start and end dates. The period is always inclusive at the start and exclusive at the end.', + ) + +export const subscriptionAddonTimelineSegmentWire = z + .strictObject({ + active_from: dateTimeWire, + active_to: dateTimeWire.optional(), + quantity: z + .number() + .int() + .nonnegative() + .describe('The quantity of the add-on for the given period.'), + }) + .describe('A subscription add-on event.') + +export const costBasisWire = z + .strictObject({ + id: ulidWire, + fiat_code: currencyCodeWire, + rate: numericWire, + effective_from: dateTimeWire.optional(), + created_at: dateTimeWire, + }) + .describe('Describes currency basis supported by billing system.') + +export const createCostBasisRequestWire = z + .strictObject({ + fiat_code: currencyCodeWire, + rate: numericWire, + effective_from: dateTimeWire.optional(), + }) + .describe('CostBasis create request.') + +export const featureCostQueryRowWire = z + .strictObject({ + usage: numericWire, + cost: z + .union([numericWire, z.null()]) + + .describe( + 'The computed cost amount (usage × unit cost). Null when pricing is not available for the given combination of dimensions.', + ), + currency: currencyCodeWire, + detail: z + .string() + .optional() + + .describe( + 'Detail message when cost amount is null, explaining why the cost could not be resolved.', + ), + from: dateTimeWire, + to: dateTimeWire, + dimensions: z + .record(z.string(), z.string()) + + .describe( + 'The dimensions the value is aggregated over. `subject` and `customer_id` are reserved dimensions.', + ), + }) + .describe('A row in the result of a feature cost query.') + +export const resourceWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + }) + .describe('Represents common fields of resources.') + +export const resourceImmutableWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + }) + .describe('Represents common fields of immutable resources.') + +export const queryFilterDateTimeWire = z + .strictObject({ + gt: dateTimeWire.optional(), + gte: dateTimeWire.optional(), + lt: dateTimeWire.optional(), + lte: dateTimeWire.optional(), + get and() { + return z + .array(queryFilterDateTimeWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical AND.') + }, + get or() { + return z + .array(queryFilterDateTimeWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical OR.') + }, + }) + + .describe( + 'A query filter for a time attribute. Operators are mutually exclusive, only one operator is allowed at a time.', + ) + +export const cursorMetaWire = z + .strictObject({ + page: cursorMetaPageWire, + }) + .describe('Cursor pagination metadata.') + +export const invalidParameterStandardWire = z + .strictObject({ + field: z.string().describe('The name of the field that failed validation.'), + rule: invalidRulesWire.optional(), + source: z + .string() + .optional() + + .describe( + 'The part of the request the field came from (e.g. `body`, `query`).', + ), + reason: z + .string() + + .describe( + 'A human readable explanation of why the field failed validation.', + ), + }) + .describe('A parameter that failed a standard validation rule.') + +export const invalidParameterMinimumLengthWire = z + .strictObject({ + field: z.string().describe('The name of the field that failed validation.'), + rule: invalidParameterMinimumRuleWire, + minimum: z.number().int().describe('The minimum allowed value or length.'), + source: z + .string() + .optional() + + .describe( + 'The part of the request the field came from (e.g. `body`, `query`).', + ), + reason: z + .string() + + .describe( + 'A human readable explanation of why the field failed validation.', + ), + }) + + .describe( + 'A parameter that failed a minimum-length (or minimum-value) validation rule.', + ) + +export const invalidParameterMaximumLengthWire = z + .strictObject({ + field: z.string().describe('The name of the field that failed validation.'), + rule: invalidParameterMaximumRuleWire, + maximum: z.number().int().describe('The maximum allowed value or length.'), + source: z + .string() + .optional() + + .describe( + 'The part of the request the field came from (e.g. `body`, `query`).', + ), + reason: z + .string() + + .describe( + 'A human readable explanation of why the field failed validation.', + ), + }) + + .describe( + 'A parameter that failed a maximum-length (or maximum-value) validation rule.', + ) + +export const invalidParameterChoiceItemWire = z + .strictObject({ + field: z.string().describe('The name of the field that failed validation.'), + rule: invalidParameterChoiceRuleWire, + reason: z + .string() + + .describe( + 'A human readable explanation of why the field failed validation.', + ), + choices: z + .array(z.unknown()) + .min(1) + .describe('The allowed choices for the field.'), + source: z + .string() + .optional() + + .describe( + 'The part of the request the field came from (e.g. `body`, `query`).', + ), + }) + .describe('A parameter whose value was not one of the allowed choices.') + +export const invalidParameterDependentItemWire = z + .strictObject({ + field: z.string().describe('The name of the field that failed validation.'), + rule: invalidParameterDependentRuleWire, + reason: z + .string() + + .describe( + 'A human readable explanation of why the field failed validation.', + ), + dependents: z + .array(z.unknown()) + .describe('The fields that this field depends on.'), + source: z + .string() + .optional() + + .describe( + 'The part of the request the field came from (e.g. `body`, `query`).', + ), + }) + .describe('A parameter that failed a dependent-fields validation rule.') + +export const unauthorizedWire = baseErrorWire.describe('Unauthorized.') + +export const forbiddenWire = baseErrorWire.describe('Forbidden.') + +export const notFoundWire = baseErrorWire.describe('Not Found.') + +export const goneWire = baseErrorWire.describe('Gone.') + +export const conflictWire = baseErrorWire.describe('Conflict.') + +export const payloadTooLargeWire = baseErrorWire.describe('Payload Too Large.') + +export const unsupportedMediaTypeWire = baseErrorWire.describe( + 'Unsupported Media Type.', +) + +export const unprocessableContentWire = baseErrorWire.describe( + 'Unprocessable Content.', +) + +export const tooManyRequestsWire = baseErrorWire.describe('Too Many Requests.') + +export const internalWire = baseErrorWire.describe('Internal Server Error.') + +export const notImplementedWire = baseErrorWire.describe('Not Implemented.') + +export const notAvailableWire = baseErrorWire.describe('Not Available.') + +export const createCreditGrantFiltersWire = z + .strictObject({ + features: z + .array(resourceKeyWire) + .optional() + + .describe( + 'Limit the credit grant to specific features. If no features are specified, the credit grant can be used for any feature.', + ), + }) + .describe('Filters for the credit grant.') + +export const creditGrantFiltersWire = z + .strictObject({ + features: z + .array(resourceKeyWire) + .optional() + + .describe( + 'Limit the credit grant to specific features. If no features are specified, the credit grant can be used for any feature.', + ), + }) + .describe('Filters for the credit grant.') + +export const upsertPlanAddonRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + from_plan_phase: resourceKeyWire, + max_quantity: z + .number() + .int() + .gte(1) + .optional() + + .describe( + 'The maximum number of times the add-on can be purchased for the plan. For single-instance add-ons this field must be omitted. For multi-instance add-ons when omitted, unlimited quantity can be purchased.', + ), + }) + .describe('PlanAddon upsert request.') + +export const resourceWithKeyWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + key: resourceKeyWire, + }) + .describe('Represents common fields of resources with a key.') + +export const ulidOrResourceKeyWire = z + .union([ulidWire, resourceKeyWire]) + .describe('ULID ID or Resource Key.') + +export const createMeterRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: resourceKeyWire, + aggregation: meterAggregationWire, + event_type: z + .string() + .min(1) + .describe('The event type to include in the aggregation.'), + events_from: dateTimeWire.optional(), + value_property: z + .string() + .min(1) + .optional() + + .describe( + "JSONPath expression to extract the value from the ingested event's data property. The ingested value for sum, avg, min, and max aggregations is a number or a string that can be parsed to a number. For unique_count aggregation, the ingested value must be a string. For count aggregation the value_property is ignored.", + ), + dimensions: z + .record(z.string(), z.string()) + .optional() + + .describe( + 'Named JSONPath expressions to extract the group by values from the event data. Keys must be unique and consist only alphanumeric and underscore characters.', + ), + }) + .describe('Meter create request.') + +export const meterWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + key: resourceKeyWire, + aggregation: meterAggregationWire, + event_type: z + .string() + .min(1) + .describe('The event type to include in the aggregation.'), + events_from: dateTimeWire.optional(), + value_property: z + .string() + .min(1) + .optional() + + .describe( + "JSONPath expression to extract the value from the ingested event's data property. The ingested value for sum, avg, min, and max aggregations is a number or a string that can be parsed to a number. For unique_count aggregation, the ingested value must be a string. For count aggregation the value_property is ignored.", + ), + dimensions: z + .record(z.string(), z.string()) + .optional() + + .describe( + 'Named JSONPath expressions to extract the group by values from the event data. Keys must be unique and consist only alphanumeric and underscore characters.', + ), + }) + + .describe( + 'A meter is a configuration that defines how to match and aggregate events.', + ) + +export const paginatedMetaWire = z + .strictObject({ + page: pageMetaWire, + }) + .describe('Pagination metadata.') + +export const queryFilterStringMapItemWire = z + .strictObject({ + exists: z.boolean().optional().describe('The attribute exists.'), + eq: z + .string() + .optional() + .describe('The attribute equals the provided value.'), + neq: z + .string() + .optional() + .describe('The attribute does not equal the provided value.'), + in: z + .array(z.string()) + .min(1) + .max(100) + .optional() + .describe('The attribute is one of the provided values.'), + nin: z + .array(z.string()) + .min(1) + .max(100) + .optional() + .describe('The attribute is not one of the provided values.'), + contains: z + .string() + .optional() + .describe('The attribute contains the provided value.'), + ncontains: z + .string() + .optional() + .describe('The attribute does not contain the provided value.'), + and: z + .array(queryFilterStringWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical AND.'), + or: z + .array(queryFilterStringWire) + .min(1) + .max(10) + .optional() + .describe('Combines the provided filters with a logical OR.'), + }) + + .describe( + 'A query filter for an item in a string map attribute. Operators are mutually exclusive, only one operator is allowed at a time.', + ) + +export const ulidOrExternalResourceKeyWire = z + .union([ulidWire, externalResourceKeyWire]) + .describe('ULID ID or External Resource Key.') + +export const customerKeyReferenceWire = z + .strictObject({ + key: externalResourceKeyWire, + }) + .describe('Customer reference by external key.') + +export const customerUsageAttributionWire = z + .strictObject({ + subject_keys: z + .array(usageAttributionSubjectKeyWire) + + .describe( + 'The subjects that are attributed to the customer. Can be empty when no usage event subjects are associated with the customer.', + ), + }) + + .describe( + 'Mapping to attribute metered usage to the customer. One customer can have zero or more subjects, but one subject can only belong to one customer.', + ) + +export const addressWire = z + .strictObject({ + country: countryCodeWire.optional(), + postal_code: z.string().optional().describe('Postal code.'), + state: z.string().optional().describe('State or province.'), + city: z.string().optional().describe('City.'), + line1: z.string().optional().describe('First line of the address.'), + line2: z.string().optional().describe('Second line of the address.'), + phone_number: z.string().optional().describe('Phone number.'), + }) + .describe('Address') + +export const appStripeCreateCheckoutSessionCustomerUpdateWire = z + .strictObject({ + address: appStripeCreateCheckoutSessionCustomerUpdateBehaviorWire + .optional() + .default('never'), + name: appStripeCreateCheckoutSessionCustomerUpdateBehaviorWire + .optional() + .default('never'), + shipping: appStripeCreateCheckoutSessionCustomerUpdateBehaviorWire + .optional() + .default('never'), + }) + + .describe( + 'Controls which customer fields can be updated by the checkout session.', + ) + +export const appStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreementWire = + z + .strictObject({ + position: + appStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreementPositionWire.optional(), + }) + .describe('Payment method reuse agreement configuration.') + +export const appStripeCreateCheckoutSessionTaxIdCollectionWire = z + .strictObject({ + enabled: z + .boolean() + .optional() + .default(false) + .describe('Enable tax ID collection during checkout. Defaults to false.'), + required: appStripeCreateCheckoutSessionTaxIdCollectionRequiredWire + .optional() + .default('never'), + }) + .describe('Tax ID collection configuration for checkout sessions.') + +export const appStripeCreateCheckoutSessionResultWire = z + .strictObject({ + customer_id: ulidWire, + stripe_customer_id: z.string().describe('The Stripe customer ID.'), + session_id: z.string().describe('The Stripe checkout session ID.'), + setup_intent_id: z + .string() + + .describe( + 'The setup intent ID created for collecting the payment method.', + ), + client_secret: z + .string() + .optional() + + .describe( + 'Client secret for initializing Stripe.js on the client side. Required for embedded checkout sessions. See: https://docs.stripe.com/payments/checkout/custom-success-page', + ), + client_reference_id: z + .string() + .optional() + + .describe( + 'The client reference ID provided in the request. Useful for reconciling the session with your internal systems.', + ), + customer_email: z + .string() + .optional() + .describe("Customer's email address if provided to Stripe."), + currency: currencyCodeWire.optional(), + created_at: dateTimeWire, + expires_at: dateTimeWire.optional(), + metadata: z + .record(z.string(), z.string()) + .optional() + .describe('Metadata attached to the checkout session.'), + status: z + .string() + .optional() + + .describe( + 'The status of the checkout session. See: https://docs.stripe.com/api/checkout/sessions/object#checkout_session_object-status', + ), + url: z + .string() + .optional() + + .describe( + 'URL to redirect customers to the checkout page (for hosted mode).', + ), + mode: appStripeCheckoutSessionModeWire, + cancel_url: z + .string() + .optional() + + .describe( + 'The cancel URL where customers are redirected if they cancel.', + ), + success_url: z + .string() + .optional() + + .describe( + 'The success URL where customers are redirected after completion.', + ), + return_url: z + .string() + .optional() + .describe('The return URL for embedded sessions after authentication.'), + }) + + .describe( + 'Result of creating a Stripe Checkout Session. Contains all the information needed to redirect customers to the checkout or initialize an embedded checkout flow.', + ) + +export const customerStripeCreateCustomerPortalSessionRequestWire = z + .strictObject({ + stripe_options: appStripeCreateCustomerPortalSessionOptionsWire, + }) + + .describe( + 'Request to create a Stripe Customer Portal Session for the customer. Useful to redirect the customer to the Stripe Customer Portal to manage their payment methods, change their billing address and access their invoice history. Only returns URL if the customer billing profile is linked to a stripe app and customer.', + ) + +export const entitlementAccessResultWire = z + .strictObject({ + type: entitlementTypeWire, + feature_key: resourceKeyWire, + has_access: z + .boolean() + + .describe( + 'Whether the customer has access to the feature. Always true for `boolean` and `static` entitlements. Depends on balance for `metered` entitlements.', + ), + config: z + .string() + .optional() + + .describe( + 'Only available for static entitlements. Config is the JSON parsable configuration of the entitlement. Useful to describe per customer configuration.', + ), + }) + .describe('Entitlement access result.') + +export const createCreditGrantPurchaseWire = z + .strictObject({ + currency: currencyCodeWire, + per_unit_cost_basis: numericWire.optional().default('1.0'), + availability_policy: creditAvailabilityPolicyWire + .optional() + .default('on_creation'), + }) + .describe('Purchase and payment terms of the grant.') + +export const rateCardMeteredEntitlementWire = z + .strictObject({ + type: z + .literal('metered') + .describe('The type of the entitlement template.'), + is_soft_limit: z + .boolean() + .optional() + .default(false) + + .describe( + 'If soft limit is true, the subject can use the feature even if the entitlement is exhausted; access remains granted.', + ), + limit: z + .number() + .nonnegative() + .optional() + + .describe( + "The amount of usage granted each usage period, in the feature's unit. Usage is counted against this allowance and the balance resets every usage period. When `is_soft_limit` is true the subject keeps access after the limit is reached; otherwise access is denied once the allowance is exhausted.", + ), + usage_period: iso8601DurationWire.optional(), + }) + .describe('The entitlement template of a metered entitlement.') + +export const recurringPeriodWire = z + .strictObject({ + anchor: dateTimeWire, + interval: iso8601DurationWire, + }) + .describe('Recurring period with an anchor and an interval.') + +export const creditGrantPurchaseWire = z + .strictObject({ + currency: currencyCodeWire, + per_unit_cost_basis: numericWire.optional().default('1.0'), + amount: numericWire, + availability_policy: creditAvailabilityPolicyWire + .optional() + .default('on_creation'), + settlement_status: creditPurchasePaymentSettlementStatusWire.optional(), + }) + .describe('Purchase and payment terms of the grant.') + +export const updateCreditGrantExternalSettlementRequestWire = z + .strictObject({ + status: creditPurchasePaymentSettlementStatusWire, + }) + + .describe( + 'Request body for updating the external payment settlement status of a credit grant.', + ) + +export const listCreditGrantsParamsFilterWire = z + .strictObject({ + status: creditGrantStatusWire.optional(), + currency: currencyCodeWire.optional(), + key: stringFieldFilterWire.optional(), + }) + .describe('Filter options for listing credit grants.') + +export const getCreditBalanceParamsFilterWire = z + .strictObject({ + currency: stringFieldFilterExactWire.optional(), + feature_key: stringFieldFilterWire.optional(), + }) + .describe('Filter options for getting a credit balance.') + +export const listChargesParamsFilterWire = z + .strictObject({ + status: stringFieldFilterExactWire.optional(), + }) + .describe('Filter options for listing charges.') + +export const listPlansParamsFilterWire = z + .strictObject({ + key: stringFieldFilterWire.optional(), + name: stringFieldFilterWire.optional(), + status: stringFieldFilterExactWire.optional(), + currency: stringFieldFilterExactWire.optional(), + }) + .describe('Filter options for listing plans.') + +export const subscriptionCreateWire = z + .strictObject({ + labels: labelsWire.optional(), + settlement_mode: settlementModeWire.optional(), + customer: z + .strictObject({ + id: ulidWire.optional(), + key: externalResourceKeyWire.optional(), + }) + .describe('The customer to create the subscription for.'), + plan: z + .strictObject({ + id: ulidWire.optional(), + key: resourceKeyWire.optional(), + version: z + .number() + .int() + .optional() + + .describe( + 'The plan version of the subscription, if any. If not provided, the latest version of the plan will be used.', + ), + }) + .describe('The plan reference of the subscription.'), + billing_anchor: dateTimeWire.optional(), + }) + .describe('Subscription create request.') + +export const rateCardProrationConfigurationWire = z + .strictObject({ + mode: rateCardProrationModeWire, + }) + .describe('The proration configuration of the rate card.') + +export const subscriptionWire = z + .strictObject({ + id: ulidWire, + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + customer_id: ulidWire, + plan_id: ulidWire.optional(), + billing_anchor: dateTimeWire, + status: subscriptionStatusWire, + settlement_mode: settlementModeWire.optional(), + }) + .describe('Subscription.') + +export const subscriptionEditTimingWire = z + .union([subscriptionEditTimingEnumWire, dateTimeWire]) + + .describe( + 'Subscription edit timing defined when the changes should take effect. If the provided configuration is not supported by the subscription, an error will be returned.', + ) + +export const unitConfigWire = z + .strictObject({ + operation: unitConfigOperationWire, + conversion_factor: numericWire, + rounding: unitConfigRoundingModeWire.optional().default('none'), + precision: z + .number() + .int() + .optional() + .default(0) + + .describe( + 'The number of decimal places to retain after rounding. Only meaningful when rounding is not "none". Defaults to 0 (round to whole numbers).', + ), + display_unit: z + .string() + .optional() + + .describe( + 'A human-readable label for the converted unit shown on invoices and in the customer portal (e.g., "GB", "hours", "M tokens"). Optional. When omitted, no unit label is rendered.', + ), + }) + + .describe( + 'Unit conversion configuration. Transforms raw metered quantities into billing-ready units before pricing and entitlement evaluation. Applied at the rate card level so the same feature can be billed in different units across plans. Examples: - Meter bytes, bill GB: operation=divide, conversionFactor=1e9, rounding=ceiling, displayUnit="GB" - Meter seconds, bill hours: operation=divide, conversionFactor=3600, rounding=ceiling, displayUnit="hours" - Cost + 20% margin: operation=multiply, conversionFactor=1.2 - Bill per million tokens: operation=divide, conversionFactor=1e6, rounding=ceiling, displayUnit="M" v1 equivalents: - DynamicPrice(multiplier): operation=multiply, conversionFactor=multiplier + UnitPrice(amount=1) - PackagePrice(amount, quantityPerPkg): operation=divide, conversionFactor=quantityPerPkg, rounding=ceiling + UnitPrice(amount)', + ) + +export const appCatalogItemWire = z + .strictObject({ + type: appTypeWire, + name: z.string().describe('Name of the app.'), + description: z.string().describe('Description of the app.'), + }) + + .describe( + 'Available apps for billing integrations to connect with third-party services. Apps can have various capabilities like syncing data from or to external systems, integrating with third-party services for tax calculation, delivery of invoices, collection of payments, etc.', + ) + +export const taxCodeAppMappingWire = z + .strictObject({ + app_type: appTypeWire, + tax_code: z.string().describe('Tax code.'), + }) + .describe('Mapping of app types to tax codes.') + +export const partyTaxIdentityWire = z + .strictObject({ + code: taxIdentificationCodeWire.optional(), + }) + + .describe( + 'Identity stores the details required to identify an entity for tax purposes in a specific country.', + ) + +export const workflowInvoicingSettingsWire = z + .strictObject({ + auto_advance: z + .boolean() + .optional() + .default(true) + + .describe( + 'Whether to automatically issue the invoice after the draftPeriod has passed.', + ), + draft_period: z + .string() + .optional() + .default('P0D') + + .describe( + 'The period for the invoice to be kept in draft status for manual reviews.', + ), + progressive_billing: z + .boolean() + .optional() + .default(true) + .describe('Should progressive billing be allowed for this workflow?'), + subscription_end_proration_mode: + workflowInvoicingSubscriptionEndProrationModeWire + .optional() + .default('bill_actual_period'), + }) + .describe('Invoice settings for a billing workflow.') + +export const workflowPaymentSettingsWire = z + .discriminatedUnion('collection_method', [ + workflowPaymentChargeAutomaticallySettingsWire, + workflowPaymentSendInvoiceSettingsWire, + ]) + .describe('Payment settings for a billing workflow.') + +export const invoiceValidationIssueWire = z + .strictObject({ + code: z.string().describe('Machine-readable error code.'), + message: z.string().describe('Human-readable description of the error.'), + attributes: z + .record(z.string(), z.unknown()) + .optional() + .describe('Additional structured context.'), + severity: invoiceValidationIssueSeverityWire, + field: z + .string() + .optional() + + .describe( + 'JSON path to the field that caused this validation issue, if applicable. For example: `lines/0/rate_card/price`.', + ), + }) + + .describe( + 'A validation issue found during invoice processing. Converges on the same structure used by plan and subscription validation errors: a machine-readable `code`, a human-readable `message`, optional structured `attributes`, plus a `severity` and optional `field` path.', + ) + +export const invoiceAvailableActionsWire = z + .strictObject({ + advance: invoiceAvailableActionDetailsWire.optional(), + approve: invoiceAvailableActionDetailsWire.optional(), + delete: invoiceAvailableActionDetailsWire.optional(), + retry: invoiceAvailableActionDetailsWire.optional(), + snapshot_quantities: invoiceAvailableActionDetailsWire.optional(), + }) + + .describe( + 'The set of state-transition actions available for an invoice in its current status. A field is present only when that action is permitted from the current state.', + ) + +export const invoiceLineAmountDiscountWire = z + .strictObject({ + id: ulidWire, + reason: invoiceDiscountReasonWire, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + external_references: invoiceLineExternalReferencesWire.optional(), + amount: numericWire, + }) + .describe('A monetary amount discount applied to an invoice line item.') + +export const invoiceLineUsageDiscountWire = z + .strictObject({ + id: ulidWire, + reason: invoiceDiscountReasonWire, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + external_references: invoiceLineExternalReferencesWire.optional(), + quantity: numericWire, + }) + .describe('A usage quantity discount applied to an invoice line item.') + +export const invoiceLineBaseDiscountWire = z + .strictObject({ + id: ulidWire, + reason: invoiceDiscountReasonWire, + description: z + .string() + .optional() + .describe('Optional human-readable description of the discount.'), + external_references: invoiceLineExternalReferencesWire.optional(), + }) + .describe('Base fields shared by all invoice line item discounts.') + +export const listCurrenciesParamsFilterWire = z + .strictObject({ + type: currencyTypeWire.optional(), + code: stringFieldFilterWire.optional(), + }) + .describe('Filter options for listing currencies.') + +export const currencyCustomWire = z + .strictObject({ + id: ulidWire, + type: z.literal('custom').describe('The type of the currency.'), + name: z + .string() + .min(1) + .max(256) + + .describe( + 'The name of the currency. It should be a human-readable string that represents the name of the currency, such as "US Dollar" or "Euro".', + ), + description: z + .string() + .min(1) + .max(256) + .optional() + .describe('Description of the currency.'), + symbol: z + .string() + .min(1) + .optional() + + .describe( + 'The symbol of the currency. It should be a string that represents the symbol of the currency, such as "$" for US Dollar or "€" for Euro.', + ), + code: currencyCodeCustomWire, + created_at: dateTimeWire, + }) + .describe('Describes custom currency.') + +export const createCurrencyCustomRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + + .describe( + 'The name of the currency. It should be a human-readable string that represents the name of the currency, such as "US Dollar" or "Euro".', + ), + description: z + .string() + .min(1) + .max(256) + .optional() + .describe('Description of the currency.'), + symbol: z + .string() + .min(1) + .optional() + + .describe( + 'The symbol of the currency. It should be a string that represents the symbol of the currency, such as "$" for US Dollar or "€" for Euro.', + ), + code: currencyCodeCustomWire, + }) + .describe('CurrencyCustom create request.') + +export const governanceQueryRequestWire = z + .strictObject({ + include_credits: z + .boolean() + .optional() + .default(false) + + .describe( + 'Whether to include credit balance availability for each resolved customer. When true, each feature evaluation includes credit balance checks. Defaults to `false`.', + ), + customer: governanceQueryRequestCustomersWire, + feature: governanceQueryRequestFeaturesWire.optional(), + }) + .describe('Query to evaluate feature access for a list of customers.') + +export const governanceFeatureAccessReasonWire = z + .strictObject({ + code: governanceFeatureAccessReasonCodeWire, + message: z.string().describe('Human-readable description of the error.'), + attributes: z + .record(z.string(), z.unknown()) + .optional() + .describe('Additional structured context.'), + }) + .describe('Reason a feature is not accessible to a customer.') + +export const governanceQueryErrorWire = z + .strictObject({ + code: governanceQueryErrorCodeWire, + message: z.string().describe('Human-readable description of the error.'), + attributes: z + .record(z.string(), z.unknown()) + .optional() + .describe('Additional structured context.'), + customer: z + .string() + .optional() + + .describe( + 'The customer identifier from the request that produced this error.', + ), + }) + + .describe( + 'Query error within a partially successful governance query response.', + ) + +export const appCustomerDataWire = z + .strictObject({ + stripe: appCustomerDataStripeWire.optional(), + external_invoicing: appCustomerDataExternalInvoicingWire.optional(), + }) + .describe('App customer data.') + +export const upsertAppCustomerDataRequestWire = z + .strictObject({ + stripe: appCustomerDataStripeWire.optional(), + external_invoicing: appCustomerDataExternalInvoicingWire.optional(), + }) + .describe('AppCustomerData upsert request.') + +export const creditAdjustmentWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + }) + + .describe( + "A credit adjustment can be used to make manual adjustments to a customer's credit balance. Supported use-cases: - Usage correction", + ) + +export const creditBalanceWire = z + .strictObject({ + currency: billingCurrencyCodeWire, + live: numericWire, + settled: numericWire, + pending: numericWire, + }) + .describe('The credit balance by currency.') + +export const createCreditAdjustmentRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + currency: billingCurrencyCodeWire, + amount: numericWire, + }) + .describe('CreditAdjustment create request.') + +export const listCreditTransactionsParamsFilterWire = z + .strictObject({ + type: creditTransactionTypeWire.optional(), + currency: billingCurrencyCodeWire.optional(), + feature_key: stringFieldFilterWire.optional(), + }) + .describe('Filter options for listing credit transactions.') + +export const creditTransactionWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + booked_at: dateTimeWire, + type: creditTransactionTypeWire, + currency: billingCurrencyCodeWire, + amount: numericWire, + available_balance: z + .strictObject({ + before: numericWire, + after: numericWire, + }) + .describe('The available balance before and after the transaction.'), + }) + + .describe( + "A credit transaction represents a single credit movement on the customer's balance. Credit transactions are immutable.", + ) + +export const priceTierWire = z + .strictObject({ + up_to_amount: numericWire.optional(), + flat_price: priceFlatWire.optional(), + unit_price: priceUnitWire.optional(), + }) + + .describe( + 'A price tier used in graduated and volume pricing. At least one price component (flat_price or unit_price) must be set. When UnitConfig is present on the rate card, up_to_amount is expressed in converted billing units.', + ) + +export const chargeTotalsWire = z + .strictObject({ + booked: totalsWire, + realtime: totalsWire.optional(), + }) + + .describe( + 'The totals of a change. RealTime is only expanded when the `real_time_usage` expand is used.', + ) + +export const featureLlmUnitCostWire = z + .strictObject({ + type: z + .literal('llm') + .describe('The type discriminator for LLM unit cost.'), + provider_property: z + .string() + .optional() + + .describe( + 'Meter group-by property that holds the LLM provider. Use this when the meter has a group-by dimension for provider. Mutually exclusive with `provider`.', + ), + provider: z + .string() + .optional() + + .describe( + 'Static LLM provider value (e.g., "openai", "anthropic"). Use this when the feature tracks a single provider. Mutually exclusive with `provider_property`.', + ), + model_property: z + .string() + .optional() + + .describe( + 'Meter group-by property that holds the model ID. Use this when the meter has a group-by dimension for model. Mutually exclusive with `model`.', + ), + model: z + .string() + .optional() + + .describe( + 'Static model ID value (e.g., "gpt-4", "claude-3-5-sonnet"). Use this when the feature tracks a single model. Mutually exclusive with `model_property`.', + ), + token_type_property: z + .string() + .optional() + + .describe( + 'Meter group-by property that holds the token type. Use this when the meter has a group-by dimension for token type. Mutually exclusive with `token_type`.', + ), + token_type: featureLlmTokenTypeWire.optional(), + pricing: featureLlmUnitCostPricingWire.optional(), + }) + + .describe( + 'LLM cost lookup configuration. Each dimension (provider, model, token type) can be specified as either a static value or a meter group-by property name (mutually exclusive).', + ) + +export const llmCostPriceWire = z + .strictObject({ + id: ulidWire, + provider: llmCostProviderWire, + model: llmCostModelWire, + pricing: llmCostModelPricingWire, + currency: currencyCodeWire, + source: llmCostPriceSourceWire, + effective_from: dateTimeWire, + effective_to: dateTimeWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + }) + + .describe( + 'An LLM cost price record, representing the cost per token for a specific model from a specific provider.', + ) + +export const llmCostOverrideCreateWire = z + .strictObject({ + provider: z.string().describe('Provider/vendor of the model.'), + model_id: z.string().describe('Canonical model identifier.'), + model_name: z.string().optional().describe('Human-readable model name.'), + pricing: llmCostModelPricingWire, + currency: currencyCodeWire, + effective_from: dateTimeWire, + effective_to: dateTimeWire.optional(), + }) + + .describe( + 'Input for creating a per-namespace price override. Unique per provider, model and currency. If an override already exists for the given provider, model and currency, it will be updated. If an override does not exist, it will be created.', + ) + +export const listCustomersParamsFilterWire = z + .strictObject({ + key: stringFieldFilterWire.optional(), + name: stringFieldFilterWire.optional(), + primary_email: stringFieldFilterWire.optional(), + usage_attribution_subject_key: stringFieldFilterWire.optional(), + plan_key: stringFieldFilterWire.optional(), + billing_profile_id: ulidFieldFilterWire.optional(), + }) + .describe('Filter options for listing customers.') + +export const listSubscriptionsParamsFilterWire = z + .strictObject({ + id: ulidFieldFilterWire.optional(), + customer_id: ulidFieldFilterWire.optional(), + status: stringFieldFilterExactWire.optional(), + plan_id: ulidFieldFilterWire.optional(), + plan_key: stringFieldFilterExactWire.optional(), + }) + .describe('Filter options for listing subscriptions.') + +export const listFeatureParamsFilterWire = z + .strictObject({ + meter_id: ulidFieldFilterWire.optional(), + key: stringFieldFilterWire.optional(), + name: stringFieldFilterWire.optional(), + }) + .describe('Filter options for listing features.') + +export const listAddonsParamsFilterWire = z + .strictObject({ + id: ulidFieldFilterWire.optional(), + key: stringFieldFilterWire.optional(), + name: stringFieldFilterWire.optional(), + status: stringFieldFilterExactWire.optional(), + currency: stringFieldFilterExactWire.optional(), + }) + .describe('Filter options for listing add-ons.') + +export const createCreditGrantTaxConfigWire = z + .strictObject({ + behavior: taxBehaviorWire.optional(), + tax_code: createResourceReferenceWire.optional(), + }) + + .describe( + 'Tax configuration for a credit grant. Tax configuration should be provided to ensure correct revenue recognition, including for externally funded grants.', + ) + +export const creditGrantTaxConfigWire = z + .strictObject({ + behavior: taxBehaviorWire.optional(), + tax_code: taxCodeReferenceWire.optional(), + }) + + .describe( + 'Tax configuration for a credit grant. Tax configuration should be provided to ensure correct revenue recognition, including for externally funded grants.', + ) + +export const taxConfigWire = z + .strictObject({ + behavior: taxBehaviorWire.optional(), + stripe: taxConfigStripeWire.optional(), + external_invoicing: taxConfigExternalInvoicingWire.optional(), + tax_code_id: ulidWire.optional(), + tax_code: taxCodeReferenceWire.optional(), + }) + .describe('Set of provider specific tax configs.') + +export const rateCardTaxConfigWire = z + .strictObject({ + behavior: taxBehaviorWire.optional(), + code: taxCodeReferenceWire, + }) + .describe('The tax config of the rate card.') + +export const organizationDefaultTaxCodesWire = z + .strictObject({ + invoicing_tax_code: taxCodeReferenceWire, + credit_grant_tax_code: taxCodeReferenceWire, + created_at: dateTimeWire, + updated_at: dateTimeWire, + }) + + .describe( + 'Organization-level default tax code references. Stores the default tax codes applied to specific billing contexts for this organization. Provisioned automatically when the organization is created.', + ) + +export const updateOrganizationDefaultTaxCodesRequestWire = z + .strictObject({ + invoicing_tax_code: taxCodeReferenceWire.optional(), + credit_grant_tax_code: taxCodeReferenceWire.optional(), + }) + .describe('OrganizationDefaultTaxCodes update request.') + +export const planAddonWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + addon: addonReferenceWire, + from_plan_phase: resourceKeyWire, + max_quantity: z + .number() + .int() + .gte(1) + .optional() + + .describe( + 'The maximum number of times the add-on can be purchased for the plan. For single-instance add-ons this field must be omitted. For multi-instance add-ons when omitted, unlimited quantity can be purchased.', + ), + validation_errors: z + .array(productCatalogValidationErrorWire) + .optional() + .describe('List of validation errors.'), + }) + + .describe( + 'PlanAddon represents an association between a plan and an add-on, controlling which add-ons are available for purchase within a plan.', + ) + +export const createPlanAddonRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + addon: addonReferenceWire, + from_plan_phase: resourceKeyWire, + max_quantity: z + .number() + .int() + .gte(1) + .optional() + + .describe( + 'The maximum number of times the add-on can be purchased for the plan. For single-instance add-ons this field must be omitted. For multi-instance add-ons when omitted, unlimited quantity can be purchased.', + ), + }) + .describe('PlanAddon create request.') + +export const profileAppReferencesWire = z + .strictObject({ + tax: appReferenceWire, + invoicing: appReferenceWire, + payment: appReferenceWire, + }) + .describe('References to the applications used by a billing profile.') + +export const invoiceWorkflowAppsReferencesWire = z + .strictObject({ + tax: appReferenceWire, + invoicing: appReferenceWire, + payment: appReferenceWire, + }) + + .describe( + 'BillingInvoiceWorkflowAppsReferences represents the references (id) to the apps used by a billing profile', + ) + +export const listEventsParamsFilterWire = z + .strictObject({ + id: stringFieldFilterWire.optional(), + source: stringFieldFilterWire.optional(), + subject: stringFieldFilterWire.optional(), + type: stringFieldFilterWire.optional(), + customer_id: ulidFieldFilterWire.optional(), + time: dateTimeFieldFilterWire.optional(), + ingested_at: dateTimeFieldFilterWire.optional(), + stored_at: dateTimeFieldFilterWire.optional(), + }) + .describe('Filter options for listing ingested events.') + +export const resourceFiltersWire = z + .strictObject({ + name: stringFieldFilterWire.optional(), + labels: labelsFieldFilterWire.optional(), + public_labels: labelsFieldFilterWire.optional(), + created_at: dateTimeFieldFilterWire.optional(), + updated_at: dateTimeFieldFilterWire.optional(), + deleted_at: dateTimeFieldFilterWire.optional(), + }) + .describe('Resource filters.') + +export const fieldFiltersWire = z + .strictObject({ + boolean: booleanFieldFilterWire.optional(), + numeric: numericFieldFilterWire.optional(), + string: stringFieldFilterWire.optional(), + string_exact: stringFieldFilterExactWire.optional(), + ulid: ulidFieldFilterWire.optional(), + datetime: dateTimeFieldFilterWire.optional(), + labels: labelsFieldFilterWire.optional(), + }) + .describe('Field filters with all supported types.') + +export const ingestedEventWire = z + .strictObject({ + event: eventWire, + customer: customerReferenceWire.optional(), + ingested_at: dateTimeWire, + stored_at: dateTimeWire, + validation_errors: z + .array(ingestedEventValidationErrorWire) + .optional() + .describe('The validation errors of the ingested event.'), + }) + .describe('An ingested metering event with ingestion metadata.') + +export const meterQueryResultWire = z + .strictObject({ + from: dateTimeWire.optional(), + to: dateTimeWire.optional(), + data: z + .array(meterQueryRowWire) + + .describe( + 'The usage data. If no data is available, an empty array is returned.', + ), + }) + .describe('Meter query result.') + +export const featureCostQueryResultWire = z + .strictObject({ + from: dateTimeWire.optional(), + to: dateTimeWire.optional(), + data: z.array(featureCostQueryRowWire).describe('The cost data rows.'), + }) + .describe('Result of a feature cost query.') + +export const invalidParameterWire = z + .union([ + invalidParameterStandardWire, + invalidParameterMinimumLengthWire, + invalidParameterMaximumLengthWire, + invalidParameterChoiceItemWire, + invalidParameterDependentItemWire, + ]) + .describe('A parameter that failed validation.') + +export const meterPagePaginatedResponseWire = z + .strictObject({ + data: z.array(meterWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const costBasisPagePaginatedResponseWire = z + .strictObject({ + data: z.array(costBasisWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const meterQueryFiltersWire = z + .strictObject({ + dimensions: z + .record(z.string(), queryFilterStringMapItemWire) + .optional() + + .describe( + 'Filters to apply to the dimensions of the query. For `subject` and `customer_id` only equals ("eq", "in") comparisons are supported.', + ), + }) + .describe('Filters to apply to a meter query.') + +export const featureMeterReferenceWire = z + .strictObject({ + id: ulidWire, + filters: z + .record(z.string(), queryFilterStringMapItemWire) + .optional() + .describe('Filters to apply to the dimensions of the meter.'), + }) + .describe('Reference to a meter associated with a feature.') + +export const createCustomerRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: externalResourceKeyWire, + usage_attribution: customerUsageAttributionWire.optional(), + primary_email: z + .string() + .optional() + .describe('The primary email address of the customer.'), + currency: currencyCodeWire.optional(), + billing_address: addressWire.optional(), + }) + .describe('Customer create request.') + +export const customerWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + key: externalResourceKeyWire, + usage_attribution: customerUsageAttributionWire.optional(), + primary_email: z + .string() + .optional() + .describe('The primary email address of the customer.'), + currency: currencyCodeWire.optional(), + billing_address: addressWire.optional(), + }) + + .describe( + 'Customers can be individuals or organizations that can subscribe to plans and have access to features.', + ) + +export const upsertCustomerRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + usage_attribution: customerUsageAttributionWire.optional(), + primary_email: z + .string() + .optional() + .describe('The primary email address of the customer.'), + currency: currencyCodeWire.optional(), + billing_address: addressWire.optional(), + }) + .describe('Customer upsert request.') + +export const partyAddressesWire = z + .strictObject({ + billing_address: addressWire, + }) + .describe('A collection of addresses for the party.') + +export const invoiceCustomerWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + usage_attribution: customerUsageAttributionWire.optional(), + billing_address: addressWire.optional(), + key: externalResourceKeyWire.optional(), + }) + + .describe( + "Snapshot of the customer's information at the time the invoice was issued.", + ) + +export const appStripeCreateCheckoutSessionConsentCollectionWire = z + .strictObject({ + payment_method_reuse_agreement: + appStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreementWire.optional(), + promotions: + appStripeCreateCheckoutSessionConsentCollectionPromotionsWire.optional(), + terms_of_service: + appStripeCreateCheckoutSessionConsentCollectionTermsOfServiceWire.optional(), + }) + .describe('Checkout Session consent collection configuration.') + +export const listCustomerEntitlementAccessResponseDataWire = z + .strictObject({ + data: z + .array(entitlementAccessResultWire) + .describe('The list of entitlement access results.'), + }) + .describe('List customer entitlement access response data.') + +export const rateCardEntitlementWire = z + .discriminatedUnion('type', [ + rateCardMeteredEntitlementWire, + rateCardStaticEntitlementWire, + rateCardBooleanEntitlementWire, + ]) + + .describe( + 'Entitlement template configured on a rate card. The feature is taken from the rate card itself, so it is omitted here.', + ) + +export const workflowCollectionAlignmentAnchoredWire = z + .strictObject({ + type: z.literal('anchored').describe('The type of alignment.'), + recurring_period: recurringPeriodWire, + }) + + .describe( + 'BillingWorkflowCollectionAlignmentAnchored specifies the alignment for collecting the pending line items into an invoice.', + ) + +export const subscriptionPagePaginatedResponseWire = z + .strictObject({ + data: z.array(subscriptionWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const subscriptionChangeResponseWire = z + .strictObject({ + current: subscriptionWire, + next: subscriptionWire, + }) + .describe('Response for changing a subscription.') + +export const subscriptionCancelWire = z + .strictObject({ + timing: subscriptionEditTimingWire.optional().default('immediate'), + }) + .describe('Request for canceling a subscription.') + +export const subscriptionChangeWire = z + .strictObject({ + labels: labelsWire.optional(), + settlement_mode: settlementModeWire.optional(), + customer: z + .strictObject({ + id: ulidWire.optional(), + key: externalResourceKeyWire.optional(), + }) + .describe('The customer to create the subscription for.'), + plan: z + .strictObject({ + id: ulidWire.optional(), + key: resourceKeyWire.optional(), + version: z + .number() + .int() + .optional() + + .describe( + 'The plan version of the subscription, if any. If not provided, the latest version of the plan will be used.', + ), + }) + .describe('The plan reference of the subscription.'), + billing_anchor: dateTimeWire.optional(), + timing: subscriptionEditTimingWire, + }) + .describe('Request for changing a subscription.') + +export const createSubscriptionAddonRequestWire = z + .strictObject({ + labels: labelsWire.optional(), + addon: addonReferenceWire, + quantity: z + .number() + .int() + .gte(1) + + .describe( + 'The quantity of the add-on. Always 1 for single instance add-ons.', + ), + timing: subscriptionEditTimingWire, + }) + .describe('SubscriptionAddon create request.') + +export const invoiceUsageQuantityDetailWire = z + .strictObject({ + raw_quantity: numericWire, + converted_quantity: numericWire, + invoiced_quantity: numericWire, + display_unit: z + .string() + .optional() + + .describe('The display unit label (e.g., "GB", "hours", "M tokens").'), + applied_unit_config: unitConfigWire, + }) + + .describe( + 'Usage quantity details on an invoice line item when UnitConfig is in effect. Provides the full audit trail from raw meter output to the invoiced amount.', + ) + +export const appStripeWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + type: z.literal('stripe').describe('The app type.'), + definition: appCatalogItemWire, + status: appStatusWire, + account_id: z + .string() + + .describe( + 'The Stripe account ID associated with the connected Stripe account.', + ), + livemode: z + .boolean() + + .describe( + 'Indicates whether the app is connected to a live Stripe account.', + ), + masked_api_key: z + .string() + + .describe( + 'The masked Stripe API key that only exposes the first and last few characters.', + ), + }) + .describe('Stripe app.') + +export const appSandboxWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + type: z.literal('sandbox').describe('The app type.'), + definition: appCatalogItemWire, + status: appStatusWire, + }) + .describe('Sandbox app can be used for testing billing features.') + +export const appExternalInvoicingWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + type: z.literal('external_invoicing').describe('The app type.'), + definition: appCatalogItemWire, + status: appStatusWire, + enable_draft_sync_hook: z + .boolean() + + .describe( + 'Enable draft synchronization hook. When enabled, invoices will pause at the draft state and wait for the integration to call the draft synchronized endpoint before progressing to the issuing state. This allows the external system to validate and prepare the invoice data. When disabled, invoices automatically progress through the draft state based on the configured workflow timing.', + ), + enable_issuing_sync_hook: z + .boolean() + + .describe( + 'Enable issuing synchronization hook. When enabled, invoices will pause at the issuing state and wait for the integration to call the issuing synchronized endpoint before progressing to the issued state. This ensures the external invoicing system has successfully created and finalized the invoice before it is marked as issued. When disabled, invoices automatically progress through the issuing state and are immediately marked as issued.', + ), + }) + + .describe( + 'External Invoicing app enables integration with third-party invoicing or payment system. The app supports a bi-directional synchronization pattern where OpenMeter Billing manages the invoice lifecycle while the external system handles invoice presentation and payment collection. Integration workflow: 1. The billing system creates invoices and transitions them through lifecycle states (draft → issuing → issued) 2. The integration receives webhook notifications about invoice state changes 3. The integration calls back to provide external system IDs and metadata 4. The integration reports payment events back via the payment status API State synchronization is controlled by hooks that pause invoice progression until the external system confirms synchronization via API callbacks.', + ) + +export const createTaxCodeRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: resourceKeyWire, + app_mappings: z + .array(taxCodeAppMappingWire) + .describe('Mapping of app types to tax codes.'), + }) + .describe('TaxCode create request.') + +export const taxCodeWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + key: resourceKeyWire, + app_mappings: z + .array(taxCodeAppMappingWire) + .describe('Mapping of app types to tax codes.'), + }) + .describe('Tax codes by provider.') + +export const upsertTaxCodeRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + app_mappings: z + .array(taxCodeAppMappingWire) + .describe('Mapping of app types to tax codes.'), + }) + .describe('TaxCode upsert request.') + +export const invoiceWorkflowWire = z + .strictObject({ + invoicing: invoiceWorkflowInvoicingSettingsWire.optional(), + payment: workflowPaymentSettingsWire.optional(), + }) + + .describe( + 'Invoice-level snapshot of the workflow configuration. Contains only the settings that are meaningful for an already-created invoice: invoicing behaviour and payment settings. Collection alignment and tax policy are gather-time / profile-wide concerns and are not included.', + ) + +export const invoiceStatusDetailsWire = z + .strictObject({ + immutable: z + .boolean() + + .describe( + 'Whether the invoice is immutable (i.e. cannot be modified or deleted).', + ), + failed: z.boolean().describe('Whether the invoice is in a failed state.'), + extended_status: z + .string() + + .describe( + 'Fine-grained internal status string providing additional workflow detail beyond the top-level status enum.', + ), + available_actions: invoiceAvailableActionsWire, + }) + .describe('Detailed status information for a standard invoice.') + +export const invoiceLineDiscountsWire = z + .strictObject({ + amount: z + .array(invoiceLineAmountDiscountWire) + .optional() + + .describe( + 'Monetary amount discounts (e.g. from maximum spend commitments).', + ), + usage: z + .array(invoiceLineUsageDiscountWire) + .optional() + .describe('Usage quantity discounts (e.g. free tier usage allowances).'), + }) + .describe('Discounts applied to an invoice line item.') + +export const currencyWire = z + .discriminatedUnion('type', [currencyFiatWire, currencyCustomWire]) + .describe('Fiat or custom currency.') + +export const governanceFeatureAccessWire = z + .strictObject({ + has_access: z + .boolean() + + .describe( + 'Whether the customer currently has access to the feature. `true` for boolean and static entitlements that are available, and for metered entitlements with remaining balance. `false` when the feature is unavailable, the usage limit has been reached, or (when applicable) credits have been exhausted.', + ), + reason: governanceFeatureAccessReasonWire.optional(), + }) + .describe('Access status for a single feature.') + +export const customerDataWire = z + .strictObject({ + billing_profile: profileReferenceWire.optional(), + app_data: appCustomerDataWire.optional(), + }) + .describe('Billing customer data.') + +export const upsertCustomerBillingDataRequestWire = z + .strictObject({ + billing_profile: profileReferenceWire.optional(), + app_data: appCustomerDataWire.optional(), + }) + .describe('CustomerBillingData upsert request.') + +export const creditBalancesWire = z + .strictObject({ + retrieved_at: dateTimeWire, + balances: z + .array(creditBalanceWire) + .describe('The balances by currencies.'), + }) + .describe('The balances of the credits of a customer.') + +export const creditTransactionPaginatedResponseWire = z + .strictObject({ + data: z.array(creditTransactionWire), + meta: cursorMetaWire, + }) + .describe('Cursor paginated response.') + +export const priceGraduatedWire = z + .strictObject({ + type: z.literal('graduated').describe('The type of the price.'), + tiers: z + .array(priceTierWire) + .min(1) + + .describe( + 'The tiers of the graduated price. At least one tier is required.', + ), + }) + + .describe( + "Graduated tiered price. Each tier's rate applies only to the usage within that tier. Pricing can change as cumulative usage crosses tier boundaries. When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are expressed in converted billing units.", + ) + +export const priceVolumeWire = z + .strictObject({ + type: z.literal('volume').describe('The type of the price.'), + tiers: z + .array(priceTierWire) + .min(1) + + .describe( + 'The tiers of the volume price. At least one tier is required.', + ), + }) + + .describe( + 'Volume tiered price. The maximum quantity within a period determines the per-unit price for all units in that period. When UnitConfig is present on the rate card, tier boundaries (up_to_amount) are expressed in converted billing units.', + ) + +export const featureUnitCostWire = z + .discriminatedUnion('type', [ + featureManualUnitCostWire, + featureLlmUnitCostWire, + ]) + + .describe( + 'Per-unit cost configuration for a feature. Either a fixed manual amount or a dynamic LLM cost lookup.', + ) + +export const pricePagePaginatedResponseWire = z + .strictObject({ + data: z.array(llmCostPriceWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const createCreditGrantRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: createLabelsWire.optional(), + funding_method: creditFundingMethodWire, + currency: createCurrencyCodeWire, + amount: numericWire, + purchase: createCreditGrantPurchaseWire.optional(), + tax_config: createCreditGrantTaxConfigWire.optional(), + filters: createCreditGrantFiltersWire.optional(), + priority: z + .number() + .int() + .gte(1) + .lte(1000) + .optional() + .default(10) + + .describe( + 'Draw-down priority of the grant. Lower values have higher priority.', + ), + effective_at: dateTimeWire.optional(), + expires_after: iso8601DurationWire.optional(), + key: externalResourceKeyWire.optional(), + }) + .describe('CreditGrant create request.') + +export const creditGrantWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + funding_method: creditFundingMethodWire, + currency: billingCurrencyCodeWire, + amount: numericWire, + purchase: creditGrantPurchaseWire.optional(), + tax_config: creditGrantTaxConfigWire.optional(), + invoice: creditGrantInvoiceReferenceWire.optional(), + filters: creditGrantFiltersWire.optional(), + priority: z + .number() + .int() + .gte(1) + .lte(1000) + .optional() + .default(10) + + .describe( + 'Draw-down priority of the grant. Lower values have higher priority.', + ), + effective_at: dateTimeWire.optional(), + key: externalResourceKeyWire.optional(), + expires_at: dateTimeWire.optional(), + voided_at: dateTimeWire.optional(), + status: creditGrantStatusWire, + }) + + .describe( + 'A credit grant allocates credits to a customer. Credits are drawn down against charges according to the settlement mode configured on the rate card.', + ) + +export const createChargeFlatFeeRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + type: z.literal('flat_fee').describe('The type of the charge.'), + currency: currencyCodeWire, + invoice_at: dateTimeWire, + service_period: closedPeriodWire, + unique_reference_id: z + .string() + .optional() + .describe('Unique reference ID of the charge.'), + settlement_mode: settlementModeWire, + tax_config: taxConfigWire.optional(), + payment_term: pricePaymentTermWire, + discounts: chargeFlatFeeDiscountsWire.optional(), + feature_key: z + .string() + .optional() + .describe('The feature associated with the charge, when applicable.'), + proration_configuration: rateCardProrationConfigurationWire, + amount_before_proration: currencyAmountWire, + full_service_period: closedPeriodWire.optional(), + billing_period: closedPeriodWire.optional(), + }) + .describe('Flat fee charge create request.') + +export const workflowTaxSettingsWire = z + .strictObject({ + enabled: z + .boolean() + .optional() + .default(true) + + .describe( + 'Enable automatic tax calculation when tax is supported by the app. For example, with Stripe Invoicing when enabled, tax is calculated via Stripe Tax.', + ), + enforced: z + .boolean() + .optional() + .default(false) + + .describe( + 'Enforce tax calculation when tax is supported by the app. When enabled, the billing system will not allow to create an invoice without tax calculation. Enforcement is different per apps, for example, Stripe app requires customer to have a tax location when starting a paid subscription.', + ), + default_tax_config: taxConfigWire.optional(), + }) + .describe('Tax settings for a billing workflow.') + +export const planAddonPagePaginatedResponseWire = z + .strictObject({ + data: z.array(planAddonWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const ingestedEventPaginatedResponseWire = z + .strictObject({ + data: z.array(ingestedEventWire), + meta: cursorMetaWire, + }) + .describe('Cursor paginated response.') + +export const invalidParametersWire = z + .array(invalidParameterWire) + .min(1) + .describe('The list of parameters that failed validation.') + +export const meterQueryRequestWire = z + .strictObject({ + from: dateTimeWire.optional(), + to: dateTimeWire.optional(), + granularity: meterQueryGranularityWire.optional(), + time_zone: z + .string() + .optional() + .default('UTC') + + .describe( + 'The value is the name of the time zone as defined in the IANA Time Zone Database (http://www.iana.org/time-zones). The time zone is used to determine the start and end of the time buckets. If not specified, the UTC timezone will be used.', + ), + group_by_dimensions: z + .array(z.string()) + .max(100) + .optional() + .describe('The dimensions to group the results by.'), + filters: meterQueryFiltersWire.optional(), + }) + .describe('A meter query request.') + +export const customerPagePaginatedResponseWire = z + .strictObject({ + data: z.array(customerWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const partyWire = z + .strictObject({ + id: z.string().optional().describe('Unique identifier for the party.'), + key: externalResourceKeyWire.optional(), + name: z + .string() + .optional() + .describe('Legal name or representation of the party.'), + tax_id: partyTaxIdentityWire.optional(), + addresses: partyAddressesWire.optional(), + }) + .describe('Party represents a person or business entity.') + +export const supplierWire = z + .strictObject({ + id: z.string().optional().describe('Unique identifier for the party.'), + name: z + .string() + .optional() + .describe('Legal name or representation of the party.'), + tax_id: partyTaxIdentityWire.optional(), + addresses: partyAddressesWire.optional(), + }) + + .describe( + "Snapshot of the supplier's information at the time the invoice was issued. Structurally a read-only subset of `BillingParty` (the type configured on the billing profile), so the snapshot stays aligned with the source. `key` is omitted because it is not part of the snapshotted supplier data.", + ) + +export const appStripeCreateCheckoutSessionRequestOptionsWire = z + .strictObject({ + billing_address_collection: + appStripeCreateCheckoutSessionBillingAddressCollectionWire + .optional() + .default('auto'), + cancel_url: z + .string() + .optional() + + .describe( + 'URL to redirect customers who cancel the checkout session. Not allowed when ui_mode is "embedded".', + ), + client_reference_id: z + .string() + .optional() + + .describe( + 'Unique reference string for reconciling sessions with internal systems. Can be a customer ID, cart ID, or any other identifier.', + ), + customer_update: + appStripeCreateCheckoutSessionCustomerUpdateWire.optional(), + consent_collection: + appStripeCreateCheckoutSessionConsentCollectionWire.optional(), + currency: currencyCodeWire.optional(), + custom_text: appStripeCheckoutSessionCustomTextParamsWire.optional(), + expires_at: z.coerce + .bigint() + .gte(-9223372036854775808n) + .lte(9223372036854775807n) + .optional() + + .describe( + 'Unix timestamp when the checkout session expires. Can be 30 minutes to 24 hours from creation. Defaults to 24 hours.', + ), + locale: z + .string() + .optional() + + .describe( + 'IETF language tag for the checkout UI locale. If blank or "auto", uses the browser\'s locale. Example: "en", "fr", "de".', + ), + metadata: z + .record(z.string(), z.string()) + .optional() + + .describe( + 'Set of key-value pairs to attach to the checkout session. Useful for storing additional structured information.', + ), + return_url: z + .string() + .optional() + + .describe( + 'Return URL for embedded checkout sessions after payment authentication. Required if ui_mode is "embedded" and redirect-based payment methods are enabled.', + ), + success_url: z + .string() + .optional() + + .describe( + 'Success URL to redirect customers after completing payment or setup. Not allowed when ui_mode is "embedded". See: https://docs.stripe.com/payments/checkout/custom-success-page', + ), + ui_mode: appStripeCheckoutSessionUiModeWire.optional().default('hosted'), + payment_method_types: z + .array(z.string()) + .optional() + + .describe( + 'List of payment method types to enable (e.g., "card", "us_bank_account"). If not specified, Stripe enables all relevant payment methods.', + ), + redirect_on_completion: + appStripeCreateCheckoutSessionRedirectOnCompletionWire.optional(), + tax_id_collection: + appStripeCreateCheckoutSessionTaxIdCollectionWire.optional(), + }) + + .describe( + "Configuration options for creating a Stripe Checkout Session. Based on Stripe's [Checkout Session API parameters](https://docs.stripe.com/api/checkout/sessions/create).", + ) + +export const workflowCollectionAlignmentWire = z + .discriminatedUnion('type', [ + workflowCollectionAlignmentSubscriptionWire, + workflowCollectionAlignmentAnchoredWire, + ]) + + .describe( + 'The alignment for collecting the pending line items into an invoice. Defaults to subscription, which means that we are to create a new invoice every time the a subscription period starts (for in advance items) or ends (for in arrears items).', + ) + +export const appWire = z + .discriminatedUnion('type', [ + appStripeWire, + appSandboxWire, + appExternalInvoicingWire, + ]) + .describe('Installed application.') + +export const taxCodePagePaginatedResponseWire = z + .strictObject({ + data: z.array(taxCodeWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const invoiceWorkflowSettingsWire = z + .strictObject({ + apps: invoiceWorkflowAppsReferencesWire.optional(), + source_billing_profile: profileReferenceWire, + workflow: invoiceWorkflowWire, + }) + + .describe( + 'Snapshot of the billing workflow configuration captured at invoice creation.', + ) + +export const invoiceDetailedLineWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + service_period: closedPeriodWire, + totals: totalsWire, + category: invoiceDetailedLineCostCategoryWire.default('regular'), + discounts: invoiceLineDiscountsWire.optional(), + credits_applied: z + .array(invoiceLineCreditsAppliedWire) + .optional() + .describe('Credit applied to this detailed line.'), + external_references: invoiceLineExternalReferencesWire.optional(), + quantity: numericWire, + unit_price: numericWire, + }) + + .describe( + 'A detailed (child) sub-line belonging to a parent invoice line. Detailed lines represent the individual flat-fee components that make up a usage-based parent line after quantity snapshotting.', + ) + +export const currencyPagePaginatedResponseWire = z + .strictObject({ + data: z.array(currencyWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const governanceQueryResultWire = z + .strictObject({ + matched: z + .array(z.string()) + + .describe( + 'The list of identifiers from the request that resolved to this customer. Each entry is either the customer `key` or one of its usage-attribution subject `key`s. Duplicate or aliased identifiers that resolve to the same customer collapse to a single result entry, with every requested identifier listed here.', + ), + customer: customerWire, + features: z + .record(z.string(), governanceFeatureAccessWire) + + .describe( + 'Map of features with their access status. Map keys are the feature keys requested in `feature.keys`, or every feature `key` available in the organization when the feature filter was omitted.', + ), + updated_at: dateTimeWire, + }) + .describe('Access evaluation result for a single resolved customer.') + +export const priceWire = z + .discriminatedUnion('type', [ + priceFreeWire, + priceFlatWire, + priceUnitWire, + priceGraduatedWire, + priceVolumeWire, + ]) + .describe('Price.') + +export const priceUsageBasedWire = z + .discriminatedUnion('type', [ + priceUnitWire, + priceGraduatedWire, + priceVolumeWire, + ]) + + .describe( + 'Usage-based price types that can appear on a usage-based rate card. When UnitConfig is present on the rate card, these price types operate on billing units (i.e. post-conversion quantities), not raw metered units.', + ) + +export const featureWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + key: resourceKeyWire, + meter: featureMeterReferenceWire.optional(), + unit_cost: featureUnitCostWire.optional(), + }) + .describe('A capability or billable dimension offered by a provider.') + +export const createFeatureRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: resourceKeyWire, + meter: featureMeterReferenceWire.optional(), + unit_cost: featureUnitCostWire.optional(), + }) + .describe('Feature create request.') + +export const updateFeatureRequestWire = z + .strictObject({ + unit_cost: z + .union([featureUnitCostWire, z.null()]) + .optional() + + .describe( + 'Optional per-unit cost configuration. Use "manual" for a fixed per-unit cost, or "llm" to look up cost from the LLM cost database based on meter group-by properties. Set to `null` to clear the existing unit cost; omit to leave it unchanged.', + ), + }) + + .describe( + 'Request body for updating a feature. Currently only the unit_cost field can be updated.', + ) + +export const creditGrantPagePaginatedResponseWire = z + .strictObject({ + data: z.array(creditGrantWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const badRequestWire = z + .intersection( + baseErrorWire, + z.strictObject({ + invalid_parameters: invalidParametersWire, + }), + ) + .describe('Bad Request.') + +export const invoiceBaseWire = z + .strictObject({ + id: ulidWire, + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + number: invoiceNumberWire, + currency: currencyCodeWire, + supplier: supplierWire, + customer: invoiceCustomerWire, + totals: totalsWire, + service_period: closedPeriodWire, + validation_issues: z + .array(invoiceValidationIssueWire) + .optional() + + .describe( + 'Validation issues found during invoice processing. Present only when there are one or more validation findings. An empty list is omitted.', + ), + external_references: invoiceExternalReferencesWire.optional(), + }) + + .describe( + 'Base fields shared by all invoice types. Spread this model into each concrete invoice variant.', + ) + +export const customerStripeCreateCheckoutSessionRequestWire = z + .strictObject({ + stripe_options: appStripeCreateCheckoutSessionRequestOptionsWire, + }) + + .describe( + 'Request to create a Stripe Checkout Session for the customer. Checkout Sessions are used to collect payment method information from customers in a secure, Stripe-hosted interface. This integration uses setup mode to collect payment methods that can be charged later for subscription billing.', + ) + +export const workflowCollectionSettingsWire = z + .strictObject({ + alignment: workflowCollectionAlignmentWire.optional().default({ + type: 'subscription', + }), + interval: z + .string() + .optional() + .default('PT1H') + + .describe( + 'This grace period can be used to delay the collection of the pending line items specified in alignment. This is useful, in case of multiple subscriptions having slightly different billing periods.', + ), + }) + + .describe( + 'Workflow collection specifies how to collect the pending line items for an invoice.', + ) + +export const appPagePaginatedResponseWire = z + .strictObject({ + data: z.array(appWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const profileAppsWire = z + .strictObject({ + tax: appWire, + invoicing: appWire, + payment: appWire, + }) + .describe('Applications used by a billing profile.') + +export const governanceQueryResponseWire = z + .strictObject({ + data: z + .array(governanceQueryResultWire) + .describe('Access evaluation results, one entry per resolved customer.'), + errors: z + .array(governanceQueryErrorWire) + .describe('Partial errors encountered while processing the request.'), + meta: cursorMetaWire, + }) + .describe('Response of the governance query.') + +export const chargeFlatFeeWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + type: z.literal('flat_fee').describe('The type of the charge.'), + customer: billingCustomerReferenceWire, + lifecycle_controller: lifecycleControllerWire, + subscription: subscriptionReferenceWire.optional(), + currency: currencyCodeWire, + status: chargeStatusWire, + invoice_at: dateTimeWire, + service_period: closedPeriodWire, + full_service_period: closedPeriodWire, + billing_period: closedPeriodWire, + advance_after: dateTimeWire.optional(), + unique_reference_id: z + .string() + .optional() + .describe('Unique reference ID of the charge.'), + settlement_mode: settlementModeWire, + tax_config: taxConfigWire.optional(), + payment_term: pricePaymentTermWire, + discounts: chargeFlatFeeDiscountsWire.optional(), + feature_key: z + .string() + .optional() + .describe('The feature associated with the charge, when applicable.'), + proration_configuration: rateCardProrationConfigurationWire, + amount_after_proration: currencyAmountWire, + price: priceWire, + }) + .describe('A flat fee charge for a customer.') + +export const chargeUsageBasedWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + type: z.literal('usage_based').describe('The type of the charge.'), + customer: billingCustomerReferenceWire, + lifecycle_controller: lifecycleControllerWire, + subscription: subscriptionReferenceWire.optional(), + currency: currencyCodeWire, + status: chargeStatusWire, + invoice_at: dateTimeWire, + service_period: closedPeriodWire, + full_service_period: closedPeriodWire, + billing_period: closedPeriodWire, + advance_after: dateTimeWire.optional(), + unique_reference_id: z + .string() + .optional() + .describe('Unique reference ID of the charge.'), + settlement_mode: settlementModeWire, + tax_config: taxConfigWire.optional(), + discounts: rateCardDiscountsWire.optional(), + feature_key: z.string().describe('The feature associated with the charge.'), + totals: chargeTotalsWire, + price: priceWire, + }) + .describe('A usage-based charge for a customer.') + +export const createChargeUsageBasedRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + type: z.literal('usage_based').describe('The type of the charge.'), + currency: currencyCodeWire, + invoice_at: dateTimeWire, + service_period: closedPeriodWire, + unique_reference_id: z + .string() + .optional() + .describe('Unique reference ID of the charge.'), + settlement_mode: settlementModeWire, + tax_config: taxConfigWire.optional(), + discounts: rateCardDiscountsWire.optional(), + feature_key: z.string().describe('The feature associated with the charge.'), + price: priceWire, + full_service_period: closedPeriodWire.optional(), + billing_period: closedPeriodWire.optional(), + }) + .describe('Usage-based charge create request.') + +export const rateCardWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: resourceKeyWire, + feature: featureReferenceWire.optional(), + billing_cadence: iso8601DurationWire.optional(), + price: priceWire, + unit_config: unitConfigWire.optional(), + payment_term: pricePaymentTermWire.optional().default('in_arrears'), + commitments: spendCommitmentsWire.optional(), + discounts: rateCardDiscountsWire.optional(), + tax_config: rateCardTaxConfigWire.optional(), + entitlement: rateCardEntitlementWire.optional(), + }) + + .describe( + 'A rate card defines the pricing and entitlement of a feature or service.', + ) + +export const invoiceLineRateCardWire = z + .strictObject({ + price: priceWire, + tax_config: rateCardTaxConfigWire.optional(), + feature_key: resourceKeyWire.optional(), + discounts: rateCardDiscountsWire.optional(), + }) + .describe('Rate card configuration snapshot for a usage-based invoice line.') + +export const featurePagePaginatedResponseWire = z + .strictObject({ + data: z.array(featureWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const workflowWire = z + .strictObject({ + collection: workflowCollectionSettingsWire.optional(), + invoicing: workflowInvoicingSettingsWire.optional(), + payment: workflowPaymentSettingsWire.optional(), + tax: workflowTaxSettingsWire.optional(), + }) + .describe('Billing workflow settings.') + +export const chargeWire = z + .discriminatedUnion('type', [chargeFlatFeeWire, chargeUsageBasedWire]) + .describe('Customer charge.') + +export const createChargeRequestWire = z + .discriminatedUnion('type', [ + createChargeFlatFeeRequestWire, + createChargeUsageBasedRequestWire, + ]) + .describe('Customer charge.') + +export const subscriptionAddonRateCardWire = z + .strictObject({ + rate_card: rateCardWire, + affected_subscription_item_ids: z + .array(ulidWire) + + .describe( + 'The IDs of the subscription items that this rate card belongs to.', + ), + }) + .describe('A rate card for a subscription add-on.') + +export const planPhaseWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: resourceKeyWire, + duration: iso8601DurationWire.optional(), + rate_cards: z.array(rateCardWire).describe('The rate cards of the plan.'), + }) + + .describe( + "The plan phase or pricing ramp allows changing a plan's rate cards over time as a subscription progresses.", + ) + +export const addonWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + key: resourceKeyWire, + version: z + .number() + .int() + .gte(1) + .default(1) + + .describe( + 'Version of the add-on. Incremented when the add-on is updated.', + ), + instance_type: addonInstanceTypeWire, + currency: billingCurrencyCodeWire, + effective_from: dateTimeWire.optional(), + effective_to: dateTimeWire.optional(), + status: addonStatusWire, + rate_cards: z.array(rateCardWire).describe('The rate cards of the add-on.'), + validation_errors: z + .array(productCatalogValidationErrorWire) + .optional() + .describe('List of validation errors.'), + }) + + .describe( + 'Add-on allows extending subscriptions with compatible plans with additional ratecards.', + ) + +export const createAddonRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: resourceKeyWire, + instance_type: addonInstanceTypeWire, + currency: billingCurrencyCodeWire, + rate_cards: z.array(rateCardWire).describe('The rate cards of the add-on.'), + }) + .describe('Addon create request.') + +export const upsertAddonRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + instance_type: addonInstanceTypeWire, + rate_cards: z.array(rateCardWire).describe('The rate cards of the add-on.'), + }) + .describe('Addon upsert request.') + +export const invoiceStandardLineWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + type: z + .literal('standard_line') + .describe('The type of charge this line item represents.'), + lifecycle_controller: lifecycleControllerWire, + service_period: closedPeriodWire, + totals: totalsWire, + discounts: invoiceLineDiscountsWire.optional(), + credits_applied: z + .array(invoiceLineCreditsAppliedWire) + .optional() + .describe('Credit applied to this line item.'), + external_references: invoiceLineExternalReferencesWire.optional(), + subscription: subscriptionReferenceWire.optional(), + rate_card: invoiceLineRateCardWire, + detailed_lines: z + .array(invoiceDetailedLineWire) + + .describe( + 'Detailed sub-lines that this line has been broken down into. Present when line has individual details.', + ), + charge: chargeReferenceWire.optional(), + }) + + .describe( + 'A top-level line item on an invoice. Each line represents a single charge, typically associated with a rate card from a subscription. Detailed (child) lines are nested under `detailed_lines` when present.', + ) + +export const profileWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + supplier: partyWire, + workflow: workflowWire, + apps: profileAppReferencesWire, + default: z.boolean().describe('Whether this is the default profile.'), + }) + + .describe( + 'Billing profiles contain the settings for billing and controls invoice generation.', + ) + +export const createBillingProfileRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + supplier: partyWire, + workflow: workflowWire, + apps: profileAppReferencesWire, + default: z.boolean().describe('Whether this is the default profile.'), + }) + .describe('BillingProfile create request.') + +export const upsertBillingProfileRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + supplier: partyWire, + workflow: workflowWire, + default: z.boolean().describe('Whether this is the default profile.'), + }) + .describe('BillingProfile upsert request.') + +export const chargePagePaginatedResponseWire = z + .strictObject({ + data: z.array(chargeWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const subscriptionAddonWire = z + .strictObject({ + id: ulidWire, + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + addon: addonReferenceWire, + quantity: z + .number() + .int() + .gte(1) + + .describe( + 'The quantity of the add-on. Always 1 for single instance add-ons.', + ), + quantity_at: dateTimeWire, + active_from: dateTimeWire, + active_to: dateTimeWire.optional(), + timeline: z + .array(subscriptionAddonTimelineSegmentWire) + + .describe( + 'The timeline of the add-on. The returned periods are sorted and continuous.', + ), + rate_cards: z + .array(subscriptionAddonRateCardWire) + .describe('The rate cards of the add-on.'), + }) + .describe('Addon purchased with a subscription.') + +export const planWire = z + .strictObject({ + id: ulidWire, + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + key: resourceKeyWire, + version: z + .number() + .int() + .gte(1) + .default(1) + + .describe( + 'Plans are versioned to allow you to make changes without affecting running subscriptions.', + ), + currency: currencyCodeWire, + billing_cadence: iso8601DurationWire, + pro_rating_enabled: z + .boolean() + .optional() + .default(true) + .describe('Whether pro-rating is enabled for this plan.'), + effective_from: dateTimeWire.optional(), + effective_to: dateTimeWire.optional(), + status: planStatusWire, + phases: z + .array(planPhaseWire) + .min(1) + + .describe( + 'The plan phases define the pricing ramp for a subscription. A phase switch occurs only at the end of a billing period. At least one phase is required.', + ), + settlement_mode: settlementModeWire + .optional() + .default('credit_then_invoice'), + validation_errors: z + .array(productCatalogValidationErrorWire) + .optional() + + .describe( + 'List of validation errors in `draft` state that prevent the plan from being published.', + ), + }) + .describe('Plans provide a template for subscriptions.') + +export const createPlanRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + key: resourceKeyWire, + currency: currencyCodeWire, + billing_cadence: iso8601DurationWire, + pro_rating_enabled: z + .boolean() + .optional() + .default(true) + .describe('Whether pro-rating is enabled for this plan.'), + phases: z + .array(planPhaseWire) + .min(1) + + .describe( + 'The plan phases define the pricing ramp for a subscription. A phase switch occurs only at the end of a billing period. At least one phase is required.', + ), + }) + .describe('Plan create request.') + +export const upsertPlanRequestWire = z + .strictObject({ + name: z + .string() + .min(1) + .max(256) + .describe('Display name of the resource. Between 1 and 256 characters.'), + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + pro_rating_enabled: z + .boolean() + .optional() + .default(true) + .describe('Whether pro-rating is enabled for this plan.'), + phases: z + .array(planPhaseWire) + .min(1) + + .describe( + 'The plan phases define the pricing ramp for a subscription. A phase switch occurs only at the end of a billing period. At least one phase is required.', + ), + }) + .describe('Plan upsert request.') + +export const addonPagePaginatedResponseWire = z + .strictObject({ + data: z.array(addonWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const invoiceLineWire = z + .discriminatedUnion('type', [invoiceStandardLineWire]) + + .describe( + 'A top-level line item on an invoice. Each line represents a single charge, typically associated with a rate card from a subscription. Detailed (child) lines are nested under `detailed_lines` when present.', + ) + +export const profilePagePaginatedResponseWire = z + .strictObject({ + data: z.array(profileWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const subscriptionAddonPagePaginatedResponseWire = z + .strictObject({ + data: z.array(subscriptionAddonWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const planPagePaginatedResponseWire = z + .strictObject({ + data: z.array(planWire), + meta: paginatedMetaWire, + }) + .describe('Page paginated response.') + +export const invoiceStandardWire = z + .strictObject({ + id: ulidWire, + description: z + .string() + .max(1024) + .optional() + + .describe( + 'Optional description of the resource. Maximum 1024 characters.', + ), + labels: labelsWire.optional(), + created_at: dateTimeWire, + updated_at: dateTimeWire, + deleted_at: dateTimeWire.optional(), + number: invoiceNumberWire, + currency: currencyCodeWire, + supplier: supplierWire, + customer: invoiceCustomerWire, + totals: totalsWire, + service_period: closedPeriodWire, + validation_issues: z + .array(invoiceValidationIssueWire) + .optional() + + .describe( + 'Validation issues found during invoice processing. Present only when there are one or more validation findings. An empty list is omitted.', + ), + external_references: invoiceExternalReferencesWire.optional(), + type: z + .literal('standard') + .describe('Discriminator field identifying this as a standard invoice.'), + status: invoiceStandardStatusWire, + status_details: invoiceStatusDetailsWire, + issued_at: dateTimeWire.optional(), + draft_until: dateTimeWire.optional(), + quantity_snapshotted_at: dateTimeWire.optional(), + collection_at: dateTimeWire.optional(), + due_at: dateTimeWire.optional(), + sent_to_customer_at: dateTimeWire.optional(), + workflow: invoiceWorkflowSettingsWire, + lines: z + .array(invoiceLineWire) + .optional() + + .describe( + 'Line items on this invoice. Always returned on single-resource GET; omitted on list endpoints unless explicitly expanded.', + ), + }) + .describe('A standard invoice for charges owed by the customer.') + +export const invoiceWire = z + .discriminatedUnion('type', [invoiceStandardWire]) + + .describe( + 'An invoice issued to a customer. The `type` field determines the concrete variant: - `standard`: a standard invoice for charges owed.', + ) + +export const listMeteringEventsQueryParamsWire = z.object({ + page: cursorPaginationQueryPageWire.optional(), + filter: listEventsParamsFilterWire.optional(), + sort: sortQueryWire.optional(), +}) + +export const listMeteringEventsResponseWire = z.strictObject({ + data: z.array(ingestedEventWire), + meta: cursorMetaWire, +}) + +export const ingestMeteringEventsBodyWire = z.union([ + eventWire, + z.array(eventWire), +]) + +export const createMeterBodyWire = createMeterRequestWire + +export const createMeterResponseWire = meterWire + +export const getMeterPathParamsWire = z.object({ + meterId: ulidWire, +}) + +export const getMeterResponseWire = meterWire + +export const listMetersQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listMetersParamsFilterWire.optional(), +}) + +export const listMetersResponseWire = z.strictObject({ + data: z.array(meterWire), + meta: paginatedMetaWire, +}) + +export const updateMeterPathParamsWire = z.object({ + meterId: ulidWire, +}) + +export const updateMeterBodyWire = updateMeterRequestWire + +export const updateMeterResponseWire = meterWire + +export const deleteMeterPathParamsWire = z.object({ + meterId: ulidWire, +}) + +export const queryMeterPathParamsWire = z.object({ + meterId: ulidWire, +}) + +export const queryMeterBodyWire = meterQueryRequestWire + +export const queryMeterResponseWire = meterQueryResultWire + +export const queryMeterCsvPathParamsWire = z.object({ + meterId: ulidWire, +}) + +export const queryMeterCsvBodyWire = meterQueryRequestWire + +export const queryMeterCsvResponseWire = z.string() + +export const createCustomerBodyWire = createCustomerRequestWire + +export const createCustomerResponseWire = customerWire + +export const getCustomerPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const getCustomerResponseWire = customerWire + +export const listCustomersQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listCustomersParamsFilterWire.optional(), +}) + +export const listCustomersResponseWire = z.strictObject({ + data: z.array(customerWire), + meta: paginatedMetaWire, +}) + +export const upsertCustomerPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const upsertCustomerBodyWire = upsertCustomerRequestWire + +export const upsertCustomerResponseWire = customerWire + +export const deleteCustomerPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const getCustomerBillingPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const getCustomerBillingResponseWire = customerDataWire + +export const updateCustomerBillingPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const updateCustomerBillingBodyWire = + upsertCustomerBillingDataRequestWire + +export const updateCustomerBillingResponseWire = customerDataWire + +export const updateCustomerBillingAppDataPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const updateCustomerBillingAppDataBodyWire = + upsertAppCustomerDataRequestWire + +export const updateCustomerBillingAppDataResponseWire = appCustomerDataWire + +export const createCustomerStripeCheckoutSessionPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const createCustomerStripeCheckoutSessionBodyWire = + customerStripeCreateCheckoutSessionRequestWire + +export const createCustomerStripeCheckoutSessionResponseWire = + appStripeCreateCheckoutSessionResultWire + +export const createCustomerStripePortalSessionPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const createCustomerStripePortalSessionBodyWire = + customerStripeCreateCustomerPortalSessionRequestWire + +export const createCustomerStripePortalSessionResponseWire = + appStripeCreateCustomerPortalSessionResultWire + +export const listCustomerEntitlementAccessPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const listCustomerEntitlementAccessResponseWire = + listCustomerEntitlementAccessResponseDataWire + +export const createCreditGrantPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const createCreditGrantBodyWire = createCreditGrantRequestWire + +export const createCreditGrantResponseWire = creditGrantWire + +export const getCreditGrantPathParamsWire = z.object({ + customerId: ulidWire, + creditGrantId: ulidWire, +}) + +export const getCreditGrantResponseWire = creditGrantWire + +export const listCreditGrantsPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const listCreditGrantsQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + filter: listCreditGrantsParamsFilterWire.optional(), +}) + +export const listCreditGrantsResponseWire = z.strictObject({ + data: z.array(creditGrantWire), + meta: paginatedMetaWire, +}) + +export const getCustomerCreditBalancePathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const getCustomerCreditBalanceQueryParamsWire = z.object({ + timestamp: dateTimeWire.optional(), + filter: getCreditBalanceParamsFilterWire.optional(), +}) + +export const getCustomerCreditBalanceResponseWire = creditBalancesWire + +export const createCreditAdjustmentPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const createCreditAdjustmentBodyWire = createCreditAdjustmentRequestWire + +export const createCreditAdjustmentResponseWire = creditAdjustmentWire + +export const updateCreditGrantExternalSettlementPathParamsWire = z.object({ + customerId: ulidWire, + creditGrantId: ulidWire, +}) + +export const updateCreditGrantExternalSettlementBodyWire = + updateCreditGrantExternalSettlementRequestWire + +export const updateCreditGrantExternalSettlementResponseWire = creditGrantWire + +export const listCreditTransactionsPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const listCreditTransactionsQueryParamsWire = z.object({ + page: cursorPaginationQueryPageWire.optional(), + filter: listCreditTransactionsParamsFilterWire.optional(), +}) + +export const listCreditTransactionsResponseWire = z.strictObject({ + data: z.array(creditTransactionWire), + meta: cursorMetaWire, +}) + +export const listCustomerChargesPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const listCustomerChargesQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listChargesParamsFilterWire.optional(), + expand: z + .array(chargesExpandWire) + .optional() + + .describe( + "Expand full objects for referenced entities. Supported values are: - `real_time_usage`: Expand the charge's real-time usage.", + ), +}) + +export const listCustomerChargesResponseWire = z.strictObject({ + data: z.array(chargeWire), + meta: paginatedMetaWire, +}) + +export const createCustomerChargesPathParamsWire = z.object({ + customerId: ulidWire, +}) + +export const createCustomerChargesBodyWire = createChargeRequestWire + +export const createCustomerChargesResponseWire = chargeWire + +export const createSubscriptionBodyWire = subscriptionCreateWire + +export const createSubscriptionResponseWire = subscriptionWire + +export const listSubscriptionsQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listSubscriptionsParamsFilterWire.optional(), +}) + +export const listSubscriptionsResponseWire = z.strictObject({ + data: z.array(subscriptionWire), + meta: paginatedMetaWire, +}) + +export const getSubscriptionPathParamsWire = z.object({ + subscriptionId: ulidWire, +}) + +export const getSubscriptionResponseWire = subscriptionWire + +export const cancelSubscriptionPathParamsWire = z.object({ + subscriptionId: ulidWire, +}) + +export const cancelSubscriptionBodyWire = subscriptionCancelWire + +export const cancelSubscriptionResponseWire = subscriptionWire + +export const unscheduleCancelationPathParamsWire = z.object({ + subscriptionId: ulidWire, +}) + +export const unscheduleCancelationResponseWire = subscriptionWire + +export const changeSubscriptionPathParamsWire = z.object({ + subscriptionId: ulidWire, +}) + +export const changeSubscriptionBodyWire = subscriptionChangeWire + +export const changeSubscriptionResponseWire = subscriptionChangeResponseWire + +export const createSubscriptionAddonPathParamsWire = z.object({ + subscriptionId: ulidWire, +}) + +export const createSubscriptionAddonBodyWire = + createSubscriptionAddonRequestWire + +export const createSubscriptionAddonResponseWire = subscriptionAddonWire + +export const listSubscriptionAddonsPathParamsWire = z.object({ + subscriptionId: ulidWire, +}) + +export const listSubscriptionAddonsQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), +}) + +export const listSubscriptionAddonsResponseWire = z.strictObject({ + data: z.array(subscriptionAddonWire), + meta: paginatedMetaWire, +}) + +export const getSubscriptionAddonPathParamsWire = z.object({ + subscriptionId: ulidWire, + subscriptionAddonId: ulidWire, +}) + +export const getSubscriptionAddonResponseWire = subscriptionAddonWire + +export const listAppsQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), +}) + +export const listAppsResponseWire = z.strictObject({ + data: z.array(appWire), + meta: paginatedMetaWire, +}) + +export const getAppPathParamsWire = z.object({ + appId: ulidWire, +}) + +export const getAppResponseWire = appWire + +export const listBillingProfilesQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), +}) + +export const listBillingProfilesResponseWire = z.strictObject({ + data: z.array(profileWire), + meta: paginatedMetaWire, +}) + +export const createBillingProfileBodyWire = createBillingProfileRequestWire + +export const createBillingProfileResponseWire = profileWire + +export const getBillingProfilePathParamsWire = z.object({ + id: ulidWire, +}) + +export const getBillingProfileResponseWire = profileWire + +export const updateBillingProfilePathParamsWire = z.object({ + id: ulidWire, +}) + +export const updateBillingProfileBodyWire = upsertBillingProfileRequestWire + +export const updateBillingProfileResponseWire = profileWire + +export const deleteBillingProfilePathParamsWire = z.object({ + id: ulidWire, +}) + +export const getInvoicePathParamsWire = z.object({ + invoiceId: ulidWire, +}) + +export const getInvoiceResponseWire = invoiceWire + +export const createTaxCodeBodyWire = createTaxCodeRequestWire + +export const createTaxCodeResponseWire = taxCodeWire + +export const getTaxCodePathParamsWire = z.object({ + taxCodeId: ulidWire, +}) + +export const getTaxCodeResponseWire = taxCodeWire + +export const listTaxCodesQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + includeDeleted: z.coerce + .boolean() + .optional() + .describe('Include deleted tax codes in the response.'), +}) + +export const listTaxCodesResponseWire = z.strictObject({ + data: z.array(taxCodeWire), + meta: paginatedMetaWire, +}) + +export const upsertTaxCodePathParamsWire = z.object({ + taxCodeId: ulidWire, +}) + +export const upsertTaxCodeBodyWire = upsertTaxCodeRequestWire + +export const upsertTaxCodeResponseWire = taxCodeWire + +export const deleteTaxCodePathParamsWire = z.object({ + taxCodeId: ulidWire, +}) + +export const listCurrenciesQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listCurrenciesParamsFilterWire.optional(), +}) + +export const listCurrenciesResponseWire = z.strictObject({ + data: z.array(currencyWire), + meta: paginatedMetaWire, +}) + +export const createCustomCurrencyBodyWire = createCurrencyCustomRequestWire + +export const createCustomCurrencyResponseWire = currencyCustomWire + +export const listCostBasesPathParamsWire = z.object({ + currencyId: ulidWire, +}) + +export const listCostBasesQueryParamsWire = z.object({ + filter: listCostBasesParamsFilterWire.optional(), + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), +}) + +export const listCostBasesResponseWire = z.strictObject({ + data: z.array(costBasisWire), + meta: paginatedMetaWire, +}) + +export const createCostBasisPathParamsWire = z.object({ + currencyId: ulidWire, +}) + +export const createCostBasisBodyWire = createCostBasisRequestWire + +export const createCostBasisResponseWire = costBasisWire + +export const listFeaturesQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listFeatureParamsFilterWire.optional(), +}) + +export const listFeaturesResponseWire = z.strictObject({ + data: z.array(featureWire), + meta: paginatedMetaWire, +}) + +export const createFeatureBodyWire = createFeatureRequestWire + +export const createFeatureResponseWire = featureWire + +export const getFeaturePathParamsWire = z.object({ + featureId: ulidWire, +}) + +export const getFeatureResponseWire = featureWire + +export const updateFeaturePathParamsWire = z.object({ + featureId: ulidWire, +}) + +export const updateFeatureBodyWire = updateFeatureRequestWire + +export const updateFeatureResponseWire = featureWire + +export const deleteFeaturePathParamsWire = z.object({ + featureId: ulidWire, +}) + +export const queryFeatureCostPathParamsWire = z.object({ + featureId: ulidWire, +}) + +export const queryFeatureCostBodyWire = meterQueryRequestWire + +export const queryFeatureCostResponseWire = featureCostQueryResultWire + +export const listLlmCostPricesQueryParamsWire = z.object({ + filter: listLlmCostPricesParamsFilterWire.optional(), + sort: sortQueryWire.optional(), + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), +}) + +export const listLlmCostPricesResponseWire = z.strictObject({ + data: z.array(llmCostPriceWire), + meta: paginatedMetaWire, +}) + +export const getLlmCostPricePathParamsWire = z.object({ + priceId: ulidWire, +}) + +export const getLlmCostPriceResponseWire = llmCostPriceWire + +export const listLlmCostOverridesQueryParamsWire = z.object({ + filter: listLlmCostPricesParamsFilterWire.optional(), + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), +}) + +export const listLlmCostOverridesResponseWire = z.strictObject({ + data: z.array(llmCostPriceWire), + meta: paginatedMetaWire, +}) + +export const createLlmCostOverrideBodyWire = llmCostOverrideCreateWire + +export const createLlmCostOverrideResponseWire = llmCostPriceWire + +export const deleteLlmCostOverridePathParamsWire = z.object({ + priceId: ulidWire, +}) + +export const listPlansQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listPlansParamsFilterWire.optional(), +}) + +export const listPlansResponseWire = z.strictObject({ + data: z.array(planWire), + meta: paginatedMetaWire, +}) + +export const createPlanBodyWire = createPlanRequestWire + +export const createPlanResponseWire = planWire + +export const updatePlanPathParamsWire = z.object({ + planId: ulidWire, +}) + +export const updatePlanBodyWire = upsertPlanRequestWire + +export const updatePlanResponseWire = planWire + +export const getPlanPathParamsWire = z.object({ + planId: ulidWire, +}) + +export const getPlanResponseWire = planWire + +export const deletePlanPathParamsWire = z.object({ + planId: ulidWire, +}) + +export const archivePlanPathParamsWire = z.object({ + planId: ulidWire, +}) + +export const archivePlanResponseWire = planWire + +export const publishPlanPathParamsWire = z.object({ + planId: ulidWire, +}) + +export const publishPlanResponseWire = planWire + +export const listAddonsQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), + sort: sortQueryWire.optional(), + filter: listAddonsParamsFilterWire.optional(), +}) + +export const listAddonsResponseWire = z.strictObject({ + data: z.array(addonWire), + meta: paginatedMetaWire, +}) + +export const createAddonBodyWire = createAddonRequestWire + +export const createAddonResponseWire = addonWire + +export const updateAddonPathParamsWire = z.object({ + addonId: ulidWire, +}) + +export const updateAddonBodyWire = upsertAddonRequestWire + +export const updateAddonResponseWire = addonWire + +export const getAddonPathParamsWire = z.object({ + addonId: ulidWire, +}) + +export const getAddonResponseWire = addonWire + +export const deleteAddonPathParamsWire = z.object({ + addonId: ulidWire, +}) + +export const archiveAddonPathParamsWire = z.object({ + addonId: ulidWire, +}) + +export const archiveAddonResponseWire = addonWire + +export const publishAddonPathParamsWire = z.object({ + addonId: ulidWire, +}) + +export const publishAddonResponseWire = addonWire + +export const listPlanAddonsPathParamsWire = z.object({ + planId: ulidWire, +}) + +export const listPlanAddonsQueryParamsWire = z.object({ + page: z + .strictObject({ + size: z.coerce + .number() + .int() + .optional() + .describe('The number of items to include per page.'), + number: z.coerce.number().int().optional().describe('The page number.'), + }) + .optional() + .describe('Determines which page of the collection to retrieve.'), +}) + +export const listPlanAddonsResponseWire = z.strictObject({ + data: z.array(planAddonWire), + meta: paginatedMetaWire, +}) + +export const createPlanAddonPathParamsWire = z.object({ + planId: ulidWire, +}) + +export const createPlanAddonBodyWire = createPlanAddonRequestWire + +export const createPlanAddonResponseWire = planAddonWire + +export const getPlanAddonPathParamsWire = z.object({ + planId: ulidWire, + planAddonId: ulidWire, +}) + +export const getPlanAddonResponseWire = planAddonWire + +export const updatePlanAddonPathParamsWire = z.object({ + planId: ulidWire, + planAddonId: ulidWire, +}) + +export const updatePlanAddonBodyWire = upsertPlanAddonRequestWire + +export const updatePlanAddonResponseWire = planAddonWire + +export const deletePlanAddonPathParamsWire = z.object({ + planId: ulidWire, + planAddonId: ulidWire, +}) + +export const getOrganizationDefaultTaxCodesResponseWire = + organizationDefaultTaxCodesWire + +export const updateOrganizationDefaultTaxCodesBodyWire = + updateOrganizationDefaultTaxCodesRequestWire + +export const updateOrganizationDefaultTaxCodesResponseWire = + organizationDefaultTaxCodesWire + +export const queryGovernanceAccessQueryParamsWire = z.object({ + page: cursorPaginationQueryPageWire.optional(), +}) + +export const queryGovernanceAccessBodyWire = governanceQueryRequestWire + +export const queryGovernanceAccessResponseWire = governanceQueryResponseWire diff --git a/api/spec/packages/aip-client-javascript/src/models/types.ts b/api/spec/packages/aip-client-javascript/src/models/types.ts index 28c28fef92..ae35f31b39 100644 --- a/api/spec/packages/aip-client-javascript/src/models/types.ts +++ b/api/spec/packages/aip-client-javascript/src/models/types.ts @@ -108,13 +108,13 @@ export interface QueryFilterString { /** Custom text displayed at various stages of the checkout flow. */ export interface AppStripeCheckoutSessionCustomTextParams { /** Text displayed after the payment confirmation button. */ - after_submit?: { message?: string } + afterSubmit?: { message?: string } /** Text displayed alongside shipping address collection. */ - shipping_address?: { message?: string } + shippingAddress?: { message?: string } /** Text displayed alongside the payment confirmation button. */ submit?: { message?: string } /** Text replacing the default terms of service agreement text. */ - terms_of_service_acceptance?: { message?: string } + termsOfServiceAcceptance?: { message?: string } } /** Request to create a Stripe Customer Portal Session. */ @@ -125,7 +125,7 @@ export interface AppStripeCreateCustomerPortalSessionOptions { * to use for this session, describing its functionality and features. If not * specified, the session uses the default configuration. */ - configuration_id?: string + configurationId?: string /** * The IETF * [language tag](https://docs.stripe.com/api/customer_portal/sessions/create#create_portal_session-locale) @@ -138,7 +138,7 @@ export interface AppStripeCreateCustomerPortalSessionOptions { * [URL to redirect](https://docs.stripe.com/api/customer_portal/sessions/create#create_portal_session-return_url) * the customer to after they have completed their requested actions. */ - return_url?: string + returnUrl?: string } /** @@ -212,7 +212,7 @@ export interface WorkflowCollectionAlignmentSubscription { */ export interface WorkflowPaymentChargeAutomaticallySettings { /** The collection method for the invoice. */ - collection_method: 'charge_automatically' + collectionMethod: 'charge_automatically' } /** @@ -221,26 +221,26 @@ export interface WorkflowPaymentChargeAutomaticallySettings { */ export interface WorkflowPaymentSendInvoiceSettings { /** The collection method for the invoice. */ - collection_method: 'send_invoice' + collectionMethod: 'send_invoice' /** * The period after which the invoice is due. With some payment solutions it's only * applicable for manual collection method. */ - due_after: string + dueAfter: string } /** External identifiers assigned to an invoice by third-party systems. */ export interface InvoiceExternalReferences { /** The ID assigned by the external invoicing app (e.g. Stripe invoice ID). */ - invoicing_id?: string + invoicingId?: string /** The ID assigned by the external payment app (e.g. Stripe payment intent ID). */ - payment_id?: string + paymentId?: string } /** Details about an available invoice action including the resulting state. */ export interface InvoiceAvailableActionDetails { /** The extended status the invoice will transition to after performing this action. */ - resulting_state: string + resultingState: string } /** @@ -252,15 +252,15 @@ export interface InvoiceAvailableActionDetails { */ export interface InvoiceWorkflowInvoicingSettings { /** Whether to automatically issue the invoice after the draft_period has passed. */ - auto_advance: boolean + autoAdvance: boolean /** The period for the invoice to be kept in draft status for manual reviews. */ - draft_period: string + draftPeriod: string } /** External identifiers for an invoice line item assigned by third-party systems. */ export interface InvoiceLineExternalReferences { /** The ID assigned by the external invoicing app. */ - invoicing_id?: string + invoicingId?: string } /** LLM Provider */ @@ -437,9 +437,9 @@ export interface UpdateMeterRequest { /** Stripe customer data. */ export interface AppCustomerDataStripe { /** The Stripe customer ID used. */ - customer_id?: string + customerId?: string /** The Stripe default payment method ID. */ - default_payment_method_id?: string + defaultPaymentMethodId?: string /** Labels for this Stripe integration on the customer. */ labels?: Labels } @@ -453,7 +453,7 @@ export interface AppCustomerDataExternalInvoicing { /** Filter options for listing cost bases. */ export interface ListCostBasesParamsFilter { /** Filter cost bases by fiat currency code. */ - fiat_code?: string + fiatCode?: string } /** Monetary amount in a specific currency. */ @@ -500,17 +500,17 @@ export interface Totals { /** The total value of the resource before taxes, discounts and commitments. */ amount: string /** The total tax amount applied to the resource. */ - taxes_total: string + taxesTotal: string /** The total tax amount already included in the resource amount. */ - taxes_inclusive_total: string + taxesInclusiveTotal: string /** The total tax amount added on top of the resource amount. */ - taxes_exclusive_total: string + taxesExclusiveTotal: string /** The total amount contributed by additional charges. */ - charges_total: string + chargesTotal: string /** The total amount deducted through discounts. */ - discounts_total: string + discountsTotal: string /** The total amount deducted through credits before taxes are applied. */ - credits_total: string + creditsTotal: string /** The final total value of the resource after taxes, discounts and commitments. */ total: string } @@ -521,9 +521,9 @@ export interface Totals { */ export interface SpendCommitments { /** The customer is committed to spend at least the amount. */ - minimum_amount?: string + minimumAmount?: string /** The customer is limited to spend at most the amount. */ - maximum_amount?: string + maximumAmount?: string } /** A credit allocation applied to an invoice line item. */ @@ -545,29 +545,29 @@ export interface FeatureManualUnitCost { /** Resolved per-token pricing from the LLM cost database. */ export interface FeatureLlmUnitCostPricing { /** Cost per input token in USD. */ - input_per_token: string + inputPerToken: string /** Cost per output token in USD. */ - output_per_token: string + outputPerToken: string /** Cost per cache read token in USD. */ - cache_read_per_token?: string + cacheReadPerToken?: string /** Cost per reasoning token in USD. */ - reasoning_per_token?: string + reasoningPerToken?: string /** Cost per cache write token in USD. */ - cache_write_per_token?: string + cacheWritePerToken?: string } /** Token pricing for an LLM model, denominated per token. */ export interface LlmCostModelPricing { /** Input price per token (USD). */ - input_per_token: string + inputPerToken: string /** Output price per token (USD). */ - output_per_token: string + outputPerToken: string /** Cache read price per token (USD). */ - cache_read_per_token?: string + cacheReadPerToken?: string /** Cache write price per token (USD). */ - cache_write_per_token?: string + cacheWritePerToken?: string /** Reasoning output price per token (USD). */ - reasoning_per_token?: string + reasoningPerToken?: string } /** @@ -646,7 +646,7 @@ export interface ListLlmCostPricesParamsFilter { exists?: boolean } /** Filter by model ID. e.g. ?filter[model_id][eq]=gpt-4 */ - model_id?: + modelId?: | string | { eq?: string @@ -661,7 +661,7 @@ export interface ListLlmCostPricesParamsFilter { exists?: boolean } /** Filter by model name. e.g. ?filter[model_name][contains]=gpt */ - model_name?: + modelName?: | string | { eq?: string @@ -882,14 +882,14 @@ export interface AppStripeCreateCustomerPortalSessionResult { */ id: string /** The ID of the stripe customer. */ - stripe_customer_id: string + stripeCustomerId: string /** * Configuration used to customize the customer portal. * * See: * https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-configuration */ - configuration_id: string + configurationId: string /** * Livemode. * @@ -903,14 +903,14 @@ export interface AppStripeCreateCustomerPortalSessionResult { * See: * https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-created */ - created_at: string + createdAt: string /** * Return URL. * * See: * https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-return_url */ - return_url: string + returnUrl: string /** * The IETF language tag of the locale customer portal is displayed in. * @@ -948,9 +948,9 @@ export interface ClosedPeriod { /** A subscription add-on event. */ export interface SubscriptionAddonTimelineSegment { /** An ISO-8601 timestamp representation of the cadence start of the resource. */ - active_from: string + activeFrom: string /** An ISO-8601 timestamp representation of the cadence end of the resource. */ - active_to?: string + activeTo?: string /** The quantity of the add-on for the given period. */ quantity: number } @@ -959,7 +959,7 @@ export interface SubscriptionAddonTimelineSegment { export interface CostBasis { id: string /** The fiat currency code for the cost basis. */ - fiat_code: string + fiatCode: string /** The cost rate for the currency. */ rate: string /** @@ -967,15 +967,15 @@ export interface CostBasis { * effective. If not provided, it will be effective immediately and will be set to * `now` by the system. */ - effective_from?: string + effectiveFrom?: string /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string } /** CostBasis create request. */ export interface CreateCostBasisRequest { /** The fiat currency code for the cost basis. */ - fiat_code: string + fiatCode: string /** The cost rate for the currency. */ rate: string /** @@ -983,7 +983,7 @@ export interface CreateCostBasisRequest { * effective. If not provided, it will be effective immediately and will be set to * `now` by the system. */ - effective_from?: string + effectiveFrom?: string } /** A row in the result of a feature cost query. */ @@ -1030,11 +1030,11 @@ export interface Resource { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string } /** Represents common fields of immutable resources. */ @@ -1054,7 +1054,7 @@ export interface ResourceImmutable { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string } /** @@ -1249,13 +1249,13 @@ export interface UpsertPlanAddonRequest { description?: string labels?: Labels /** The key of the plan phase from which the add-on becomes available for purchase. */ - from_plan_phase: string + fromPlanPhase: string /** * The maximum number of times the add-on can be purchased for the plan. For * single-instance add-ons this field must be omitted. For multi-instance add-ons * when omitted, unlimited quantity can be purchased. */ - max_quantity?: number + maxQuantity?: number } /** Represents common fields of resources with a key. */ @@ -1275,11 +1275,11 @@ export interface ResourceWithKey { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string key: string } @@ -1309,12 +1309,12 @@ export interface CreateMeterRequest { | 'max' | 'latest' /** The event type to include in the aggregation. */ - event_type: string + eventType: string /** * The date since the meter should include events. Useful to skip old events. If * not specified, all historical events are included. */ - events_from?: string + eventsFrom?: string /** * JSONPath expression to extract the value from the ingested event's data * property. @@ -1325,7 +1325,7 @@ export interface CreateMeterRequest { * For unique_count aggregation, the ingested value must be a string. For count * aggregation the value_property is ignored. */ - value_property?: string + valueProperty?: string /** * Named JSONPath expressions to extract the group by values from the event data. * @@ -1351,11 +1351,11 @@ export interface Meter { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string key: string /** The aggregation type to use for the meter. */ aggregation: @@ -1367,12 +1367,12 @@ export interface Meter { | 'max' | 'latest' /** The event type to include in the aggregation. */ - event_type: string + eventType: string /** * The date since the meter should include events. Useful to skip old events. If * not specified, all historical events are included. */ - events_from?: string + eventsFrom?: string /** * JSONPath expression to extract the value from the ingested event's data * property. @@ -1383,7 +1383,7 @@ export interface Meter { * For unique_count aggregation, the ingested value must be a string. For count * aggregation the value_property is ignored. */ - value_property?: string + valueProperty?: string /** * Named JSONPath expressions to extract the group by values from the event data. * @@ -1438,7 +1438,7 @@ export interface CustomerUsageAttribution { * The subjects that are attributed to the customer. Can be empty when no usage * event subjects are associated with the customer. */ - subject_keys: string[] + subjectKeys: string[] } /** Address */ @@ -1449,7 +1449,7 @@ export interface Address { */ country?: string /** Postal code. */ - postal_code?: string + postalCode?: string /** State or province. */ state?: string /** City. */ @@ -1459,7 +1459,7 @@ export interface Address { /** Second line of the address. */ line2?: string /** Phone number. */ - phone_number?: string + phoneNumber?: string } /** Controls which customer fields can be updated by the checkout session. */ @@ -1514,34 +1514,34 @@ export interface AppStripeCreateCheckoutSessionTaxIdCollection { */ export interface AppStripeCreateCheckoutSessionResult { /** The customer ID in the billing system. */ - customer_id: string + customerId: string /** The Stripe customer ID. */ - stripe_customer_id: string + stripeCustomerId: string /** The Stripe checkout session ID. */ - session_id: string + sessionId: string /** The setup intent ID created for collecting the payment method. */ - setup_intent_id: string + setupIntentId: string /** * Client secret for initializing Stripe.js on the client side. * * Required for embedded checkout sessions. See: * https://docs.stripe.com/payments/checkout/custom-success-page */ - client_secret?: string + clientSecret?: string /** * The client reference ID provided in the request. * * Useful for reconciling the session with your internal systems. */ - client_reference_id?: string + clientReferenceId?: string /** Customer's email address if provided to Stripe. */ - customer_email?: string + customerEmail?: string /** Currency code for the checkout session. */ currency?: string /** Timestamp when the checkout session was created. */ - created_at: string + createdAt: string /** Timestamp when the checkout session will expire. */ - expires_at?: string + expiresAt?: string /** Metadata attached to the checkout session. */ metadata?: Record /** @@ -1560,11 +1560,11 @@ export interface AppStripeCreateCheckoutSessionResult { */ mode: 'setup' /** The cancel URL where customers are redirected if they cancel. */ - cancel_url?: string + cancelUrl?: string /** The success URL where customers are redirected after completion. */ - success_url?: string + successUrl?: string /** The return URL for embedded sessions after authentication. */ - return_url?: string + returnUrl?: string } /** @@ -1577,7 +1577,7 @@ export interface AppStripeCreateCheckoutSessionResult { */ export interface CustomerStripeCreateCustomerPortalSessionRequest { /** Options for configuring the Stripe Customer Portal Session. */ - stripe_options: AppStripeCreateCustomerPortalSessionOptions + stripeOptions: AppStripeCreateCustomerPortalSessionOptions } /** Entitlement access result. */ @@ -1585,12 +1585,12 @@ export interface EntitlementAccessResult { /** The type of the entitlement. */ type: 'metered' | 'static' | 'boolean' /** The feature key of the entitlement. */ - feature_key: string + featureKey: string /** * Whether the customer has access to the feature. Always true for `boolean` and * `static` entitlements. Depends on balance for `metered` entitlements. */ - has_access: boolean + hasAccess: boolean /** * Only available for static entitlements. Config is the JSON parsable * configuration of the entitlement. Useful to describe per customer configuration. @@ -1611,13 +1611,13 @@ export interface CreateCreditGrantPurchase { * * Defaults to 1.0. */ - per_unit_cost_basis: string + perUnitCostBasis: string /** * Controls when credits become available for consumption. * * Defaults to `on_creation`. */ - availability_policy: 'on_creation' + availabilityPolicy: 'on_creation' } /** The entitlement template of a metered entitlement. */ @@ -1628,7 +1628,7 @@ export interface RateCardMeteredEntitlement { * If soft limit is true, the subject can use the feature even if the entitlement * is exhausted; access remains granted. */ - is_soft_limit: boolean + isSoftLimit: boolean /** * The amount of usage granted each usage period, in the feature's unit. Usage is * counted against this allowance and the balance resets every usage period. When @@ -1640,7 +1640,7 @@ export interface RateCardMeteredEntitlement { * The reset interval of the metered entitlement in ISO8601 format. Defaults to the * billing cadence of the rate card. */ - usage_period?: string + usagePeriod?: string } /** Recurring period with an anchor and an interval. */ @@ -1664,7 +1664,7 @@ export interface CreditGrantPurchase { * * Defaults to 1.0. */ - per_unit_cost_basis: string + perUnitCostBasis: string /** The purchase amount. Calculated from `per_unit_cost_basis` and credit `amount`. */ amount: string /** @@ -1672,9 +1672,9 @@ export interface CreditGrantPurchase { * * Defaults to `on_creation`. */ - availability_policy: 'on_creation' + availabilityPolicy: 'on_creation' /** Current payment settlement status. */ - settlement_status?: 'pending' | 'authorized' | 'settled' + settlementStatus?: 'pending' | 'authorized' | 'settled' } /** @@ -1717,7 +1717,7 @@ export interface GetCreditBalanceParamsFilter { * Filter credit balance by feature key. Omit to return the total portfolio value. * Use `exists=false` to return only unrestricted balance. */ - feature_key?: + featureKey?: | string | { eq?: string @@ -1796,7 +1796,7 @@ export interface SubscriptionCreate { * invoiced. * - `credit_only`: Usage is settled exclusively against credits. */ - settlement_mode?: 'credit_then_invoice' | 'credit_only' + settlementMode?: 'credit_then_invoice' | 'credit_only' /** The customer to create the subscription for. */ customer: { id?: string; key?: string } /** The plan reference of the subscription. */ @@ -1813,7 +1813,7 @@ export interface SubscriptionCreate { * If not provided, the subscription will be created with the subscription's * creation time as the billing anchor. */ - billing_anchor?: string + billingAnchor?: string } /** The proration configuration of the rate card. */ @@ -1827,15 +1827,15 @@ export interface Subscription { id: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The customer ID of the subscription. */ - customer_id: string + customerId: string /** The plan ID of the subscription. Set if subscription is created from a plan. */ - plan_id?: string + planId?: string /** * A billing anchor is the fixed point in time that determines the subscription's * recurring billing cycle. It affects when charges occur and how prorations are @@ -1845,7 +1845,7 @@ export interface Subscription { * - Subscription anniversary (day customer signed up) * - Custom date (customer-specified day) */ - billing_anchor: string + billingAnchor: string /** The status of the subscription. */ status: 'active' | 'inactive' | 'canceled' | 'scheduled' /** @@ -1857,7 +1857,7 @@ export interface Subscription { * invoiced. * - `credit_only`: Usage is settled exclusively against credits. */ - settlement_mode?: 'credit_then_invoice' | 'credit_only' + settlementMode?: 'credit_then_invoice' | 'credit_only' } /** @@ -1895,7 +1895,7 @@ export interface UnitConfig { * * Must be a positive non-zero value. */ - conversion_factor: string + conversionFactor: string /** * The rounding mode applied to the converted quantity for invoicing. * @@ -1916,7 +1916,7 @@ export interface UnitConfig { * * Optional. When omitted, no unit label is rendered. */ - display_unit?: string + displayUnit?: string } /** @@ -1937,9 +1937,9 @@ export interface AppCatalogItem { /** Mapping of app types to tax codes. */ export interface TaxCodeAppMapping { /** The app type that the tax code is associated with. */ - app_type: 'sandbox' | 'stripe' | 'external_invoicing' + appType: 'sandbox' | 'stripe' | 'external_invoicing' /** Tax code. */ - tax_code: string + taxCode: string } /** @@ -1954,13 +1954,13 @@ export interface PartyTaxIdentity { /** Invoice settings for a billing workflow. */ export interface WorkflowInvoicingSettings { /** Whether to automatically issue the invoice after the draftPeriod has passed. */ - auto_advance: boolean + autoAdvance: boolean /** The period for the invoice to be kept in draft status for manual reviews. */ - draft_period: string + draftPeriod: string /** Should progressive billing be allowed for this workflow? */ - progressive_billing: boolean + progressiveBilling: boolean /** Controls how subscription-ending shortened service periods are billed. */ - subscription_end_proration_mode: 'bill_full_period' | 'bill_actual_period' + subscriptionEndProrationMode: 'bill_full_period' | 'bill_actual_period' } /** @@ -2003,7 +2003,7 @@ export interface InvoiceAvailableActions { /** Retry a failed workflow step. */ retry?: InvoiceAvailableActionDetails /** Snapshot the current usage quantities. */ - snapshot_quantities?: InvoiceAvailableActionDetails + snapshotQuantities?: InvoiceAvailableActionDetails } /** A monetary amount discount applied to an invoice line item. */ @@ -2015,7 +2015,7 @@ export interface InvoiceLineAmountDiscount { /** Optional human-readable description of the discount. */ description?: string /** External identifiers for this discount. */ - external_references?: InvoiceLineExternalReferences + externalReferences?: InvoiceLineExternalReferences /** The monetary amount deducted. */ amount: string } @@ -2029,7 +2029,7 @@ export interface InvoiceLineUsageDiscount { /** Optional human-readable description of the discount. */ description?: string /** External identifiers for this discount. */ - external_references?: InvoiceLineExternalReferences + externalReferences?: InvoiceLineExternalReferences /** The usage quantity deducted (in billing units). */ quantity: string } @@ -2043,7 +2043,7 @@ export interface InvoiceLineBaseDiscount { /** Optional human-readable description of the discount. */ description?: string /** External identifiers for this discount. */ - external_references?: InvoiceLineExternalReferences + externalReferences?: InvoiceLineExternalReferences } /** Filter options for listing currencies. */ @@ -2084,7 +2084,7 @@ export interface CurrencyCustom { symbol?: string code: string /** An ISO-8601 timestamp representation of the custom currency creation date. */ - created_at: string + createdAt: string } /** CurrencyCustom create request. */ @@ -2112,7 +2112,7 @@ export interface GovernanceQueryRequest { * * Defaults to `false`. */ - include_credits: boolean + includeCredits: boolean customer: GovernanceQueryRequestCustomers feature?: GovernanceQueryRequestFeatures } @@ -2149,7 +2149,7 @@ export interface AppCustomerData { /** Used if the customer has a linked Stripe app. */ stripe?: AppCustomerDataStripe /** Used if the customer has a linked external invoicing app. */ - external_invoicing?: AppCustomerDataExternalInvoicing + externalInvoicing?: AppCustomerDataExternalInvoicing } /** AppCustomerData upsert request. */ @@ -2157,7 +2157,7 @@ export interface UpsertAppCustomerDataRequest { /** Used if the customer has a linked Stripe app. */ stripe?: AppCustomerDataStripe /** Used if the customer has a linked external invoicing app. */ - external_invoicing?: AppCustomerDataExternalInvoicing + externalInvoicing?: AppCustomerDataExternalInvoicing } /** @@ -2230,7 +2230,7 @@ export interface ListCreditTransactionsParamsFilter { * transactions. Use `exists=false` to return only unrestricted credit * transactions. */ - feature_key?: + featureKey?: | string | { eq?: string @@ -2268,9 +2268,9 @@ export interface CreditTransaction { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** The date and time the transaction was booked. */ - booked_at: string + bookedAt: string /** The type of credit transaction. */ type: 'funded' | 'consumed' | 'expired' /** Currency of the balance affected by the transaction. */ @@ -2281,7 +2281,7 @@ export interface CreditTransaction { */ amount: string /** The available balance before and after the transaction. */ - available_balance: { before: string; after: string } + availableBalance: { before: string; after: string } } /** @@ -2296,11 +2296,11 @@ export interface PriceTier { * Up to and including this quantity will be contained in the tier. If undefined, * the tier is open-ended (the last tier). */ - up_to_amount?: string + upToAmount?: string /** The flat price component of the tier. Charged once when the tier is entered. */ - flat_price?: PriceFlat + flatPrice?: PriceFlat /** The unit price component of the tier. Charged per billing unit within the tier. */ - unit_price?: PriceUnit + unitPrice?: PriceUnit } /** @@ -2331,7 +2331,7 @@ export interface FeatureLlmUnitCost { * Meter group-by property that holds the LLM provider. Use this when the meter has * a group-by dimension for provider. Mutually exclusive with `provider`. */ - provider_property?: string + providerProperty?: string /** * Static LLM provider value (e.g., "openai", "anthropic"). Use this when the * feature tracks a single provider. Mutually exclusive with `provider_property`. @@ -2341,7 +2341,7 @@ export interface FeatureLlmUnitCost { * Meter group-by property that holds the model ID. Use this when the meter has a * group-by dimension for model. Mutually exclusive with `model`. */ - model_property?: string + modelProperty?: string /** * Static model ID value (e.g., "gpt-4", "claude-3-5-sonnet"). Use this when the * feature tracks a single model. Mutually exclusive with `model_property`. @@ -2351,13 +2351,13 @@ export interface FeatureLlmUnitCost { * Meter group-by property that holds the token type. Use this when the meter has a * group-by dimension for token type. Mutually exclusive with `token_type`. */ - token_type_property?: string + tokenTypeProperty?: string /** * Static token type value. Use this when the feature tracks a single token type * (e.g., only input tokens). `request` is an alias for `input`, `response` is an * alias for `output`. Mutually exclusive with `token_type_property`. */ - token_type?: + tokenType?: | 'input' | 'output' | 'cache_read' @@ -2391,13 +2391,13 @@ export interface LlmCostPrice { /** Where this price came from. */ source: 'manual' | 'system' /** When this price becomes effective. */ - effective_from: string + effectiveFrom: string /** When this price expires. Omitted when the price is currently effective. */ - effective_to?: string + effectiveTo?: string /** Creation timestamp. */ - created_at: string + createdAt: string /** Last update timestamp. */ - updated_at: string + updatedAt: string } /** @@ -2409,17 +2409,17 @@ export interface LlmCostOverrideCreate { /** Provider/vendor of the model. */ provider: string /** Canonical model identifier. */ - model_id: string + modelId: string /** Human-readable model name. */ - model_name?: string + modelName?: string /** Token pricing data. */ pricing: LlmCostModelPricing /** Currency code. */ currency: string /** When this override becomes effective. */ - effective_from: string + effectiveFrom: string /** When this override expires. */ - effective_to?: string + effectiveTo?: string } /** Filter options for listing customers. */ @@ -2452,7 +2452,7 @@ export interface ListCustomersParamsFilter { lte?: string exists?: boolean } - primary_email?: + primaryEmail?: | string | { eq?: string @@ -2466,7 +2466,7 @@ export interface ListCustomersParamsFilter { lte?: string exists?: boolean } - usage_attribution_subject_key?: + usageAttributionSubjectKey?: | string | { eq?: string @@ -2480,7 +2480,7 @@ export interface ListCustomersParamsFilter { lte?: string exists?: boolean } - plan_key?: + planKey?: | string | { eq?: string @@ -2494,21 +2494,21 @@ export interface ListCustomersParamsFilter { lte?: string exists?: boolean } - billing_profile_id?: string | { eq?: string; oeq?: string[]; neq?: string } + billingProfileId?: string | { eq?: string; oeq?: string[]; neq?: string } } /** Filter options for listing subscriptions. */ export interface ListSubscriptionsParamsFilter { id?: string | { eq?: string; oeq?: string[]; neq?: string } - customer_id?: string | { eq?: string; oeq?: string[]; neq?: string } + customerId?: string | { eq?: string; oeq?: string[]; neq?: string } status?: string | { eq?: string; oeq?: string[]; neq?: string } - plan_id?: string | { eq?: string; oeq?: string[]; neq?: string } - plan_key?: string | { eq?: string; oeq?: string[]; neq?: string } + planId?: string | { eq?: string; oeq?: string[]; neq?: string } + planKey?: string | { eq?: string; oeq?: string[]; neq?: string } } /** Filter options for listing features. */ export interface ListFeatureParamsFilter { - meter_id?: string | { eq?: string; oeq?: string[]; neq?: string } + meterId?: string | { eq?: string; oeq?: string[]; neq?: string } key?: | string | { @@ -2584,7 +2584,7 @@ export interface CreateCreditGrantTaxConfig { /** Tax behavior applied to the invoice line item. */ behavior?: 'inclusive' | 'exclusive' /** Tax code applied to the invoice line item. */ - tax_code?: CreateResourceReference + taxCode?: CreateResourceReference } /** @@ -2597,7 +2597,7 @@ export interface CreditGrantTaxConfig { /** Tax behavior applied to the invoice line item. */ behavior?: 'inclusive' | 'exclusive' /** Tax code applied to the invoice line item. */ - tax_code?: TaxCodeReference + taxCode?: TaxCodeReference } /** Set of provider specific tax configs. */ @@ -2612,9 +2612,9 @@ export interface TaxConfig { /** Stripe tax config. */ stripe?: TaxConfigStripe /** External invoicing tax config. */ - external_invoicing?: TaxConfigExternalInvoicing + externalInvoicing?: TaxConfigExternalInvoicing /** Tax code ID. */ - tax_code_id?: string + taxCodeId?: string /** * Tax code reference. * @@ -2622,7 +2622,7 @@ export interface TaxConfig { * precedence. When `stripe.code` is also provided, `tax_code` still wins and * `stripe.code` is ignored. */ - tax_code?: TaxCodeReference + taxCode?: TaxCodeReference } /** The tax config of the rate card. */ @@ -2639,21 +2639,21 @@ export interface RateCardTaxConfig { */ export interface OrganizationDefaultTaxCodes { /** Default tax code for invoicing. */ - invoicing_tax_code: TaxCodeReference + invoicingTaxCode: TaxCodeReference /** Default tax code for credit grants. */ - credit_grant_tax_code: TaxCodeReference + creditGrantTaxCode: TaxCodeReference /** Timestamp of creation. */ - created_at: string + createdAt: string /** Timestamp of last update. */ - updated_at: string + updatedAt: string } /** OrganizationDefaultTaxCodes update request. */ export interface UpdateOrganizationDefaultTaxCodesRequest { /** Default tax code for invoicing. */ - invoicing_tax_code?: TaxCodeReference + invoicingTaxCode?: TaxCodeReference /** Default tax code for credit grants. */ - credit_grant_tax_code?: TaxCodeReference + creditGrantTaxCode?: TaxCodeReference } /** @@ -2676,23 +2676,23 @@ export interface PlanAddon { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The add-on associated with the plan. */ addon: AddonReference /** The key of the plan phase from which the add-on becomes available for purchase. */ - from_plan_phase: string + fromPlanPhase: string /** * The maximum number of times the add-on can be purchased for the plan. For * single-instance add-ons this field must be omitted. For multi-instance add-ons * when omitted, unlimited quantity can be purchased. */ - max_quantity?: number + maxQuantity?: number /** List of validation errors. */ - validation_errors?: ProductCatalogValidationError[] + validationErrors?: ProductCatalogValidationError[] } /** PlanAddon create request. */ @@ -2713,13 +2713,13 @@ export interface CreatePlanAddonRequest { /** The add-on associated with the plan. */ addon: AddonReference /** The key of the plan phase from which the add-on becomes available for purchase. */ - from_plan_phase: string + fromPlanPhase: string /** * The maximum number of times the add-on can be purchased for the plan. For * single-instance add-ons this field must be omitted. For multi-instance add-ons * when omitted, unlimited quantity can be purchased. */ - max_quantity?: number + maxQuantity?: number } /** References to the applications used by a billing profile. */ @@ -2808,17 +2808,17 @@ export interface ListEventsParamsFilter { exists?: boolean } /** Filter events by the associated customer ID. */ - customer_id?: string | { eq?: string; oeq?: string[]; neq?: string } + customerId?: string | { eq?: string; oeq?: string[]; neq?: string } /** Filter events by event time. */ time?: | string | { eq?: string; lt?: string; lte?: string; gt?: string; gte?: string } /** Filter events by the time the event was ingested. */ - ingested_at?: + ingestedAt?: | string | { eq?: string; lt?: string; lte?: string; gt?: string; gte?: string } /** Filter events by the time the event was stored. */ - stored_at?: + storedAt?: | string | { eq?: string; lt?: string; lte?: string; gt?: string; gte?: string } } @@ -2840,14 +2840,14 @@ export interface ResourceFilters { exists?: boolean } labels?: LabelsFieldFilter - public_labels?: LabelsFieldFilter - created_at?: + publicLabels?: LabelsFieldFilter + createdAt?: | string | { eq?: string; lt?: string; lte?: string; gt?: string; gte?: string } - updated_at?: + updatedAt?: | string | { eq?: string; lt?: string; lte?: string; gt?: string; gte?: string } - deleted_at?: + deletedAt?: | string | { eq?: string; lt?: string; lte?: string; gt?: string; gte?: string } } @@ -2880,7 +2880,7 @@ export interface FieldFilters { lte?: string exists?: boolean } - string_exact?: string | { eq?: string; oeq?: string[]; neq?: string } + stringExact?: string | { eq?: string; oeq?: string[]; neq?: string } ulid?: string | { eq?: string; oeq?: string[]; neq?: string } datetime?: | string @@ -2895,11 +2895,11 @@ export interface IngestedEvent { /** The customer if the event is associated with a customer. */ customer?: CustomerReference /** The date and time the event was ingested and its processing started. */ - ingested_at: string + ingestedAt: string /** The date and time the event was stored in the database. */ - stored_at: string + storedAt: string /** The validation errors of the ingested event. */ - validation_errors?: IngestedEventValidationError[] + validationErrors?: IngestedEventValidationError[] } /** Meter query result. */ @@ -2968,13 +2968,13 @@ export interface CreateCustomerRequest { labels?: Labels key: string /** Mapping to attribute metered usage to the customer by the event subject. */ - usage_attribution?: CustomerUsageAttribution + usageAttribution?: CustomerUsageAttribution /** The primary email address of the customer. */ - primary_email?: string + primaryEmail?: string /** Currency of the customer. Used for billing, tax and invoicing. */ currency?: string /** The billing address of the customer. Used for tax and invoicing. */ - billing_address?: Address + billingAddress?: Address } /** @@ -2997,20 +2997,20 @@ export interface Customer { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string key: string /** Mapping to attribute metered usage to the customer by the event subject. */ - usage_attribution?: CustomerUsageAttribution + usageAttribution?: CustomerUsageAttribution /** The primary email address of the customer. */ - primary_email?: string + primaryEmail?: string /** Currency of the customer. Used for billing, tax and invoicing. */ currency?: string /** The billing address of the customer. Used for tax and invoicing. */ - billing_address?: Address + billingAddress?: Address } /** Customer upsert request. */ @@ -3029,19 +3029,19 @@ export interface UpsertCustomerRequest { description?: string labels?: Labels /** Mapping to attribute metered usage to the customer by the event subject. */ - usage_attribution?: CustomerUsageAttribution + usageAttribution?: CustomerUsageAttribution /** The primary email address of the customer. */ - primary_email?: string + primaryEmail?: string /** Currency of the customer. Used for billing, tax and invoicing. */ currency?: string /** The billing address of the customer. Used for tax and invoicing. */ - billing_address?: Address + billingAddress?: Address } /** A collection of addresses for the party. */ export interface PartyAddresses { /** Billing address. */ - billing_address: Address + billingAddress: Address } /** Snapshot of the customer's information at the time the invoice was issued. */ @@ -3054,9 +3054,9 @@ export interface InvoiceCustomer { */ name: string /** Mapping to attribute metered usage to the customer by the event subject. */ - usage_attribution?: CustomerUsageAttribution + usageAttribution?: CustomerUsageAttribution /** The billing address of the customer. Used for tax and invoicing. */ - billing_address?: Address + billingAddress?: Address /** * Optional external resource key for the customer. * @@ -3070,7 +3070,7 @@ export interface InvoiceCustomer { /** Checkout Session consent collection configuration. */ export interface AppStripeCreateCheckoutSessionConsentCollection { /** Controls the visibility of payment method reuse agreement. */ - payment_method_reuse_agreement?: AppStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreement + paymentMethodReuseAgreement?: AppStripeCreateCheckoutSessionConsentCollectionPaymentMethodReuseAgreement /** * Enables collection of promotional communication consent. * @@ -3083,7 +3083,7 @@ export interface AppStripeCreateCheckoutSessionConsentCollection { * * Requires a valid terms of service URL in your Stripe Dashboard settings. */ - terms_of_service?: 'none' | 'required' + termsOfService?: 'none' | 'required' } /** List customer entitlement access response data. */ @@ -3100,7 +3100,7 @@ export interface WorkflowCollectionAlignmentAnchored { /** The type of alignment. */ type: 'anchored' /** The recurring period for the alignment. */ - recurring_period: RecurringPeriod + recurringPeriod: RecurringPeriod } /** Page paginated response. */ @@ -3135,7 +3135,7 @@ export interface SubscriptionChange { * invoiced. * - `credit_only`: Usage is settled exclusively against credits. */ - settlement_mode?: 'credit_then_invoice' | 'credit_only' + settlementMode?: 'credit_then_invoice' | 'credit_only' /** The customer to create the subscription for. */ customer: { id?: string; key?: string } /** The plan reference of the subscription. */ @@ -3152,7 +3152,7 @@ export interface SubscriptionChange { * If not provided, the subscription will be created with the subscription's * creation time as the billing anchor. */ - billing_anchor?: string + billingAnchor?: string /** * Timing configuration for the change, when the change should take effect. For * changing a subscription, the accepted values depend on the subscription @@ -3182,21 +3182,21 @@ export interface CreateSubscriptionAddonRequest { */ export interface InvoiceUsageQuantityDetail { /** The raw quantity as reported by the meter (native units). */ - raw_quantity: string + rawQuantity: string /** * The precise decimal value after applying the conversion operation and factor, * before rounding. */ - converted_quantity: string + convertedQuantity: string /** The quantity after rounding, used for pricing. */ - invoiced_quantity: string + invoicedQuantity: string /** The display unit label (e.g., "GB", "hours", "M tokens"). */ - display_unit?: string + displayUnit?: string /** * Snapshot of the UnitConfig that was in effect at billing time. Ensures * historical invoices reflect the config that was actually applied. */ - applied_unit_config: UnitConfig + appliedUnitConfig: UnitConfig } /** Stripe app. */ @@ -3216,11 +3216,11 @@ export interface AppStripe { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The app type. */ type: 'stripe' /** The app catalog definition that this installed app is based on. */ @@ -3228,11 +3228,11 @@ export interface AppStripe { /** Status of the app connection. */ status: 'ready' | 'unauthorized' /** The Stripe account ID associated with the connected Stripe account. */ - account_id: string + accountId: string /** Indicates whether the app is connected to a live Stripe account. */ livemode: boolean /** The masked Stripe API key that only exposes the first and last few characters. */ - masked_api_key: string + maskedApiKey: string } /** Sandbox app can be used for testing billing features. */ @@ -3252,11 +3252,11 @@ export interface AppSandbox { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The app type. */ type: 'sandbox' /** The app catalog definition that this installed app is based on. */ @@ -3300,11 +3300,11 @@ export interface AppExternalInvoicing { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The app type. */ type: 'external_invoicing' /** The app catalog definition that this installed app is based on. */ @@ -3322,7 +3322,7 @@ export interface AppExternalInvoicing { * When disabled, invoices automatically progress through the draft state based on * the configured workflow timing. */ - enable_draft_sync_hook: boolean + enableDraftSyncHook: boolean /** * Enable issuing synchronization hook. * @@ -3334,7 +3334,7 @@ export interface AppExternalInvoicing { * When disabled, invoices automatically progress through the issuing state and are * immediately marked as issued. */ - enable_issuing_sync_hook: boolean + enableIssuingSyncHook: boolean } /** TaxCode create request. */ @@ -3354,7 +3354,7 @@ export interface CreateTaxCodeRequest { labels?: Labels key: string /** Mapping of app types to tax codes. */ - app_mappings: TaxCodeAppMapping[] + appMappings: TaxCodeAppMapping[] } /** Tax codes by provider. */ @@ -3374,14 +3374,14 @@ export interface TaxCode { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string key: string /** Mapping of app types to tax codes. */ - app_mappings: TaxCodeAppMapping[] + appMappings: TaxCodeAppMapping[] } /** TaxCode upsert request. */ @@ -3400,7 +3400,7 @@ export interface UpsertTaxCodeRequest { description?: string labels?: Labels /** Mapping of app types to tax codes. */ - app_mappings: TaxCodeAppMapping[] + appMappings: TaxCodeAppMapping[] } /** @@ -3429,9 +3429,9 @@ export interface InvoiceStatusDetails { * Fine-grained internal status string providing additional workflow detail beyond * the top-level status enum. */ - extended_status: string + extendedStatus: string /** The set of state-transition actions currently available for this invoice. */ - available_actions: InvoiceAvailableActions + availableActions: InvoiceAvailableActions } /** Discounts applied to an invoice line item. */ @@ -3452,7 +3452,7 @@ export interface GovernanceFeatureAccess { * the usage limit has been reached, or (when applicable) credits have been * exhausted. */ - has_access: boolean + hasAccess: boolean /** * Optional reason when the customer does not have access to the feature. Populated * when `has_access` is `false`. @@ -3467,9 +3467,9 @@ export interface CustomerData { * * If not provided, the default billing profile will be used. */ - billing_profile?: ProfileReference + billingProfile?: ProfileReference /** App customer data. */ - app_data?: AppCustomerData + appData?: AppCustomerData } /** CustomerBillingData upsert request. */ @@ -3479,15 +3479,15 @@ export interface UpsertCustomerBillingDataRequest { * * If not provided, the default billing profile will be used. */ - billing_profile?: ProfileReference + billingProfile?: ProfileReference /** App customer data. */ - app_data?: AppCustomerData + appData?: AppCustomerData } /** The balances of the credits of a customer. */ export interface CreditBalances { /** The timestamp of the balance retrieval. */ - retrieved_at: string + retrievedAt: string /** The balances by currencies. */ balances: CreditBalance[] } @@ -3552,7 +3552,7 @@ export interface CreateCreditGrantRequest { description?: string labels?: CreateLabels /** Funding method of the grant. */ - funding_method: 'none' | 'invoice' | 'external' + fundingMethod: 'none' | 'invoice' | 'external' /** The currency of the granted credits. */ currency: string /** Granted credit amount. */ @@ -3567,7 +3567,7 @@ export interface CreateCreditGrantRequest { * credit grant tax code is applied, if that's not set the global default taxcode * is used. */ - tax_config?: CreateCreditGrantTaxConfig + taxConfig?: CreateCreditGrantTaxConfig filters?: CreateCreditGrantFilters /** Draw-down priority of the grant. Lower values have higher priority. */ priority: number @@ -3576,13 +3576,13 @@ export interface CreateCreditGrantRequest { * * Defaults to the current date and time. */ - effective_at?: string + effectiveAt?: string /** * The duration after which the credit grant expires. * * Defaults to never expiring. */ - expires_after?: string + expiresAfter?: string /** * Idempotency key for the credit grant creation request. * @@ -3614,13 +3614,13 @@ export interface CreditGrant { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** Funding method of the grant. */ - funding_method: 'none' | 'invoice' | 'external' + fundingMethod: 'none' | 'invoice' | 'external' /** The currency of the granted credits. */ currency: string /** Granted credit amount. */ @@ -3635,7 +3635,7 @@ export interface CreditGrant { * credit grant tax code is applied, if that's not set the global default taxcode * is used. */ - tax_config?: CreditGrantTaxConfig + taxConfig?: CreditGrantTaxConfig /** Available when `funding_method` is `invoice`. */ invoice?: CreditGrantInvoiceReference filters?: CreditGrantFilters @@ -3646,7 +3646,7 @@ export interface CreditGrant { * * Defaults to the current date and time. */ - effective_at?: string + effectiveAt?: string /** * Idempotency key for the credit grant creation request. * @@ -3659,9 +3659,9 @@ export interface CreditGrant { * * Calculated from the grant effective time and `expires_after` if provided. */ - expires_at?: string + expiresAt?: string /** Timestamp when the grant was voided. */ - voided_at?: string + voidedAt?: string /** Current lifecycle status of the grant. */ status: 'pending' | 'active' | 'expired' | 'voided' } @@ -3686,29 +3686,29 @@ export interface CreateChargeFlatFeeRequest { /** The currency of the charge. */ currency: string /** The timestamp when the charge is intended to be invoiced. */ - invoice_at: string + invoiceAt: string /** The effective service period covered by the charge. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** Unique reference ID of the charge. */ - unique_reference_id?: string + uniqueReferenceId?: string /** Settlement mode of the charge. */ - settlement_mode: 'credit_then_invoice' | 'credit_only' + settlementMode: 'credit_then_invoice' | 'credit_only' /** Tax configuration of the charge. */ - tax_config?: TaxConfig + taxConfig?: TaxConfig /** Payment term of the flat fee charge. */ - payment_term: 'in_advance' | 'in_arrears' + paymentTerm: 'in_advance' | 'in_arrears' /** The discounts applied to the charge. */ discounts?: ChargeFlatFeeDiscounts /** The feature associated with the charge, when applicable. */ - feature_key?: string + featureKey?: string /** The proration configuration of the charge. */ - proration_configuration: RateCardProrationConfiguration + prorationConfiguration: RateCardProrationConfiguration /** The amount before proration of the charge. */ - amount_before_proration: CurrencyAmount + amountBeforeProration: CurrencyAmount /** The full, unprorated service period of the charge. */ - full_service_period?: ClosedPeriod + fullServicePeriod?: ClosedPeriod /** The billing period the charge belongs to. */ - billing_period?: ClosedPeriod + billingPeriod?: ClosedPeriod } /** Tax settings for a billing workflow. */ @@ -3733,7 +3733,7 @@ export interface WorkflowTaxSettings { * default tax code is used instead. Existing tax-code values may still be removed, * and `behavior` remains fully supported. */ - default_tax_config?: TaxConfig + defaultTaxConfig?: TaxConfig } /** Page paginated response. */ @@ -3773,9 +3773,9 @@ export interface MeterQueryRequest { * (http://www.iana.org/time-zones). The time zone is used to determine the start * and end of the time buckets. If not specified, the UTC timezone will be used. */ - time_zone: string + timeZone: string /** The dimensions to group the results by. */ - group_by_dimensions?: string[] + groupByDimensions?: string[] /** Filters to apply to the query. */ filters?: MeterQueryFilters } @@ -3798,7 +3798,7 @@ export interface Party { * The entity's legal identification used for tax purposes. They may have other * numbers, but we're only interested in those valid for tax purposes. */ - tax_id?: PartyTaxIdentity + taxId?: PartyTaxIdentity /** Address for where information should be sent if needed. */ addresses?: PartyAddresses } @@ -3819,7 +3819,7 @@ export interface Supplier { * The entity's legal identification used for tax purposes. They may have other * numbers, but we're only interested in those valid for tax purposes. */ - tax_id?: PartyTaxIdentity + taxId?: PartyTaxIdentity /** Address for where information should be sent if needed. */ addresses?: PartyAddresses } @@ -3837,23 +3837,23 @@ export interface AppStripeCreateCheckoutSessionRequestOptions { * Defaults to auto, which only collects the address when necessary for tax * calculation. */ - billing_address_collection: 'auto' | 'required' + billingAddressCollection: 'auto' | 'required' /** * URL to redirect customers who cancel the checkout session. * * Not allowed when ui_mode is "embedded". */ - cancel_url?: string + cancelUrl?: string /** * Unique reference string for reconciling sessions with internal systems. * * Can be a customer ID, cart ID, or any other identifier. */ - client_reference_id?: string + clientReferenceId?: string /** Controls which customer fields can be updated by the checkout session. */ - customer_update?: AppStripeCreateCheckoutSessionCustomerUpdate + customerUpdate?: AppStripeCreateCheckoutSessionCustomerUpdate /** Configuration for collecting customer consent during checkout. */ - consent_collection?: AppStripeCreateCheckoutSessionConsentCollection + consentCollection?: AppStripeCreateCheckoutSessionConsentCollection /** * Three-letter ISO 4217 currency code in uppercase. * @@ -3861,13 +3861,13 @@ export interface AppStripeCreateCheckoutSessionRequestOptions { */ currency?: string /** Custom text to display during checkout at various stages. */ - custom_text?: AppStripeCheckoutSessionCustomTextParams + customText?: AppStripeCheckoutSessionCustomTextParams /** * Unix timestamp when the checkout session expires. * * Can be 30 minutes to 24 hours from creation. Defaults to 24 hours. */ - expires_at?: bigint + expiresAt?: bigint /** * IETF language tag for the checkout UI locale. * @@ -3886,36 +3886,36 @@ export interface AppStripeCreateCheckoutSessionRequestOptions { * Required if ui_mode is "embedded" and redirect-based payment methods are * enabled. */ - return_url?: string + returnUrl?: string /** * Success URL to redirect customers after completing payment or setup. * * Not allowed when ui_mode is "embedded". See: * https://docs.stripe.com/payments/checkout/custom-success-page */ - success_url?: string + successUrl?: string /** * The UI mode for the checkout session. * * "hosted" displays a Stripe-hosted page. "embedded" integrates directly into your * app. Defaults to "hosted". */ - ui_mode: 'embedded' | 'hosted' + uiMode: 'embedded' | 'hosted' /** * List of payment method types to enable (e.g., "card", "us_bank_account"). * * If not specified, Stripe enables all relevant payment methods. */ - payment_method_types?: string[] + paymentMethodTypes?: string[] /** * Redirect behavior for embedded checkout sessions. * * Controls when to redirect users after completion. See: * https://docs.stripe.com/payments/checkout/custom-success-page?payment-ui=embedded-form */ - redirect_on_completion?: 'always' | 'if_required' | 'never' + redirectOnCompletion?: 'always' | 'if_required' | 'never' /** Configuration for collecting tax IDs during checkout. */ - tax_id_collection?: AppStripeCreateCheckoutSessionTaxIdCollection + taxIdCollection?: AppStripeCreateCheckoutSessionTaxIdCollection } /** Page paginated response. */ @@ -3929,7 +3929,7 @@ export interface InvoiceWorkflowSettings { /** The apps that will be used to orchestrate the invoice's workflow. */ apps?: InvoiceWorkflowAppsReferences /** The billing profile that was the source of this workflow snapshot. */ - source_billing_profile: ProfileReference + sourceBillingProfile: ProfileReference /** * The workflow configuration that was active when the invoice was created. * @@ -3963,13 +3963,13 @@ export interface InvoiceDetailedLine { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The service period covered by this detailed line. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** Aggregated financial totals for the detailed line. */ totals: Totals /** The cost category of this detailed line. */ @@ -3977,13 +3977,13 @@ export interface InvoiceDetailedLine { /** Discounts applied to this detailed line. */ discounts?: InvoiceLineDiscounts /** Credit applied to this detailed line. */ - credits_applied?: InvoiceLineCreditsApplied[] + creditsApplied?: InvoiceLineCreditsApplied[] /** External identifiers for this detailed line. */ - external_references?: InvoiceLineExternalReferences + externalReferences?: InvoiceLineExternalReferences /** The quantity of the detailed line. */ quantity: string /** The unit price of the detailed line. */ - unit_price: string + unitPrice: string } /** Page paginated response. */ @@ -4016,7 +4016,7 @@ export interface GovernanceQueryResult { * Timestamp of the most recent change to the customer's access state reflected in * this result. */ - updated_at: string + updatedAt: string } /** A capability or billable dimension offered by a provider. */ @@ -4036,11 +4036,11 @@ export interface Feature { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string key: string /** * The meter that the feature is associated with and based on which usage is @@ -4052,7 +4052,7 @@ export interface Feature { * "llm" to look up cost from the LLM cost database based on meter group-by * properties. */ - unit_cost?: FeatureManualUnitCost | FeatureLlmUnitCost + unitCost?: FeatureManualUnitCost | FeatureLlmUnitCost } /** Feature create request. */ @@ -4081,7 +4081,7 @@ export interface CreateFeatureRequest { * "llm" to look up cost from the LLM cost database based on meter group-by * properties. */ - unit_cost?: FeatureManualUnitCost | FeatureLlmUnitCost + unitCost?: FeatureManualUnitCost | FeatureLlmUnitCost } /** @@ -4095,7 +4095,7 @@ export interface UpdateFeatureRequest { * properties. Set to `null` to clear the existing unit cost; omit to leave it * unchanged. */ - unit_cost?: FeatureManualUnitCost | FeatureLlmUnitCost | null + unitCost?: FeatureManualUnitCost | FeatureLlmUnitCost | null } /** Page paginated response. */ @@ -4107,7 +4107,7 @@ export interface CreditGrantPagePaginatedResponse { /** Bad Request. */ export interface BadRequest extends BaseError { /** The list of parameters that failed validation. */ - invalid_parameters: ( + invalidParameters: ( | InvalidParameterStandard | InvalidParameterMinimumLength | InvalidParameterMaximumLength @@ -4131,11 +4131,11 @@ export interface InvoiceBase { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** Human-readable invoice number generated by the invoicing app. */ number: string /** Three-letter ISO 4217 currency code for the invoice. */ @@ -4156,16 +4156,16 @@ export interface InvoiceBase { * to `to`. In other cases those fields will be filled with the actual service * period. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** * Validation issues found during invoice processing. * * Present only when there are one or more validation findings. An empty list is * omitted. */ - validation_issues?: InvoiceValidationIssue[] + validationIssues?: InvoiceValidationIssue[] /** External identifiers assigned to this invoice by third-party systems. */ - external_references?: InvoiceExternalReferences + externalReferences?: InvoiceExternalReferences } /** @@ -4182,7 +4182,7 @@ export interface CustomerStripeCreateCheckoutSessionRequest { * These options are passed directly to Stripe's * [checkout session creation API](https://docs.stripe.com/api/checkout/sessions/create). */ - stripe_options: AppStripeCreateCheckoutSessionRequestOptions + stripeOptions: AppStripeCreateCheckoutSessionRequestOptions } /** @@ -4250,11 +4250,11 @@ export interface ChargeFlatFee { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The type of the charge. */ type: 'flat_fee' /** The customer owning the charge. */ @@ -4263,7 +4263,7 @@ export interface ChargeFlatFee { * Indicates whether the charge lifecycle is controlled by OpenMeter or manually * overridden by the API user. */ - lifecycle_controller: 'system' | 'manual' + lifecycleController: 'system' | 'manual' /** * The subscription that originated the charge, when the charge was created from a * subscription item. @@ -4274,34 +4274,34 @@ export interface ChargeFlatFee { /** The lifecycle status of the charge. */ status: 'created' | 'active' | 'final' | 'deleted' /** The timestamp when the charge is intended to be invoiced. */ - invoice_at: string + invoiceAt: string /** The effective service period covered by the charge. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** The full, unprorated service period of the charge. */ - full_service_period: ClosedPeriod + fullServicePeriod: ClosedPeriod /** The billing period the charge belongs to. */ - billing_period: ClosedPeriod + billingPeriod: ClosedPeriod /** * The earliest time when the charge should be advanced again by background * processing. */ - advance_after?: string + advanceAfter?: string /** Unique reference ID of the charge. */ - unique_reference_id?: string + uniqueReferenceId?: string /** Settlement mode of the charge. */ - settlement_mode: 'credit_then_invoice' | 'credit_only' + settlementMode: 'credit_then_invoice' | 'credit_only' /** Tax configuration of the charge. */ - tax_config?: TaxConfig + taxConfig?: TaxConfig /** Payment term of the flat fee charge. */ - payment_term: 'in_advance' | 'in_arrears' + paymentTerm: 'in_advance' | 'in_arrears' /** The discounts applied to the charge. */ discounts?: ChargeFlatFeeDiscounts /** The feature associated with the charge, when applicable. */ - feature_key?: string + featureKey?: string /** The proration configuration of the charge. */ - proration_configuration: RateCardProrationConfiguration + prorationConfiguration: RateCardProrationConfiguration /** The amount after proration of the charge. */ - amount_after_proration: CurrencyAmount + amountAfterProration: CurrencyAmount /** The price of the charge. */ price: PriceFree | PriceFlat | PriceUnit | PriceGraduated | PriceVolume } @@ -4323,11 +4323,11 @@ export interface ChargeUsageBased { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The type of the charge. */ type: 'usage_based' /** The customer owning the charge. */ @@ -4336,7 +4336,7 @@ export interface ChargeUsageBased { * Indicates whether the charge lifecycle is controlled by OpenMeter or manually * overridden by the API user. */ - lifecycle_controller: 'system' | 'manual' + lifecycleController: 'system' | 'manual' /** * The subscription that originated the charge, when the charge was created from a * subscription item. @@ -4347,28 +4347,28 @@ export interface ChargeUsageBased { /** The lifecycle status of the charge. */ status: 'created' | 'active' | 'final' | 'deleted' /** The timestamp when the charge is intended to be invoiced. */ - invoice_at: string + invoiceAt: string /** The effective service period covered by the charge. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** The full, unprorated service period of the charge. */ - full_service_period: ClosedPeriod + fullServicePeriod: ClosedPeriod /** The billing period the charge belongs to. */ - billing_period: ClosedPeriod + billingPeriod: ClosedPeriod /** * The earliest time when the charge should be advanced again by background * processing. */ - advance_after?: string + advanceAfter?: string /** Unique reference ID of the charge. */ - unique_reference_id?: string + uniqueReferenceId?: string /** Settlement mode of the charge. */ - settlement_mode: 'credit_then_invoice' | 'credit_only' + settlementMode: 'credit_then_invoice' | 'credit_only' /** Tax configuration of the charge. */ - tax_config?: TaxConfig + taxConfig?: TaxConfig /** Discounts applied to the usage-based charge. */ discounts?: RateCardDiscounts /** The feature associated with the charge. */ - feature_key: string + featureKey: string /** Aggregated booked and realtime totals for the charge. */ totals: ChargeTotals /** The price of the charge. */ @@ -4395,25 +4395,25 @@ export interface CreateChargeUsageBasedRequest { /** The currency of the charge. */ currency: string /** The timestamp when the charge is intended to be invoiced. */ - invoice_at: string + invoiceAt: string /** The effective service period covered by the charge. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** Unique reference ID of the charge. */ - unique_reference_id?: string + uniqueReferenceId?: string /** Settlement mode of the charge. */ - settlement_mode: 'credit_then_invoice' | 'credit_only' + settlementMode: 'credit_then_invoice' | 'credit_only' /** Tax configuration of the charge. */ - tax_config?: TaxConfig + taxConfig?: TaxConfig /** Discounts applied to the usage-based charge. */ discounts?: RateCardDiscounts /** The feature associated with the charge. */ - feature_key: string + featureKey: string /** The price of the charge. */ price: PriceFree | PriceFlat | PriceUnit | PriceGraduated | PriceVolume /** The full, unprorated service period of the charge. */ - full_service_period?: ClosedPeriod + fullServicePeriod?: ClosedPeriod /** The billing period the charge belongs to. */ - billing_period?: ClosedPeriod + billingPeriod?: ClosedPeriod } /** A rate card defines the pricing and entitlement of a feature or service. */ @@ -4438,7 +4438,7 @@ export interface RateCard { * The billing cadence of the rate card. When null, the charge is one-time * (non-recurring). Only valid for flat prices. */ - billing_cadence?: string + billingCadence?: string /** The price of the rate card. */ price: PriceFree | PriceFlat | PriceUnit | PriceGraduated | PriceVolume /** @@ -4451,12 +4451,12 @@ export interface RateCard { * Accepted on create and update only when the UnitConfig feature is enabled on the * deployment; otherwise rejected. */ - unit_config?: UnitConfig + unitConfig?: UnitConfig /** * The payment term of the rate card. In advance payment term can only be used for * flat prices. */ - payment_term: 'in_advance' | 'in_arrears' + paymentTerm: 'in_advance' | 'in_arrears' /** * Spend commitments for this rate card. Only applicable to usage-based prices * (unit, graduated, volume). @@ -4465,7 +4465,7 @@ export interface RateCard { /** The discounts of the rate card. */ discounts?: RateCardDiscounts /** The tax config of the rate card. */ - tax_config?: RateCardTaxConfig + taxConfig?: RateCardTaxConfig /** * The entitlement template granted to subscribers of a plan or addon containing * this rate card. Requires `feature` to be set. @@ -4481,9 +4481,9 @@ export interface InvoiceLineRateCard { /** The price definition used to calculate charges for this line. */ price: PriceFree | PriceFlat | PriceUnit | PriceGraduated | PriceVolume /** Tax configuration snapshot for this line. */ - tax_config?: RateCardTaxConfig + taxConfig?: RateCardTaxConfig /** The feature key associated with this line's rate card. */ - feature_key?: string + featureKey?: string /** Discount configuration from the rate card. */ discounts?: RateCardDiscounts } @@ -4511,9 +4511,9 @@ export interface Workflow { /** A rate card for a subscription add-on. */ export interface SubscriptionAddonRateCard { /** The rate card. */ - rate_card: RateCard + rateCard: RateCard /** The IDs of the subscription items that this rate card belongs to. */ - affected_subscription_item_ids: string[] + affectedSubscriptionItemIds: string[] } /** @@ -4541,7 +4541,7 @@ export interface PlanPhase { */ duration?: string /** The rate cards of the plan. */ - rate_cards: RateCard[] + rateCards: RateCard[] } /** @@ -4564,11 +4564,11 @@ export interface Addon { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * A key is a semi-unique string that is used to identify the add-on. It is used to * reference the latest `active` version of the add-on and is unique with the @@ -4578,19 +4578,19 @@ export interface Addon { /** Version of the add-on. Incremented when the add-on is updated. */ version: number /** The InstanceType of the add-ons. Can be "single" or "multiple". */ - instance_type: 'single' | 'multiple' + instanceType: 'single' | 'multiple' /** The currency code of the add-on. */ currency: string /** * The date and time when the add-on becomes effective. When not specified, the * add-on is a draft. */ - effective_from?: string + effectiveFrom?: string /** * The date and time when the add-on is no longer effective. When not specified, * the add-on is effective indefinitely. */ - effective_to?: string + effectiveTo?: string /** * The status of the add-on. Computed based on the effective start and end dates: * @@ -4601,9 +4601,9 @@ export interface Addon { */ status: 'draft' | 'active' | 'archived' /** The rate cards of the add-on. */ - rate_cards: RateCard[] + rateCards: RateCard[] /** List of validation errors. */ - validation_errors?: ProductCatalogValidationError[] + validationErrors?: ProductCatalogValidationError[] } /** Addon create request. */ @@ -4628,11 +4628,11 @@ export interface CreateAddonRequest { */ key: string /** The InstanceType of the add-ons. Can be "single" or "multiple". */ - instance_type: 'single' | 'multiple' + instanceType: 'single' | 'multiple' /** The currency code of the add-on. */ currency: string /** The rate cards of the add-on. */ - rate_cards: RateCard[] + rateCards: RateCard[] } /** Addon upsert request. */ @@ -4651,9 +4651,9 @@ export interface UpsertAddonRequest { description?: string labels?: Labels /** The InstanceType of the add-ons. Can be "single" or "multiple". */ - instance_type: 'single' | 'multiple' + instanceType: 'single' | 'multiple' /** The rate cards of the add-on. */ - rate_cards: RateCard[] + rateCards: RateCard[] } /** @@ -4679,18 +4679,18 @@ export interface InvoiceStandardLine { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The type of charge this line item represents. */ type: 'standard_line' /** * Indicates whether this line item's lifecycle is controlled by OpenMeter or * manually overridden by the API user. */ - lifecycle_controller: 'system' | 'manual' + lifecycleController: 'system' | 'manual' /** * The service period covered by this invoice, spanning the earliest line start to * the latest line end across all of its lines. @@ -4698,25 +4698,25 @@ export interface InvoiceStandardLine { * For an invoice with no lines the period is empty, which means `from` will be * equal to `to`. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** Aggregated financial totals for the line item. */ totals: Totals /** Discounts applied to this line item. */ discounts?: InvoiceLineDiscounts /** Credit applied to this line item. */ - credits_applied?: InvoiceLineCreditsApplied[] + creditsApplied?: InvoiceLineCreditsApplied[] /** External identifiers for this line item assigned by third-party systems. */ - external_references?: InvoiceLineExternalReferences + externalReferences?: InvoiceLineExternalReferences /** Reference to the subscription item that generated this line. */ subscription?: SubscriptionReference /** The rate card configuration snapshot used to price this line item. */ - rate_card: InvoiceLineRateCard + rateCard: InvoiceLineRateCard /** * Detailed sub-lines that this line has been broken down into. * * Present when line has individual details. */ - detailed_lines: InvoiceDetailedLine[] + detailedLines: InvoiceDetailedLine[] /** Reference to the charge associated with this line item. */ charge?: ChargeReference } @@ -4741,11 +4741,11 @@ export interface Profile { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * The name and contact information for the supplier this billing profile * represents @@ -4824,11 +4824,11 @@ export interface SubscriptionAddon { id: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * Display name of the resource. * @@ -4849,15 +4849,15 @@ export interface SubscriptionAddon { * An ISO-8601 timestamp representation of which point in time the quantity was * resolved to. */ - quantity_at: string + quantityAt: string /** An ISO-8601 timestamp representation of the cadence start of the resource. */ - active_from: string + activeFrom: string /** An ISO-8601 timestamp representation of the cadence end of the resource. */ - active_to?: string + activeTo?: string /** The timeline of the add-on. The returned periods are sorted and continuous. */ timeline: SubscriptionAddonTimelineSegment[] /** The rate cards of the add-on. */ - rate_cards: SubscriptionAddonRateCard[] + rateCards: SubscriptionAddonRateCard[] } /** Plans provide a template for subscriptions. */ @@ -4877,11 +4877,11 @@ export interface Plan { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * A key is a semi-unique string that is used to identify the plan. It is used to * reference the latest `active` version of the plan and is unique with the version @@ -4896,19 +4896,19 @@ export interface Plan { /** The currency code of the plan. */ currency: string /** The billing cadence for subscriptions using this plan. */ - billing_cadence: string + billingCadence: string /** Whether pro-rating is enabled for this plan. */ - pro_rating_enabled: boolean + proRatingEnabled: boolean /** * The date and time when the plan becomes `active`. When not specified, the plan * is in `draft` status. */ - effective_from?: string + effectiveFrom?: string /** * A scheduled date and time when the plan becomes `archived`. When not specified, * the plan is in `active` status indefinitely. */ - effective_to?: string + effectiveTo?: string /** * The status of the plan. Computed based on the effective start and end dates: * @@ -4933,12 +4933,12 @@ export interface Plan { * invoiced. * - `credit_only`: Usage is settled exclusively against credits. */ - settlement_mode: 'credit_then_invoice' | 'credit_only' + settlementMode: 'credit_then_invoice' | 'credit_only' /** * List of validation errors in `draft` state that prevent the plan from being * published. */ - validation_errors?: ProductCatalogValidationError[] + validationErrors?: ProductCatalogValidationError[] } /** Plan create request. */ @@ -4965,9 +4965,9 @@ export interface CreatePlanRequest { /** The currency code of the plan. */ currency: string /** The billing cadence for subscriptions using this plan. */ - billing_cadence: string + billingCadence: string /** Whether pro-rating is enabled for this plan. */ - pro_rating_enabled: boolean + proRatingEnabled: boolean /** * The plan phases define the pricing ramp for a subscription. A phase switch * occurs only at the end of a billing period. At least one phase is required. @@ -4991,7 +4991,7 @@ export interface UpsertPlanRequest { description?: string labels?: Labels /** Whether pro-rating is enabled for this plan. */ - pro_rating_enabled: boolean + proRatingEnabled: boolean /** * The plan phases define the pricing ramp for a subscription. A phase switch * occurs only at the end of a billing period. At least one phase is required. @@ -5034,11 +5034,11 @@ export interface InvoiceStandard { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** Human-readable invoice number generated by the invoicing app. */ number: string /** Three-letter ISO 4217 currency code for the invoice. */ @@ -5059,16 +5059,16 @@ export interface InvoiceStandard { * to `to`. In other cases those fields will be filled with the actual service * period. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** * Validation issues found during invoice processing. * * Present only when there are one or more validation findings. An empty list is * omitted. */ - validation_issues?: InvoiceValidationIssue[] + validationIssues?: InvoiceValidationIssue[] /** External identifiers assigned to this invoice by third-party systems. */ - external_references?: InvoiceExternalReferences + externalReferences?: InvoiceExternalReferences /** Discriminator field identifying this as a standard invoice. */ type: 'standard' /** Current lifecycle status of the invoice. */ @@ -5082,23 +5082,23 @@ export interface InvoiceStandard { | 'uncollectible' | 'voided' /** Detailed status information including available actions and workflow state. */ - status_details: InvoiceStatusDetails + statusDetails: InvoiceStatusDetails /** Timestamp when the invoice was issued to the customer. */ - issued_at?: string + issuedAt?: string /** * Timestamp until which the invoice remains in draft state. * * The invoice advances automatically once this time is reached. */ - draft_until?: string + draftUntil?: string /** Timestamp when usage quantities were last snapshotted for this invoice. */ - quantity_snapshotted_at?: string + quantitySnapshottedAt?: string /** Timestamp when collection was initiated for this invoice. */ - collection_at?: string + collectionAt?: string /** Timestamp when payment is due. */ - due_at?: string + dueAt?: string /** Timestamp when the invoice was sent to the customer. */ - sent_to_customer_at?: string + sentToCustomerAt?: string /** Workflow configuration snapshot captured at invoice creation time. */ workflow: InvoiceWorkflowSettings /** @@ -5136,19 +5136,19 @@ export interface BaseErrorInput { export interface WorkflowPaymentSendInvoiceSettingsInput { /** The collection method for the invoice. */ - collection_method: 'send_invoice' + collectionMethod: 'send_invoice' /** * The period after which the invoice is due. With some payment solutions it's only * applicable for manual collection method. */ - due_after?: string + dueAfter?: string } export interface InvoiceWorkflowInvoicingSettingsInput { /** Whether to automatically issue the invoice after the draft_period has passed. */ - auto_advance?: boolean + autoAdvance?: boolean /** The period for the invoice to be kept in draft status for manual reviews. */ - draft_period?: string + draftPeriod?: string } export interface EventInput { @@ -5253,13 +5253,13 @@ export interface CreateCreditGrantPurchaseInput { * * Defaults to 1.0. */ - per_unit_cost_basis?: string + perUnitCostBasis?: string /** * Controls when credits become available for consumption. * * Defaults to `on_creation`. */ - availability_policy?: 'on_creation' + availabilityPolicy?: 'on_creation' } export interface RateCardMeteredEntitlementInput { @@ -5269,7 +5269,7 @@ export interface RateCardMeteredEntitlementInput { * If soft limit is true, the subject can use the feature even if the entitlement * is exhausted; access remains granted. */ - is_soft_limit?: boolean + isSoftLimit?: boolean /** * The amount of usage granted each usage period, in the feature's unit. Usage is * counted against this allowance and the balance resets every usage period. When @@ -5281,7 +5281,7 @@ export interface RateCardMeteredEntitlementInput { * The reset interval of the metered entitlement in ISO8601 format. Defaults to the * billing cadence of the rate card. */ - usage_period?: string + usagePeriod?: string } export interface CreditGrantPurchaseInput { @@ -5296,7 +5296,7 @@ export interface CreditGrantPurchaseInput { * * Defaults to 1.0. */ - per_unit_cost_basis?: string + perUnitCostBasis?: string /** The purchase amount. Calculated from `per_unit_cost_basis` and credit `amount`. */ amount: string /** @@ -5304,9 +5304,9 @@ export interface CreditGrantPurchaseInput { * * Defaults to `on_creation`. */ - availability_policy?: 'on_creation' + availabilityPolicy?: 'on_creation' /** Current payment settlement status. */ - settlement_status?: 'pending' | 'authorized' | 'settled' + settlementStatus?: 'pending' | 'authorized' | 'settled' } export interface UnitConfigInput { @@ -5320,7 +5320,7 @@ export interface UnitConfigInput { * * Must be a positive non-zero value. */ - conversion_factor: string + conversionFactor: string /** * The rounding mode applied to the converted quantity for invoicing. * @@ -5341,18 +5341,18 @@ export interface UnitConfigInput { * * Optional. When omitted, no unit label is rendered. */ - display_unit?: string + displayUnit?: string } export interface WorkflowInvoicingSettingsInput { /** Whether to automatically issue the invoice after the draftPeriod has passed. */ - auto_advance?: boolean + autoAdvance?: boolean /** The period for the invoice to be kept in draft status for manual reviews. */ - draft_period?: string + draftPeriod?: string /** Should progressive billing be allowed for this workflow? */ - progressive_billing?: boolean + progressiveBilling?: boolean /** Controls how subscription-ending shortened service periods are billed. */ - subscription_end_proration_mode?: 'bill_full_period' | 'bill_actual_period' + subscriptionEndProrationMode?: 'bill_full_period' | 'bill_actual_period' } export interface GovernanceQueryRequestInput { @@ -5362,7 +5362,7 @@ export interface GovernanceQueryRequestInput { * * Defaults to `false`. */ - include_credits?: boolean + includeCredits?: boolean customer: GovernanceQueryRequestCustomers feature?: GovernanceQueryRequestFeatures } @@ -5373,11 +5373,11 @@ export interface IngestedEventInput { /** The customer if the event is associated with a customer. */ customer?: CustomerReference /** The date and time the event was ingested and its processing started. */ - ingested_at: string + ingestedAt: string /** The date and time the event was stored in the database. */ - stored_at: string + storedAt: string /** The validation errors of the ingested event. */ - validation_errors?: IngestedEventValidationError[] + validationErrors?: IngestedEventValidationError[] } export interface SubscriptionCancelInput { @@ -5387,21 +5387,21 @@ export interface SubscriptionCancelInput { export interface InvoiceUsageQuantityDetailInput { /** The raw quantity as reported by the meter (native units). */ - raw_quantity: string + rawQuantity: string /** * The precise decimal value after applying the conversion operation and factor, * before rounding. */ - converted_quantity: string + convertedQuantity: string /** The quantity after rounding, used for pricing. */ - invoiced_quantity: string + invoicedQuantity: string /** The display unit label (e.g., "GB", "hours", "M tokens"). */ - display_unit?: string + displayUnit?: string /** * Snapshot of the UnitConfig that was in effect at billing time. Ensures * historical invoices reflect the config that was actually applied. */ - applied_unit_config: UnitConfigInput + appliedUnitConfig: UnitConfigInput } export interface InvoiceWorkflowInput { @@ -5428,7 +5428,7 @@ export interface CreateCreditGrantRequestInput { description?: string labels?: CreateLabels /** Funding method of the grant. */ - funding_method: 'none' | 'invoice' | 'external' + fundingMethod: 'none' | 'invoice' | 'external' /** The currency of the granted credits. */ currency: string /** Granted credit amount. */ @@ -5443,7 +5443,7 @@ export interface CreateCreditGrantRequestInput { * credit grant tax code is applied, if that's not set the global default taxcode * is used. */ - tax_config?: CreateCreditGrantTaxConfig + taxConfig?: CreateCreditGrantTaxConfig filters?: CreateCreditGrantFilters /** Draw-down priority of the grant. Lower values have higher priority. */ priority?: number @@ -5452,13 +5452,13 @@ export interface CreateCreditGrantRequestInput { * * Defaults to the current date and time. */ - effective_at?: string + effectiveAt?: string /** * The duration after which the credit grant expires. * * Defaults to never expiring. */ - expires_after?: string + expiresAfter?: string /** * Idempotency key for the credit grant creation request. * @@ -5484,13 +5484,13 @@ export interface CreditGrantInput { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** Funding method of the grant. */ - funding_method: 'none' | 'invoice' | 'external' + fundingMethod: 'none' | 'invoice' | 'external' /** The currency of the granted credits. */ currency: string /** Granted credit amount. */ @@ -5505,7 +5505,7 @@ export interface CreditGrantInput { * credit grant tax code is applied, if that's not set the global default taxcode * is used. */ - tax_config?: CreditGrantTaxConfig + taxConfig?: CreditGrantTaxConfig /** Available when `funding_method` is `invoice`. */ invoice?: CreditGrantInvoiceReference filters?: CreditGrantFilters @@ -5516,7 +5516,7 @@ export interface CreditGrantInput { * * Defaults to the current date and time. */ - effective_at?: string + effectiveAt?: string /** * Idempotency key for the credit grant creation request. * @@ -5529,9 +5529,9 @@ export interface CreditGrantInput { * * Calculated from the grant effective time and `expires_after` if provided. */ - expires_at?: string + expiresAt?: string /** Timestamp when the grant was voided. */ - voided_at?: string + voidedAt?: string /** Current lifecycle status of the grant. */ status: 'pending' | 'active' | 'expired' | 'voided' } @@ -5557,7 +5557,7 @@ export interface WorkflowTaxSettingsInput { * default tax code is used instead. Existing tax-code values may still be removed, * and `behavior` remains fully supported. */ - default_tax_config?: TaxConfig + defaultTaxConfig?: TaxConfig } export interface IngestedEventPaginatedResponseInput { @@ -5580,9 +5580,9 @@ export interface MeterQueryRequestInput { * (http://www.iana.org/time-zones). The time zone is used to determine the start * and end of the time buckets. If not specified, the UTC timezone will be used. */ - time_zone?: string + timeZone?: string /** The dimensions to group the results by. */ - group_by_dimensions?: string[] + groupByDimensions?: string[] /** Filters to apply to the query. */ filters?: MeterQueryFilters } @@ -5594,23 +5594,23 @@ export interface AppStripeCreateCheckoutSessionRequestOptionsInput { * Defaults to auto, which only collects the address when necessary for tax * calculation. */ - billing_address_collection?: 'auto' | 'required' + billingAddressCollection?: 'auto' | 'required' /** * URL to redirect customers who cancel the checkout session. * * Not allowed when ui_mode is "embedded". */ - cancel_url?: string + cancelUrl?: string /** * Unique reference string for reconciling sessions with internal systems. * * Can be a customer ID, cart ID, or any other identifier. */ - client_reference_id?: string + clientReferenceId?: string /** Controls which customer fields can be updated by the checkout session. */ - customer_update?: AppStripeCreateCheckoutSessionCustomerUpdateInput + customerUpdate?: AppStripeCreateCheckoutSessionCustomerUpdateInput /** Configuration for collecting customer consent during checkout. */ - consent_collection?: AppStripeCreateCheckoutSessionConsentCollection + consentCollection?: AppStripeCreateCheckoutSessionConsentCollection /** * Three-letter ISO 4217 currency code in uppercase. * @@ -5618,13 +5618,13 @@ export interface AppStripeCreateCheckoutSessionRequestOptionsInput { */ currency?: string /** Custom text to display during checkout at various stages. */ - custom_text?: AppStripeCheckoutSessionCustomTextParams + customText?: AppStripeCheckoutSessionCustomTextParams /** * Unix timestamp when the checkout session expires. * * Can be 30 minutes to 24 hours from creation. Defaults to 24 hours. */ - expires_at?: bigint + expiresAt?: bigint /** * IETF language tag for the checkout UI locale. * @@ -5643,43 +5643,43 @@ export interface AppStripeCreateCheckoutSessionRequestOptionsInput { * Required if ui_mode is "embedded" and redirect-based payment methods are * enabled. */ - return_url?: string + returnUrl?: string /** * Success URL to redirect customers after completing payment or setup. * * Not allowed when ui_mode is "embedded". See: * https://docs.stripe.com/payments/checkout/custom-success-page */ - success_url?: string + successUrl?: string /** * The UI mode for the checkout session. * * "hosted" displays a Stripe-hosted page. "embedded" integrates directly into your * app. Defaults to "hosted". */ - ui_mode?: 'embedded' | 'hosted' + uiMode?: 'embedded' | 'hosted' /** * List of payment method types to enable (e.g., "card", "us_bank_account"). * * If not specified, Stripe enables all relevant payment methods. */ - payment_method_types?: string[] + paymentMethodTypes?: string[] /** * Redirect behavior for embedded checkout sessions. * * Controls when to redirect users after completion. See: * https://docs.stripe.com/payments/checkout/custom-success-page?payment-ui=embedded-form */ - redirect_on_completion?: 'always' | 'if_required' | 'never' + redirectOnCompletion?: 'always' | 'if_required' | 'never' /** Configuration for collecting tax IDs during checkout. */ - tax_id_collection?: AppStripeCreateCheckoutSessionTaxIdCollectionInput + taxIdCollection?: AppStripeCreateCheckoutSessionTaxIdCollectionInput } export interface InvoiceWorkflowSettingsInput { /** The apps that will be used to orchestrate the invoice's workflow. */ apps?: InvoiceWorkflowAppsReferences /** The billing profile that was the source of this workflow snapshot. */ - source_billing_profile: ProfileReference + sourceBillingProfile: ProfileReference /** * The workflow configuration that was active when the invoice was created. * @@ -5707,13 +5707,13 @@ export interface InvoiceDetailedLineInput { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The service period covered by this detailed line. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** Aggregated financial totals for the detailed line. */ totals: Totals /** The cost category of this detailed line. */ @@ -5721,13 +5721,13 @@ export interface InvoiceDetailedLineInput { /** Discounts applied to this detailed line. */ discounts?: InvoiceLineDiscounts /** Credit applied to this detailed line. */ - credits_applied?: InvoiceLineCreditsApplied[] + creditsApplied?: InvoiceLineCreditsApplied[] /** External identifiers for this detailed line. */ - external_references?: InvoiceLineExternalReferences + externalReferences?: InvoiceLineExternalReferences /** The quantity of the detailed line. */ quantity: string /** The unit price of the detailed line. */ - unit_price: string + unitPrice: string } export interface CreditGrantPagePaginatedResponseInput { @@ -5737,7 +5737,7 @@ export interface CreditGrantPagePaginatedResponseInput { export interface BadRequestInput extends BaseErrorInput { /** The list of parameters that failed validation. */ - invalid_parameters: ( + invalidParameters: ( | InvalidParameterStandard | InvalidParameterMinimumLength | InvalidParameterMaximumLength @@ -5753,7 +5753,7 @@ export interface CustomerStripeCreateCheckoutSessionRequestInput { * These options are passed directly to Stripe's * [checkout session creation API](https://docs.stripe.com/api/checkout/sessions/create). */ - stripe_options: AppStripeCreateCheckoutSessionRequestOptionsInput + stripeOptions: AppStripeCreateCheckoutSessionRequestOptionsInput } export interface WorkflowCollectionSettingsInput { @@ -5792,7 +5792,7 @@ export interface RateCardInput { * The billing cadence of the rate card. When null, the charge is one-time * (non-recurring). Only valid for flat prices. */ - billing_cadence?: string + billingCadence?: string /** The price of the rate card. */ price: PriceFree | PriceFlat | PriceUnit | PriceGraduated | PriceVolume /** @@ -5805,12 +5805,12 @@ export interface RateCardInput { * Accepted on create and update only when the UnitConfig feature is enabled on the * deployment; otherwise rejected. */ - unit_config?: UnitConfigInput + unitConfig?: UnitConfigInput /** * The payment term of the rate card. In advance payment term can only be used for * flat prices. */ - payment_term?: 'in_advance' | 'in_arrears' + paymentTerm?: 'in_advance' | 'in_arrears' /** * Spend commitments for this rate card. Only applicable to usage-based prices * (unit, graduated, volume). @@ -5819,7 +5819,7 @@ export interface RateCardInput { /** The discounts of the rate card. */ discounts?: RateCardDiscounts /** The tax config of the rate card. */ - tax_config?: RateCardTaxConfig + taxConfig?: RateCardTaxConfig /** * The entitlement template granted to subscribers of a plan or addon containing * this rate card. Requires `feature` to be set. @@ -5845,9 +5845,9 @@ export interface WorkflowInput { export interface SubscriptionAddonRateCardInput { /** The rate card. */ - rate_card: RateCardInput + rateCard: RateCardInput /** The IDs of the subscription items that this rate card belongs to. */ - affected_subscription_item_ids: string[] + affectedSubscriptionItemIds: string[] } export interface PlanPhaseInput { @@ -5871,7 +5871,7 @@ export interface PlanPhaseInput { */ duration?: string /** The rate cards of the plan. */ - rate_cards: RateCardInput[] + rateCards: RateCardInput[] } export interface AddonInput { @@ -5890,11 +5890,11 @@ export interface AddonInput { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * A key is a semi-unique string that is used to identify the add-on. It is used to * reference the latest `active` version of the add-on and is unique with the @@ -5904,19 +5904,19 @@ export interface AddonInput { /** Version of the add-on. Incremented when the add-on is updated. */ version?: number /** The InstanceType of the add-ons. Can be "single" or "multiple". */ - instance_type: 'single' | 'multiple' + instanceType: 'single' | 'multiple' /** The currency code of the add-on. */ currency: string /** * The date and time when the add-on becomes effective. When not specified, the * add-on is a draft. */ - effective_from?: string + effectiveFrom?: string /** * The date and time when the add-on is no longer effective. When not specified, * the add-on is effective indefinitely. */ - effective_to?: string + effectiveTo?: string /** * The status of the add-on. Computed based on the effective start and end dates: * @@ -5927,9 +5927,9 @@ export interface AddonInput { */ status: 'draft' | 'active' | 'archived' /** The rate cards of the add-on. */ - rate_cards: RateCardInput[] + rateCards: RateCardInput[] /** List of validation errors. */ - validation_errors?: ProductCatalogValidationError[] + validationErrors?: ProductCatalogValidationError[] } export interface CreateAddonRequestInput { @@ -5953,11 +5953,11 @@ export interface CreateAddonRequestInput { */ key: string /** The InstanceType of the add-ons. Can be "single" or "multiple". */ - instance_type: 'single' | 'multiple' + instanceType: 'single' | 'multiple' /** The currency code of the add-on. */ currency: string /** The rate cards of the add-on. */ - rate_cards: RateCardInput[] + rateCards: RateCardInput[] } export interface UpsertAddonRequestInput { @@ -5975,9 +5975,9 @@ export interface UpsertAddonRequestInput { description?: string labels?: Labels /** The InstanceType of the add-ons. Can be "single" or "multiple". */ - instance_type: 'single' | 'multiple' + instanceType: 'single' | 'multiple' /** The rate cards of the add-on. */ - rate_cards: RateCardInput[] + rateCards: RateCardInput[] } export interface InvoiceStandardLineInput { @@ -5996,18 +5996,18 @@ export interface InvoiceStandardLineInput { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** The type of charge this line item represents. */ type: 'standard_line' /** * Indicates whether this line item's lifecycle is controlled by OpenMeter or * manually overridden by the API user. */ - lifecycle_controller: 'system' | 'manual' + lifecycleController: 'system' | 'manual' /** * The service period covered by this invoice, spanning the earliest line start to * the latest line end across all of its lines. @@ -6015,25 +6015,25 @@ export interface InvoiceStandardLineInput { * For an invoice with no lines the period is empty, which means `from` will be * equal to `to`. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** Aggregated financial totals for the line item. */ totals: Totals /** Discounts applied to this line item. */ discounts?: InvoiceLineDiscounts /** Credit applied to this line item. */ - credits_applied?: InvoiceLineCreditsApplied[] + creditsApplied?: InvoiceLineCreditsApplied[] /** External identifiers for this line item assigned by third-party systems. */ - external_references?: InvoiceLineExternalReferences + externalReferences?: InvoiceLineExternalReferences /** Reference to the subscription item that generated this line. */ subscription?: SubscriptionReference /** The rate card configuration snapshot used to price this line item. */ - rate_card: InvoiceLineRateCard + rateCard: InvoiceLineRateCard /** * Detailed sub-lines that this line has been broken down into. * * Present when line has individual details. */ - detailed_lines: InvoiceDetailedLineInput[] + detailedLines: InvoiceDetailedLineInput[] /** Reference to the charge associated with this line item. */ charge?: ChargeReference } @@ -6054,11 +6054,11 @@ export interface ProfileInput { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * The name and contact information for the supplier this billing profile * represents @@ -6128,11 +6128,11 @@ export interface SubscriptionAddonInput { id: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * Display name of the resource. * @@ -6153,15 +6153,15 @@ export interface SubscriptionAddonInput { * An ISO-8601 timestamp representation of which point in time the quantity was * resolved to. */ - quantity_at: string + quantityAt: string /** An ISO-8601 timestamp representation of the cadence start of the resource. */ - active_from: string + activeFrom: string /** An ISO-8601 timestamp representation of the cadence end of the resource. */ - active_to?: string + activeTo?: string /** The timeline of the add-on. The returned periods are sorted and continuous. */ timeline: SubscriptionAddonTimelineSegment[] /** The rate cards of the add-on. */ - rate_cards: SubscriptionAddonRateCardInput[] + rateCards: SubscriptionAddonRateCardInput[] } export interface PlanInput { @@ -6180,11 +6180,11 @@ export interface PlanInput { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** * A key is a semi-unique string that is used to identify the plan. It is used to * reference the latest `active` version of the plan and is unique with the version @@ -6199,19 +6199,19 @@ export interface PlanInput { /** The currency code of the plan. */ currency: string /** The billing cadence for subscriptions using this plan. */ - billing_cadence: string + billingCadence: string /** Whether pro-rating is enabled for this plan. */ - pro_rating_enabled?: boolean + proRatingEnabled?: boolean /** * The date and time when the plan becomes `active`. When not specified, the plan * is in `draft` status. */ - effective_from?: string + effectiveFrom?: string /** * A scheduled date and time when the plan becomes `archived`. When not specified, * the plan is in `active` status indefinitely. */ - effective_to?: string + effectiveTo?: string /** * The status of the plan. Computed based on the effective start and end dates: * @@ -6236,12 +6236,12 @@ export interface PlanInput { * invoiced. * - `credit_only`: Usage is settled exclusively against credits. */ - settlement_mode?: 'credit_then_invoice' | 'credit_only' + settlementMode?: 'credit_then_invoice' | 'credit_only' /** * List of validation errors in `draft` state that prevent the plan from being * published. */ - validation_errors?: ProductCatalogValidationError[] + validationErrors?: ProductCatalogValidationError[] } export interface CreatePlanRequestInput { @@ -6267,9 +6267,9 @@ export interface CreatePlanRequestInput { /** The currency code of the plan. */ currency: string /** The billing cadence for subscriptions using this plan. */ - billing_cadence: string + billingCadence: string /** Whether pro-rating is enabled for this plan. */ - pro_rating_enabled?: boolean + proRatingEnabled?: boolean /** * The plan phases define the pricing ramp for a subscription. A phase switch * occurs only at the end of a billing period. At least one phase is required. @@ -6292,7 +6292,7 @@ export interface UpsertPlanRequestInput { description?: string labels?: Labels /** Whether pro-rating is enabled for this plan. */ - pro_rating_enabled?: boolean + proRatingEnabled?: boolean /** * The plan phases define the pricing ramp for a subscription. A phase switch * occurs only at the end of a billing period. At least one phase is required. @@ -6330,11 +6330,11 @@ export interface InvoiceStandardInput { description?: string labels?: Labels /** An ISO-8601 timestamp representation of entity creation date. */ - created_at: string + createdAt: string /** An ISO-8601 timestamp representation of entity last update date. */ - updated_at: string + updatedAt: string /** An ISO-8601 timestamp representation of entity deletion date. */ - deleted_at?: string + deletedAt?: string /** Human-readable invoice number generated by the invoicing app. */ number: string /** Three-letter ISO 4217 currency code for the invoice. */ @@ -6355,16 +6355,16 @@ export interface InvoiceStandardInput { * to `to`. In other cases those fields will be filled with the actual service * period. */ - service_period: ClosedPeriod + servicePeriod: ClosedPeriod /** * Validation issues found during invoice processing. * * Present only when there are one or more validation findings. An empty list is * omitted. */ - validation_issues?: InvoiceValidationIssue[] + validationIssues?: InvoiceValidationIssue[] /** External identifiers assigned to this invoice by third-party systems. */ - external_references?: InvoiceExternalReferences + externalReferences?: InvoiceExternalReferences /** Discriminator field identifying this as a standard invoice. */ type: 'standard' /** Current lifecycle status of the invoice. */ @@ -6378,23 +6378,23 @@ export interface InvoiceStandardInput { | 'uncollectible' | 'voided' /** Detailed status information including available actions and workflow state. */ - status_details: InvoiceStatusDetails + statusDetails: InvoiceStatusDetails /** Timestamp when the invoice was issued to the customer. */ - issued_at?: string + issuedAt?: string /** * Timestamp until which the invoice remains in draft state. * * The invoice advances automatically once this time is reached. */ - draft_until?: string + draftUntil?: string /** Timestamp when usage quantities were last snapshotted for this invoice. */ - quantity_snapshotted_at?: string + quantitySnapshottedAt?: string /** Timestamp when collection was initiated for this invoice. */ - collection_at?: string + collectionAt?: string /** Timestamp when payment is due. */ - due_at?: string + dueAt?: string /** Timestamp when the invoice was sent to the customer. */ - sent_to_customer_at?: string + sentToCustomerAt?: string /** Workflow configuration snapshot captured at invoice creation time. */ workflow: InvoiceWorkflowSettingsInput /** diff --git a/api/spec/packages/aip-client-javascript/tests/meters.spec.ts b/api/spec/packages/aip-client-javascript/tests/meters.spec.ts index 385e1efa1b..fd21bb977e 100644 --- a/api/spec/packages/aip-client-javascript/tests/meters.spec.ts +++ b/api/spec/packages/aip-client-javascript/tests/meters.spec.ts @@ -66,10 +66,10 @@ describe('listMeters query serialization', () => { expect(q.get('filter[key][oeq]')).toBe('a,b,c') }) - it('serializes sort as a plain string', async () => { + it('serializes sort as a plain string, snake-ifying the field name', async () => { mockList() await funcs.listMeters(client(), { - sort: { by: 'created_at', order: 'desc' }, + sort: { by: 'createdAt', order: 'desc' }, }) const q = lastQuery() expect(q.get('sort')).toBe('created_at desc') @@ -83,17 +83,20 @@ describe('listMeters query serialization', () => { }) }) -describe('responses are not validated', () => { - it('passes through additive response fields untouched', async () => { +describe('responses are mapped to the camelCase shape', () => { + it('camelizes known fields and drops fields not in the schema', async () => { fetchMock.route('*', { - body: { ...meter, brand_new_field: 'kept' }, + body: { ...meter, brand_new_field: 'dropped' }, headers: { 'Content-Type': 'application/json' }, }) const result = await funcs.getMeter(client(), { meterId: 'm' }) expect(result.ok).toBe(true) expect(result.value).toMatchObject({ id: meter.id, - brand_new_field: 'kept', + eventType: meter.event_type, + createdAt: meter.created_at, }) + expect('event_type' in result.value!).toBe(false) + expect('brand_new_field' in result.value!).toBe(false) }) }) diff --git a/api/spec/packages/aip-client-javascript/tests/wire-helpers.ts b/api/spec/packages/aip-client-javascript/tests/wire-helpers.ts new file mode 100644 index 0000000000..f281eaf8a1 --- /dev/null +++ b/api/spec/packages/aip-client-javascript/tests/wire-helpers.ts @@ -0,0 +1,146 @@ +import type { ZodType } from 'zod' + +type ZodDef = { + type: string + innerType?: ZodType + valueType?: ZodType + element?: ZodType + options?: ZodType[] +} + +function def(schema: ZodType | undefined): ZodDef | undefined { + return (schema as { def?: ZodDef } | undefined)?.def +} + +function shapeOf( + schema: ZodType | undefined, +): Record | undefined { + return (schema as { shape?: Record } | undefined)?.shape +} + +/** + * A value satisfying a schema's structure, with camelCase object keys (the emitted + * schemas are camelCase) and a deliberately underscore-containing record key so the + * casing assertion can tell a leaked field key from a preserved user key. + * + * Every field whose sample is defined is populated (so optional camelCase fields are + * exercised). A key whose sample comes back undefined is omitted rather than set to + * undefined, so a discriminated-union variant always carries its discriminator + * literal and the mapper can resolve every variant it descends into. The depth cap + * bounds the self-referential filter schemas. + */ +export function sampleCamel(schema: ZodType | undefined, depth = 0): unknown { + const d = def(schema) + if (!d || depth > 12) { + return undefined + } + switch (d.type) { + case 'object': { + const out: Record = {} + for (const [k, v] of Object.entries(shapeOf(schema) ?? {})) { + const value = sampleCamel(v, depth + 1) + if (value !== undefined) { + out[k] = value + } + } + return out + } + case 'optional': + case 'nullable': + case 'default': + return sampleCamel(d.innerType, depth) + case 'array': + return [sampleCamel(d.element, depth + 1)] + case 'record': + return { user_key_a: sampleCamel(d.valueType, depth + 1) } + case 'union': + return sampleCamel(d.options?.[0], depth) + case 'literal': + return (schema as { value?: unknown }).value + case 'string': + return 's' + case 'int': + case 'number': + return 1 + case 'bigint': + return 1n + case 'boolean': + return true + case 'date': + return new Date(0) + default: + return undefined + } +} + +// Snake-cased counterpart for response samples: object keys are snake_cased so +// fromWire has wire-shaped input. Record user keys and literal values are kept. +export function sampleSnake(schema: ZodType | undefined, depth = 0): unknown { + const value = sampleCamel(schema, depth) + return toSnakeKeys(value) +} + +function toSnakeKeys(value: unknown): unknown { + if (Array.isArray(value)) { + return value.map(toSnakeKeys) + } + if (value && typeof value === 'object' && !(value instanceof Date)) { + const out: Record = {} + for (const [k, v] of Object.entries(value)) { + const key = k.startsWith('user_key_') + ? k + : k.replace(/([A-Z])/g, (_m, c: string) => `_${c.toLowerCase()}`) + out[key] = toSnakeKeys(v) + } + return out + } + return value +} + +/** + * Every object key in the value, except keys of a `user_key_*` map (preserved + * record keys) and date values. Used to assert no casing leaks past the mapper. + */ +export function collectFieldKeys( + value: unknown, + keys: string[] = [], +): string[] { + if (Array.isArray(value)) { + for (const item of value) { + collectFieldKeys(item, keys) + } + return keys + } + if (value && typeof value === 'object' && !(value instanceof Date)) { + for (const [k, v] of Object.entries(value)) { + if (k.startsWith('user_key_')) { + // Preserved user record key — not a schema field, skip it and its subtree. + continue + } + keys.push(k) + collectFieldKeys(v, keys) + } + } + return keys +} + +/** Per-op schemas grouped by base name, from the generated schemas module. */ +export function operationSchemaPairs( + schemas: Record, +): Array<{ base: string; body?: ZodType; response?: ZodType }> { + const bases = new Map() + for (const [name, schema] of Object.entries(schemas)) { + const bodyMatch = name.match(/^(.*)Body$/) + const responseMatch = name.match(/^(.*)Response$/) + if (bodyMatch) { + const entry = bases.get(bodyMatch[1]) ?? {} + entry.body = schema as ZodType + bases.set(bodyMatch[1], entry) + } else if (responseMatch) { + const entry = bases.get(responseMatch[1]) ?? {} + entry.response = schema as ZodType + bases.set(responseMatch[1], entry) + } + } + return [...bases.entries()].map(([base, v]) => ({ base, ...v })) +} diff --git a/api/spec/packages/aip-client-javascript/tests/wire.generated.spec.ts b/api/spec/packages/aip-client-javascript/tests/wire.generated.spec.ts new file mode 100644 index 0000000000..e4f60d0753 --- /dev/null +++ b/api/spec/packages/aip-client-javascript/tests/wire.generated.spec.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest' +import * as schemas from '../src/models/schemas.js' +import { fromWire, toWire } from '../src/lib/wire.js' +import { + collectFieldKeys, + operationSchemaPairs, + sampleCamel, + sampleSnake, +} from './wire-helpers.js' + +const SNAKE_KEY = /_/ +const CAMEL_KEY = /[A-Z]/ + +const pairs = operationSchemaPairs(schemas as Record) + +// The casing-leak invariant, applied to every operation: toWire produces a +// snake_case body (no camelCase field key escapes), fromWire produces a camelCase +// shape (no snake_case field key escapes). Preserved record user keys +// (`user_key_*`) are excluded by collectFieldKeys, so a legitimately-preserved +// key never trips the assertion. Field names that are single-word in both cases +// pass trivially. +describe('per-operation wire casing (toWire → snake, fromWire → camel)', () => { + for (const { base, body, response } of pairs) { + if (body) { + it(`${base}: request body has no camelCase field key on the wire`, () => { + const wire = toWire(sampleCamel(body), body) + const leaked = collectFieldKeys(wire).filter((k) => CAMEL_KEY.test(k)) + expect(leaked).toEqual([]) + }) + + // Round-trips the public sample through the wire and back. Catches a silent + // drop or a mis-mapped name that the no-leak check alone cannot see: the + // sample contains only in-schema fields, so nothing should be dropped and + // identity must hold exactly. + it(`${base}: request body round-trips (camel → wire → camel)`, () => { + const sample = sampleCamel(body) + expect(fromWire(toWire(sample, body), body)).toEqual(sample) + }) + } + if (response) { + it(`${base}: response has no snake_case field key after mapping`, () => { + const camel = fromWire(sampleSnake(response), response) + const leaked = collectFieldKeys(camel).filter((k) => SNAKE_KEY.test(k)) + expect(leaked).toEqual([]) + }) + + it(`${base}: response round-trips (wire → camel → wire)`, () => { + const wire = sampleSnake(response) + expect(toWire(fromWire(wire, response), response)).toEqual(wire) + }) + } + } +}) + +// The strict `…Wire` schemas behind the validate option are emitted from the same +// TypeSpec walk as the camelCase schemas (one pass parameterized by casing + +// strictness), so they agree with the data mapper by construction — no per-op +// runtime sweep is needed. wire.spec.ts spot-checks the generated wire schemas +// (strict closed model, open record-spread, discriminator, record value, cyclic). diff --git a/api/spec/packages/aip-client-javascript/tests/wire.spec.ts b/api/spec/packages/aip-client-javascript/tests/wire.spec.ts new file mode 100644 index 0000000000..47d8cadc6f --- /dev/null +++ b/api/spec/packages/aip-client-javascript/tests/wire.spec.ts @@ -0,0 +1,379 @@ +import fetchMock from '@fetch-mock/vitest' +import { beforeEach, describe, expect, it } from 'vitest' +import { z } from 'zod' +import { Client, funcs, ValidationError } from '../src/index.js' +import * as schemas from '../src/models/schemas.js' +import { fromWire, toWire } from '../src/lib/wire.js' + +beforeEach(() => { + fetchMock.mockReset() +}) + +function client() { + return new Client({ + baseUrl: 'https://eu.api.konghq.com/v3', + apiKey: 'k', + fetch: fetchMock.fetchHandler, + }) +} + +describe('wire mapper (toWire/fromWire over real schemas)', () => { + it('preserves record keys and renames value fields (governance)', () => { + const wire = { features: { my_user_feature: { has_access: true } } } + const camel = fromWire(wire, schemas.governanceQueryResult) as any + expect(Object.keys(camel.features)[0]).toBe('my_user_feature') + expect(camel.features.my_user_feature.hasAccess).toBe(true) + + const back = toWire( + { features: { my_user_feature: { hasAccess: true } } }, + schemas.governanceQueryResult, + ) as any + expect(Object.keys(back.features)[0]).toBe('my_user_feature') + expect(back.features.my_user_feature.has_access).toBe(true) + }) + + it('handles a multi-word discriminator (collection_method)', () => { + const wire = { collection_method: 'charge_automatically' } + const camel = fromWire(wire, schemas.workflowPaymentSettings) as any + expect(camel.collectionMethod).toBe('charge_automatically') + + const back = toWire( + { collectionMethod: 'charge_automatically' }, + schemas.workflowPaymentSettings, + ) as any + expect(back.collection_method).toBe('charge_automatically') + }) + + it('preserves meter dimension record keys', () => { + const wire = { dimensions: { my_dim_key: { eq: 'x' } } } + const camel = fromWire(wire, schemas.meterQueryFilters) as any + expect('my_dim_key' in camel.dimensions).toBe(true) + expect(camel.dimensions.my_dim_key.eq).toBe('x') + }) + + it('terminates on a cyclic filter (and/or self-reference)', () => { + const wire = { + dimensions: { + my_dim: { eq: 'a', and: [{ eq: 'b' }, { or: [{ eq: 'c' }] }] }, + }, + } + const camel = fromWire(wire, schemas.meterQueryFilters) as any + expect(camel.dimensions.my_dim.and[1].or[0].eq).toBe('c') + }) + + it('preserves arbitrary keys inside a record of unknown (event.data)', () => { + const wire = { type: 'x', source: 's', data: { user_set_key: { a_b: 1 } } } + const camel = fromWire(wire, schemas.event) as any + expect(camel.data.user_set_key).toEqual({ a_b: 1 }) + }) + + it('walks every element of a single-or-batch (T | T[]) body', () => { + // Mirrors the ingest body shape `EventInput | EventInput[]`. A batch must + // walk each element, not pass it through untransformed (the array branch + // must resolve the array variant's element schema from the union). + const single = z.object({ eventType: z.string() }) + const body = z.union([single, z.array(single)]) + + const batch = toWire( + [{ eventType: 'a' }, { eventType: 'b' }], + body, + ) as any[] + expect(batch).toHaveLength(2) + for (const e of batch) { + expect('event_type' in e).toBe(true) + expect('eventType' in e).toBe(false) + } + + const one = toWire({ eventType: 'a' }, body) as any + expect(one.event_type).toBe('a') + }) +}) + +describe('end-to-end wire mapping through a func', () => { + it('sends a snake body and returns a camelCase response (createMeter)', async () => { + let sentBody: any + fetchMock.route('*', async ({ options }) => { + sentBody = JSON.parse(options!.body as string) + return { + body: { + id: '01ARZ3NDEKTSV4RRFFQ69G5FAV', + name: 'API calls', + key: 'api_calls', + aggregation: 'count', + event_type: 'api-request', + created_at: '2024-01-01T00:00:00Z', + updated_at: '2024-01-01T00:00:00Z', + }, + headers: { 'Content-Type': 'application/json' }, + } + }) + + const result = await funcs.createMeter(client(), { + name: 'API calls', + key: 'api_calls', + aggregation: 'count', + eventType: 'api-request', + valueProperty: '$.value', + }) + + // request body is snake on the wire + expect(sentBody.event_type).toBe('api-request') + expect(sentBody.value_property).toBe('$.value') + expect('eventType' in sentBody).toBe(false) + + // response is camelCase + expect(result.ok).toBe(true) + expect(result.value).toMatchObject({ + eventType: 'api-request', + createdAt: '2024-01-01T00:00:00Z', + }) + }) +}) + +describe('wire walker edge cases', () => { + it('passes a scalar/null/array-of-scalars through unchanged', () => { + expect(toWire(5, z.number())).toBe(5) + expect(toWire(null, z.string().nullable())).toBe(null) + expect(toWire([1, 2], z.array(z.number()))).toEqual([1, 2]) + }) + + it('recurses a record whose value is an array or union of models', () => { + const arrModel = z.record( + z.string(), + z.array(z.object({ fooBar: z.string() })), + ) + expect(toWire({ user_k: [{ fooBar: 'x' }] }, arrModel)).toEqual({ + user_k: [{ foo_bar: 'x' }], + }) + + const unionModel = z.record( + z.string(), + z.union([ + z.object({ fooBar: z.string() }), + z.object({ bazQux: z.number() }), + ]), + ) + expect(toWire({ user_k: { fooBar: 'x' } }, unionModel)).toEqual({ + user_k: { foo_bar: 'x' }, + }) + }) + + it('leaves a record of scalars untouched (keys and values)', () => { + const scalarMap = z.record(z.string(), z.string()) + expect(toWire({ a_b: 'v', cD: 'w' }, scalarMap)).toEqual({ + a_b: 'v', + cD: 'w', + }) + }) + + it('renames the object variant of a scalar-or-object union', () => { + // The realistic non-discriminated shape (a filter field: string | { op }). + // The codegen gate guarantees at most one object variant, so it is picked + // unambiguously; scalar data flows through the scalar branch unchanged. + const union = z.union([z.string(), z.object({ fooBar: z.string() })]) + expect(toWire({ fooBar: 'x' }, union)).toEqual({ foo_bar: 'x' }) + expect(toWire('plain', union)).toBe('plain') + }) + + it('fails closed on a discriminated union with an unknown discriminator value', () => { + const union = z.discriminatedUnion('kind', [ + z.object({ kind: z.literal('a'), fooBar: z.string() }), + z.object({ kind: z.literal('b'), bazQux: z.number() }), + ]) + // no variant has kind === 'z' → returned unchanged + expect(toWire({ kind: 'z', fooBar: 'x' }, union)).toEqual({ + kind: 'z', + fooBar: 'x', + }) + }) + + it('reuses the memoized variant map across calls on the same union', () => { + const union = z.discriminatedUnion('kind', [ + z.object({ kind: z.literal('a'), fooBar: z.string() }), + z.object({ kind: z.literal('b'), bazQux: z.number() }), + ]) + // First call builds the map; second hits the cache. Both dispatch correctly. + expect(toWire({ kind: 'a', fooBar: 'x' }, union)).toEqual({ + kind: 'a', + foo_bar: 'x', + }) + expect(toWire({ kind: 'b', bazQux: 2 }, union)).toEqual({ + kind: 'b', + baz_qux: 2, + }) + }) + + it('walks array data against a union schema with an array variant', () => { + // exercises arrayElement resolving the array option of a union + const union = z.union([ + z.object({ fooBar: z.string() }), + z.array(z.object({ fooBar: z.string() })), + ]) + expect(toWire([{ fooBar: 'x' }], union)).toEqual([{ foo_bar: 'x' }]) + }) + + it('passes array data through when the union has no array variant', () => { + // arrayElement returns undefined → elements walked with no schema → unchanged + const union = z.union([ + z.object({ fooBar: z.string() }), + z.object({ bazQux: z.number() }), + ]) + expect(toWire([{ fooBar: 'x' }], union)).toEqual([{ fooBar: 'x' }]) + }) + + it('keeps a record whose value schema is an unknown/any (event.data-like)', () => { + const rec = z.record(z.string(), z.unknown()) + expect(toWire({ user_k: { aB: 1 } }, rec)).toEqual({ user_k: { aB: 1 } }) + }) +}) + +describe('optional schema validation (validate option)', () => { + const goodMeter = { + id: '01ARZ3NDEKTSV4RRFFQ69G5FAV', + name: 'API calls', + key: 'api_calls', + aggregation: 'count', + event_type: 'api-request', + created_at: '2024-01-01T00:00:00Z', + updated_at: '2024-01-01T00:00:00Z', + } + const goodCreate = { + name: 'API calls', + key: 'api_calls', + aggregation: 'count' as const, + eventType: 'api-request', + valueProperty: '$.value', + } + + function validatingClient() { + return new Client({ + baseUrl: 'https://eu.api.konghq.com/v3', + apiKey: 'k', + fetch: fetchMock.fetchHandler, + validate: true, + }) + } + + it('passes a valid request and response when validate is on', async () => { + fetchMock.route('*', { + body: goodMeter, + headers: { 'Content-Type': 'application/json' }, + }) + const result = await funcs.createMeter(validatingClient(), goodCreate) + expect(result.ok).toBe(true) + expect(result.value).toMatchObject({ eventType: 'api-request' }) + }) + + it('rejects a request body that fails its schema', async () => { + fetchMock.route('*', { + body: goodMeter, + headers: { 'Content-Type': 'application/json' }, + }) + // name is required; omit it + const result = await funcs.createMeter(validatingClient(), { + ...goodCreate, + name: undefined as unknown as string, + }) + expect(result.ok).toBe(false) + expect(result.error).toBeInstanceOf(ValidationError) + }) + + it('rejects an enum-drift response (the reason validation is off by default)', async () => { + fetchMock.route('*', { + // server sends an aggregation value this SDK version does not know + body: { ...goodMeter, aggregation: 'p99' }, + headers: { 'Content-Type': 'application/json' }, + }) + const result = await funcs.getMeter(validatingClient(), { meterId: 'm' }) + expect(result.ok).toBe(false) + expect(result.error).toBeInstanceOf(ValidationError) + }) + + it('does NOT reject the same enum-drift response when validate is off', async () => { + fetchMock.route('*', { + body: { ...goodMeter, aggregation: 'p99' }, + headers: { 'Content-Type': 'application/json' }, + }) + const result = await funcs.getMeter(client(), { meterId: 'm' }) + expect(result.ok).toBe(true) + }) + + it('rejects a bad body on a void/no-JSON-response op as Result.error, not a throw', async () => { + // ingestMeteringEvents has a request body but a void response; body validation + // must run inside request() so a failure becomes Result.error here too. + fetchMock.route('*', { status: 204 }) + const result = await funcs.ingestMeteringEvents( + validatingClient(), + {} as never, + ) + expect(result.ok).toBe(false) + expect(result.error).toBeInstanceOf(ValidationError) + }) +}) + +describe('generated wire schemas (snake_case, strict)', () => { + const ok = (s: { safeParse(v: unknown): { success: boolean } }, v: unknown) => + s.safeParse(v).success + + const meterWireData = { + id: '01ARZ3NDEKTSV4RRFFQ69G5FAV', + name: 'n', + key: 'k', + aggregation: 'count', + event_type: 'e', + created_at: '2024-01-01T00:00:00Z', + updated_at: '2024-01-01T00:00:00Z', + } + + it('a closed model wire schema is snake-keyed and strict', () => { + expect(ok(schemas.meterWire, meterWireData)).toBe(true) + // leaked camelCase key rejected + expect(ok(schemas.meterWire, { ...meterWireData, eventType: 'x' })).toBe( + false, + ) + // unknown/extra wire key rejected + expect(ok(schemas.meterWire, { ...meterWireData, new_field: 1 })).toBe( + false, + ) + }) + + it('a record value model wire schema preserves user keys, snakeifies fields', () => { + expect(ok(schemas.governanceFeatureAccessWire, { has_access: true })).toBe( + true, + ) + expect(ok(schemas.governanceFeatureAccessWire, { hasAccess: true })).toBe( + false, + ) + }) + + it('a discriminated union wire schema uses the snake discriminator', () => { + expect( + ok(schemas.workflowPaymentSettingsWire, { + collection_method: 'charge_automatically', + }), + ).toBe(true) + }) + + it('an open (record-spread) wire schema accepts extra keys (not strict)', () => { + // baseErrorWire is emitsAsIntersection — strict would defeat the record arm + // that exists to accept additional members. Must stay permissive. + expect( + ok(schemas.baseErrorWire, { + type: 't', + status: 400, + title: 'x', + detail: 'd', + instance: '/i', + anything_extra: 1, + }), + ).toBe(true) + }) + + it('a cyclic filter wire schema terminates and accepts nested data', () => { + expect( + ok(schemas.meterQueryFiltersWire, { + dimensions: { my_dim: { eq: 'a', and: [{ eq: 'b' }] } }, + }), + ).toBe(true) + }) +}) diff --git a/api/spec/packages/aip-client-javascript/vitest.config.ts b/api/spec/packages/aip-client-javascript/vitest.config.ts new file mode 100644 index 0000000000..76f5faa692 --- /dev/null +++ b/api/spec/packages/aip-client-javascript/vitest.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'vitest/config' + +// The boundary mapper (src/lib/wire.ts) is the one hand-written runtime module with +// no compile-time guard, so its behavior is covered entirely by tests. Hold it at +// full statement/line/function coverage. The branch threshold is below 100 because +// the residual uncovered branches are defensive nullish-coalescing arms (`?? []`, +// `?? {}`) guarding zod internals that the preceding type-guard already makes +// non-null — unreachable with any real schema, so not worth contriving inputs for. +export default defineConfig({ + test: { + coverage: { + provider: 'v8', + include: ['src/lib/wire.ts'], + thresholds: { + statements: 100, + functions: 100, + lines: 100, + branches: 85, + }, + }, + }, +}) diff --git a/api/spec/packages/typespec-typescript/package.json b/api/spec/packages/typespec-typescript/package.json index 6590d21318..b23c337bed 100644 --- a/api/spec/packages/typespec-typescript/package.json +++ b/api/spec/packages/typespec-typescript/package.json @@ -14,7 +14,8 @@ "build": "alloy build", "watch": "alloy build --watch", "typecheck": "tsc --noEmit", - "check": "pnpm run typecheck" + "test": "vitest --run", + "check": "pnpm run typecheck && pnpm run test" }, "dependencies": { "@alloy-js/core": "0.23.1", diff --git a/api/spec/packages/typespec-typescript/src/ZodOperations.tsx b/api/spec/packages/typespec-typescript/src/ZodOperations.tsx index 65a7c7d395..e33434d703 100644 --- a/api/spec/packages/typespec-typescript/src/ZodOperations.tsx +++ b/api/spec/packages/typespec-typescript/src/ZodOperations.tsx @@ -15,7 +15,12 @@ import { $ } from '@typespec/compiler/typekit' import { getAllHttpServices } from '@typespec/http' import { getOperationId } from '@typespec/openapi' import { ZodSchema } from './components/ZodSchema.jsx' -import { callPart, CoerceContext, zodMemberExpr } from './utils.jsx' +import { + callPart, + CoerceContext, + toCamelCase, + zodMemberExpr, +} from './utils.jsx' /** * Per-operation request and response Zod schemas. @@ -103,7 +108,7 @@ interface ParamLeaf { prop: ModelProperty } -function paramObject(params: ParamLeaf[]): Children { +function paramObject(params: ParamLeaf[], camelize: boolean): Children { return ( {zodMemberExpr( @@ -112,7 +117,7 @@ function paramObject(params: ParamLeaf[]): Children { {(p) => ( - + )} @@ -132,6 +137,7 @@ function paramObject(params: ParamLeaf[]): Children { export function operationSchemas( program: Program, op: Operation, + bodyOverrides: Map, ): OperationSchema[] { const tk = $(program) const httpOp = tk.httpOperation.get(op) @@ -154,7 +160,7 @@ export function operationSchemas( baseName: `${base}PathParams`, render: (name) => ( - {paramObject(pathParams)} + {paramObject(pathParams, false)} ), }) @@ -165,13 +171,17 @@ export function operationSchemas( baseName: `${base}QueryParams`, render: (name) => ( - {paramObject(queryParams)} + {paramObject(queryParams, true)} ), }) } - const bodyType = httpOp.parameters.body?.type + // The body the func actually sends: a shared-route JSON override (e.g. the + // single-or-batch ingest union, or a response-only variant like queryMeterCsv + // whose declared op omits the body) when present, else the op's own body. The + // boundary mapper walks this schema, so it must match the wire shape. + const bodyType = bodyOverrides.get(base) ?? httpOp.parameters.body?.type if (bodyType) { out.push({ baseName: `${base}Body`, diff --git a/api/spec/packages/typespec-typescript/src/casing-gate.ts b/api/spec/packages/typespec-typescript/src/casing-gate.ts new file mode 100644 index 0000000000..407a34ac0a --- /dev/null +++ b/api/spec/packages/typespec-typescript/src/casing-gate.ts @@ -0,0 +1,209 @@ +import { + type Model, + type Namespace, + type Operation, + type Program, + resolveEncodedName, + type Type, + type Union, +} from '@typespec/compiler' +import { $ } from '@typespec/compiler/typekit' +import '@typespec/http/experimental/typekit' +import { isCasingDerivable } from './casing.js' +import { bodyProperties } from './utils.jsx' + +/** + * The JSON wire name of a body property: its `@encodedName("application/json", …)` + * when present, otherwise its declared name. This is the same source the OpenAPI + * emitter uses, so the gate measures the public→snake transform against the real + * wire contract rather than against the (camelized) emitted key. + */ +function wireName(program: Program, prop: Type & { name: string }): string { + return resolveEncodedName(program, prop, 'application/json') +} + +/** + * Fails the build when an emitted wire key is not recoverable from its public + * (camelized) form by the deterministic casing rule. The boundary mapper derives + * every wire key it does not carry an explicit name for via `toSnakeCase`, so a + * non-derivable name would silently ship a wrong key; this gate turns that into a + * codegen error. Covers body property names, query parameter names, and the + * discriminator/envelope keys of discriminated unions — every key the mapper or + * the URL serializer rewrites. + */ +export function assertCasingDerivable( + program: Program, + models: Model[], + operations: Operation[], +): void { + const tk = $(program) + const violations: string[] = [] + const check = (where: string, name: string): void => { + if (!isCasingDerivable(name)) { + violations.push(`${where}: '${name}' is not snake↔camel derivable`) + } + } + + for (const model of models) { + for (const prop of bodyProperties(program, model)) { + check( + `${model.name}.${prop.name}`, + wireName(program, prop as Type & { name: string }), + ) + } + } + + for (const op of operations) { + const httpOp = tk.httpOperation.get(op) + for (const param of httpOp.parameters.parameters) { + if (param.type === 'query') { + check(`${op.name} query`, param.name) + } + } + } + + // Unions the boundary mapper actually walks: those reachable from a request body + // or a success response. Error-envelope unions (e.g. `InvalidParameter` via + // `badRequest`) are excluded — they are consumed by `to-error.ts`, never mapped. + const mappedUnions = mappedReachableUnions(program, operations) + const ambiguousUnions: string[] = [] + for (const union of userUnions(program)) { + const discriminated = tk.union.getDiscriminatedUnion(union) + if (!discriminated) { + // A non-discriminated union of two or more object variants has no key the + // mapper can use to pick a variant; it would have to guess from the data's + // key set at runtime. The mapper deliberately does not — so fail the build, + // forcing the union to be `@discriminated` (scalar-vs-object unions are fine, + // the mapper distinguishes those by JS type). + if (mappedUnions.has(union) && objectVariantCount(program, union) >= 2) { + ambiguousUnions.push(union.name ?? '') + } + continue + } + check( + `${union.name ?? 'union'} discriminator`, + discriminated.options.discriminatorPropertyName, + ) + if (discriminated.options.envelope === 'object') { + check( + `${union.name ?? 'union'} envelope`, + discriminated.options.envelopePropertyName, + ) + } + } + + if (ambiguousUnions.length > 0) { + throw new Error( + `camelCase SDK: ${ambiguousUnions.length} non-discriminated union(s) with ` + + `multiple object variants cannot be mapped (the wire mapper cannot pick a ` + + `variant). Add @discriminated.\n ${ambiguousUnions.join('\n ')}`, + ) + } + + if (violations.length > 0) { + throw new Error( + `camelCase SDK: ${violations.length} wire key(s) are not casing-derivable. ` + + `Add an @encodedName or an explicit override.\n ${violations.join('\n ')}`, + ) + } +} + +/** The number of a union's variants whose value is an object/model type. */ +function objectVariantCount(program: Program, union: Union): number { + const tk = $(program) + let count = 0 + for (const variant of union.variants.values()) { + const type = variant.type + if (type.kind === 'Model' && !tk.array.is(type) && !tk.record.is(type)) { + count++ + } + } + return count +} + +/** + * The unions the boundary mapper walks: those in the transitive closure of every + * operation's request body and success-response body. Error responses are excluded + * (their bodies are read by the error path, not mapped). + */ +function mappedReachableUnions( + program: Program, + operations: Operation[], +): Set { + const tk = $(program) + const unions = new Set() + const seen = new Set() + const visit = (type: Type | undefined): void => { + if (!type || seen.has(type)) { + return + } + seen.add(type) + switch (type.kind) { + case 'Union': + unions.add(type) + for (const variant of type.variants.values()) { + visit(variant.type) + } + break + case 'Model': + if (type.indexer) { + visit(type.indexer.value) + } + if (type.baseModel) { + visit(type.baseModel) + } + for (const prop of type.properties.values()) { + visit(prop.type) + } + break + case 'Tuple': + for (const value of type.values) { + visit(value) + } + break + default: + break + } + } + + for (const op of operations) { + const httpOp = tk.httpOperation.get(op) + visit(httpOp.parameters.body?.type) + for (const response of httpOp.responses) { + if (!isSuccessStatus(response.statusCodes)) { + continue + } + for (const content of response.responses) { + visit(content.body?.type) + } + } + } + return unions +} + +function isSuccessStatus( + statusCodes: number | '*' | { start: number; end: number }, +): boolean { + if (statusCodes === '*') { + return false + } + if (typeof statusCodes === 'number') { + return statusCodes >= 200 && statusCodes < 300 + } + return statusCodes.start >= 200 && statusCodes.start < 300 +} + +function userUnions(program: Program): Union[] { + const tk = $(program) + const globalNs = program.getGlobalNamespaceType() + const result: Union[] = [] + const walk = (ns: Namespace): void => { + if (ns !== globalNs && !tk.type.isUserDefined(ns)) { + return + } + result.push(...ns.unions.values()) + ns.namespaces.forEach(walk) + } + walk(globalNs) + return result +} diff --git a/api/spec/packages/typespec-typescript/src/casing.ts b/api/spec/packages/typespec-typescript/src/casing.ts new file mode 100644 index 0000000000..ec9e2966b9 --- /dev/null +++ b/api/spec/packages/typespec-typescript/src/casing.ts @@ -0,0 +1,18 @@ +export function toCamelCase(name: string): string { + return name.replace(/_([a-z0-9])/g, (_m, c: string) => c.toUpperCase()) +} + +export function toSnakeCase(name: string): string { + return name.replace(/([A-Z])/g, (_m, c: string) => `_${c.toLowerCase()}`) +} + +/** + * True when a name survives the camel→snake→camel round-trip, i.e. its wire form + * is recoverable from its public (camelized) form by {@link toSnakeCase} alone. + * The boundary mapper relies on this for every key it does not carry an explicit + * wire name for; the codegen gate asserts it over every emitted wire key so a + * non-derivable name fails the build instead of silently shipping a wrong key. + */ +export function isCasingDerivable(wireName: string): boolean { + return toSnakeCase(toCamelCase(wireName)) === wireName +} diff --git a/api/spec/packages/typespec-typescript/src/components/ZodSchema.tsx b/api/spec/packages/typespec-typescript/src/components/ZodSchema.tsx index 2f011e3435..2eaacc1c31 100644 --- a/api/spec/packages/typespec-typescript/src/components/ZodSchema.tsx +++ b/api/spec/packages/typespec-typescript/src/components/ZodSchema.tsx @@ -2,7 +2,7 @@ import { type Children, refkey } from '@alloy-js/core' import { MemberExpression } from '@alloy-js/typescript' import type { Type } from '@typespec/compiler' import { useTsp } from '@typespec/emitter-framework' -import { refkeySym, shouldReference } from '../utils.jsx' +import { activeRefkeySym, shouldReference, useWireMode } from '../utils.jsx' import { zodBaseSchemaParts } from '../zodBaseSchema.jsx' import { zodConstraintsParts } from '../zodConstraintsParts.jsx' import { zodDescriptionParts } from '../zodDescriptionParts.jsx' @@ -19,6 +19,7 @@ export interface ZodSchemaProps { */ export function ZodSchema(props: ZodSchemaProps): Children { const { $ } = useTsp() + const rkSym = activeRefkeySym(useWireMode()) if (!props.nested) { return ( @@ -38,7 +39,7 @@ export function ZodSchema(props: ZodSchemaProps): Children { return ( - + {zodMemberParts(member)} diff --git a/api/spec/packages/typespec-typescript/src/components/ZodSchemaDeclaration.tsx b/api/spec/packages/typespec-typescript/src/components/ZodSchemaDeclaration.tsx index 1ab984486d..21042956db 100644 --- a/api/spec/packages/typespec-typescript/src/components/ZodSchemaDeclaration.tsx +++ b/api/spec/packages/typespec-typescript/src/components/ZodSchemaDeclaration.tsx @@ -2,7 +2,11 @@ import * as ay from '@alloy-js/core' import * as ts from '@alloy-js/typescript' import { getFriendlyName } from '@typespec/compiler' import { useTsp } from '@typespec/emitter-framework' -import { DeclaringTypeContext, refkeySym } from '../utils.jsx' +import { + activeRefkeySym, + DeclaringTypeContext, + useWireMode, +} from '../utils.jsx' import { ZodCustomTypeComponent } from './ZodCustomTypeComponent.jsx' import { ZodSchema, type ZodSchemaProps } from './ZodSchema.jsx' @@ -18,7 +22,7 @@ interface ZodSchemaDeclarationProps */ export function ZodSchemaDeclaration(props: ZodSchemaDeclarationProps) { const { $ } = useTsp() - const internalRk = ay.refkey(props.type, refkeySym) + const internalRk = ay.refkey(props.type, activeRefkeySym(useWireMode())) const [zodSchemaProps, varDeclProps] = ay.splitProps(props, [ 'type', 'nested', diff --git a/api/spec/packages/typespec-typescript/src/emitter.tsx b/api/spec/packages/typespec-typescript/src/emitter.tsx index a107d36252..ba83623fa8 100644 --- a/api/spec/packages/typespec-typescript/src/emitter.tsx +++ b/api/spec/packages/typespec-typescript/src/emitter.tsx @@ -19,8 +19,10 @@ import { zod } from './external-packages/zod.js' import type { ZodEmitterOptions } from './lib.js' import { collectHttpOperations, operationSchemas } from './ZodOperations.jsx' import { resolveStrippedNames } from './strip-prefixes.js' -import { newTopologicalTypeCollector } from './utils.jsx' +import { newTopologicalTypeCollector, WireModeContext } from './utils.jsx' import { RUNTIME_TEMPLATES } from './runtime-templates.js' +import { assertCasingDerivable } from './casing-gate.js' +import { WIRE_RUNTIME } from './wire-runtime.js' import { interfacesFile } from './interface-types.js' import { inputVariantName } from './input-variants.js' import { @@ -74,8 +76,9 @@ export async function $onEmit(context: EmitContext) { computeResponseReachableModels(context.program, operations), ) + const bodyOverrides = jsonBodyOverrides(context.program) const opSchemas = operations.flatMap((op) => - operationSchemas(context.program, op), + operationSchemas(context.program, op, bodyOverrides), ) // Pre-pass: collision-guarded resolved names so a strip is only applied when @@ -95,6 +98,12 @@ export async function $onEmit(context: EmitContext) { return base ? (resolved.get(base) ?? base) : undefined } const models = types.filter((t): t is Model => t.kind === 'Model') + + // Fail the build if any wire key is not recoverable from its camelCase public + // form by the deterministic casing rule, before emitting anything that relies + // on it. + assertCasingDerivable(context.program, models, operations) + const interfaceName = (name: string) => tsNamePolicy.getName(name, 'interface') const interfaces = interfacesFile( @@ -145,7 +154,6 @@ export async function $onEmit(context: EmitContext) { const groups = groupOperations(operations) const resources = [...groups.keys()] - const bodyOverrides = jsonBodyOverrides(context.program) const sdkFiles: Array<{ path: string; content: string }> = [] const readmeResources: ReadmeResource[] = [] for (const [resource, ops] of groups) { @@ -188,6 +196,7 @@ export async function $onEmit(context: EmitContext) { content: facadeFile(resource, sdkOps), }) } + sdkFiles.push({ path: 'src/lib/wire.ts', content: WIRE_RUNTIME }) sdkFiles.push({ path: 'src/models/types.ts', content: interfaces.types }) sdkFiles.push({ path: 'src/models/types.assert.ts', @@ -251,6 +260,54 @@ export async function $onEmit(context: EmitContext) { schema.render(resolved.get(schema.baseName) ?? schema.baseName) } + ; + + {/* The snake_case wire pass: the same models and per-op body/response + schemas re-emitted strict for the optional `validate` option. Because + both come from one walk over the same types, they are structurally + identical except for casing and strictness. */} + + + ; + + + } + > + {(type) => { + const base = baseName(context.program, type) + const name = base ? resolved.get(base) : undefined + return ( + + ) + }} + + ; + + + ; + + + } + > + {(schema) => + schema.render( + `${resolved.get(schema.baseName) ?? schema.baseName}Wire`, + ) + } + + {Object.entries(RUNTIME_TEMPLATES).map(([path, content]) => ( {content} diff --git a/api/spec/packages/typespec-typescript/src/interface-types.ts b/api/spec/packages/typespec-typescript/src/interface-types.ts index 0f0e188389..f486facdfa 100644 --- a/api/spec/packages/typespec-typescript/src/interface-types.ts +++ b/api/spec/packages/typespec-typescript/src/interface-types.ts @@ -1,6 +1,6 @@ import { type Model, type Program, type Type } from '@typespec/compiler' import { $ } from '@typespec/compiler/typekit' -import { bodyProperties, jsdoc } from './utils.jsx' +import { bodyProperties, jsdoc, publicPropertyName } from './utils.jsx' import { type IoMode, type RefName, isOptional, tsTypeOf } from './ts-types.js' import { computeDivergentModels, inputVariantName } from './input-variants.js' @@ -54,7 +54,7 @@ function interfaceBody( } const opt = isOptional(prop, io) ? '?' : '' lines.push( - ` ${prop.name}${opt}: ${tsTypeOf(program, prop.type, refName, io)}`, + ` ${publicPropertyName(program, prop)}${opt}: ${tsTypeOf(program, prop.type, refName, io)}`, ) } // An indexer (`...Record<...>`) makes the model open; mirror it with an index diff --git a/api/spec/packages/typespec-typescript/src/readme.ts b/api/spec/packages/typespec-typescript/src/readme.ts index 1d954ba1cc..91994fe3f6 100644 --- a/api/spec/packages/typespec-typescript/src/readme.ts +++ b/api/spec/packages/typespec-typescript/src/readme.ts @@ -159,8 +159,8 @@ function usage(packageName: string): string { " name: 'Tokens',", " key: 'tokens',", " aggregation: 'sum',", - " event_type: 'request',", - " value_property: '$.tokens',", + " eventType: 'request',", + " valueProperty: '$.tokens',", '})', '', 'const meters = await client.meters.list()', diff --git a/api/spec/packages/typespec-typescript/src/request-types.ts b/api/spec/packages/typespec-typescript/src/request-types.ts index 88590f41ca..f88c9dd8ed 100644 --- a/api/spec/packages/typespec-typescript/src/request-types.ts +++ b/api/spec/packages/typespec-typescript/src/request-types.ts @@ -8,7 +8,7 @@ import { $ } from '@typespec/compiler/typekit' import { operationBaseName } from './ZodOperations.jsx' import { type RefName, isOptional, tsTypeOf } from './ts-types.js' import type { SdkOperation } from './sdk-operations.js' -import { jsdoc } from './utils.jsx' +import { jsdoc, toCamelCase } from './utils.jsx' interface QueryLeaf { name: string @@ -47,7 +47,7 @@ function queryType( } const opt = isOptional(prop, 'input') ? '?' : '' lines.push( - ` ${name}${opt}: ${tsTypeOf(program, prop.type, refNameInput, 'input')}`, + ` ${toCamelCase(name)}${opt}: ${tsTypeOf(program, prop.type, refNameInput, 'input')}`, ) } return `export interface ${base}Query {\n${lines.join('\n')}\n}` diff --git a/api/spec/packages/typespec-typescript/src/runtime-templates.ts b/api/spec/packages/typespec-typescript/src/runtime-templates.ts index b69ff7c49c..7d5d6afb42 100644 --- a/api/spec/packages/typespec-typescript/src/runtime-templates.ts +++ b/api/spec/packages/typespec-typescript/src/runtime-templates.ts @@ -8,9 +8,9 @@ const ENCODED: Record = { 'src/lib/types.ts': 'aW1wb3J0IHR5cGUgeyBPcHRpb25zIH0gZnJvbSAna3knCgpleHBvcnQgdHlwZSBSZXF1ZXN0T3B0aW9ucyA9IFBpY2s8CiAgT3B0aW9ucywKICAnc2lnbmFsJyB8ICdoZWFkZXJzJyB8ICd0aW1lb3V0JyB8ICdyZXRyeScKPgoKZXhwb3J0IHR5cGUgUmVzdWx0PFQsIEUgPSBFcnJvcj4gPQogIHwgeyBvazogdHJ1ZTsgdmFsdWU6IFQ7IGVycm9yPzogbmV2ZXIgfQogIHwgeyBvazogZmFsc2U7IHZhbHVlPzogbmV2ZXI7IGVycm9yOiBFIH0KCmV4cG9ydCBmdW5jdGlvbiBvazxUPih2YWx1ZTogVCk6IFJlc3VsdDxULCBuZXZlcj4gewogIHJldHVybiB7IG9rOiB0cnVlLCB2YWx1ZSB9Cn0KCmV4cG9ydCBmdW5jdGlvbiBlcnI8RT4oZXJyb3I6IEUpOiBSZXN1bHQ8bmV2ZXIsIEU+IHsKICByZXR1cm4geyBvazogZmFsc2UsIGVycm9yIH0KfQoKZXhwb3J0IGZ1bmN0aW9uIHVud3JhcDxULCBFPihyZXN1bHQ6IFJlc3VsdDxULCBFPik6IFQgewogIGlmIChyZXN1bHQub2spIHsKICAgIHJldHVybiByZXN1bHQudmFsdWUKICB9CiAgdGhyb3cgcmVzdWx0LmVycm9yCn0K', 'src/lib/config.ts': - 'aW1wb3J0IHsgdHlwZSBPcHRpb25zIH0gZnJvbSAna3knCgpleHBvcnQgY29uc3QgU2VydmVyTGlzdCA9IFsKICAnaHR0cHM6Ly97cmVnaW9ufS5hcGkua29uZ2hxLmNvbS92MycsCiAgJ2h0dHA6Ly9sb2NhbGhvc3Q6e3BvcnR9L2FwaS92MycsCiAgJ2h0dHBzOi8vb3Blbm1ldGVyLmNsb3VkL2FwaS92MycsCl0gYXMgY29uc3QKCmV4cG9ydCBjb25zdCBSZWdpb25zID0gWwogICdpbicsCiAgJ21lJywKICAnYXUnLAogICdldScsCiAgJ3VzJywKXSBhcyBjb25zdAoKZXhwb3J0IHR5cGUgUmVnaW9uID0gKHR5cGVvZiBSZWdpb25zKVtudW1iZXJdCgpleHBvcnQgdHlwZSBTZXJ2ZXJWYXJpYWJsZXMgPSB7CiAgcmVnaW9uPzogUmVnaW9uCiAgcG9ydD86IHN0cmluZyB8IG51bWJlcgp9CgpleHBvcnQgaW50ZXJmYWNlIFNES09wdGlvbnMgZXh0ZW5kcyBPbWl0PE9wdGlvbnMsICdtZXRob2QnPiB7CiAgYmFzZVVybDogKHR5cGVvZiBTZXJ2ZXJMaXN0KVtudW1iZXJdIHwgVVJMIHwgc3RyaW5nCiAgc2VydmVyVmFyaWFibGVzPzogU2VydmVyVmFyaWFibGVzCiAgYXBpS2V5Pzogc3RyaW5nIHwgKCgpID0+IHN0cmluZyB8IFByb21pc2U8c3RyaW5nPikKfQo=', + 'aW1wb3J0IHsgdHlwZSBPcHRpb25zIH0gZnJvbSAna3knCgpleHBvcnQgY29uc3QgU2VydmVyTGlzdCA9IFsKICAnaHR0cHM6Ly97cmVnaW9ufS5hcGkua29uZ2hxLmNvbS92MycsCiAgJ2h0dHA6Ly9sb2NhbGhvc3Q6e3BvcnR9L2FwaS92MycsCiAgJ2h0dHBzOi8vb3Blbm1ldGVyLmNsb3VkL2FwaS92MycsCl0gYXMgY29uc3QKCmV4cG9ydCBjb25zdCBSZWdpb25zID0gWwogICdpbicsCiAgJ21lJywKICAnYXUnLAogICdldScsCiAgJ3VzJywKXSBhcyBjb25zdAoKZXhwb3J0IHR5cGUgUmVnaW9uID0gKHR5cGVvZiBSZWdpb25zKVtudW1iZXJdCgpleHBvcnQgdHlwZSBTZXJ2ZXJWYXJpYWJsZXMgPSB7CiAgcmVnaW9uPzogUmVnaW9uCiAgcG9ydD86IHN0cmluZyB8IG51bWJlcgp9CgpleHBvcnQgaW50ZXJmYWNlIFNES09wdGlvbnMgZXh0ZW5kcyBPbWl0PE9wdGlvbnMsICdtZXRob2QnPiB7CiAgYmFzZVVybDogKHR5cGVvZiBTZXJ2ZXJMaXN0KVtudW1iZXJdIHwgVVJMIHwgc3RyaW5nCiAgc2VydmVyVmFyaWFibGVzPzogU2VydmVyVmFyaWFibGVzCiAgYXBpS2V5Pzogc3RyaW5nIHwgKCgpID0+IHN0cmluZyB8IFByb21pc2U8c3RyaW5nPikKICAvKioKICAgKiBWYWxpZGF0ZSByZXF1ZXN0IGJvZGllcyBhbmQgcmVzcG9uc2UgcGF5bG9hZHMgYWdhaW5zdCB0aGVpciBzY2hlbWFzLiBPZmYgYnkKICAgKiBkZWZhdWx0OiB0aGUgU0RLIG1hcHMgY2FzaW5nIGJ1dCBkb2VzIG5vdCB2YWxpZGF0ZSwgc28gYWRkaXRpdmUgc2VydmVyIGZpZWxkcwogICAqIG5ldmVyIGJyZWFrIGNsaWVudHMuIFdoZW4gb24sIGEgcmVxdWVzdCBib2R5IG9yIHJlc3BvbnNlIHRoYXQgZmFpbHMgaXRzIHNjaGVtYQogICAqIChtaXNzaW5nL3dyb25nLXR5cGVkIGZpZWxkLCB1bmtub3duIGVudW0gdmFsdWUpIHJlamVjdHMgd2l0aCBhIFZhbGlkYXRpb25FcnJvci4KICAgKi8KICB2YWxpZGF0ZT86IGJvb2xlYW4KfQo=', 'src/lib/encodings.ts': - 'ZXhwb3J0IGZ1bmN0aW9uIGVuY29kZVBhdGgoCiAgdGVtcGxhdGU6IHN0cmluZywKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlcj4sCik6IHN0cmluZyB7CiAgcmV0dXJuIHRlbXBsYXRlLnJlcGxhY2UoL1x7KFx3KylcfS9nLCAoXywga2V5OiBzdHJpbmcpID0+IHsKICAgIGNvbnN0IHZhbHVlID0gcGFyYW1zW2tleV0KICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihgbWlzc2luZyBwYXRoIHBhcmFtZXRlcjogJHtrZXl9YCkKICAgIH0KICAgIHJldHVybiBlbmNvZGVVUklDb21wb25lbnQoU3RyaW5nKHZhbHVlKSkKICB9KQp9CgpmdW5jdGlvbiBzZXJpYWxpemVEZWVwT2JqZWN0KAogIHByZWZpeDogc3RyaW5nLAogIHZhbHVlOiB1bmtub3duLAogIHBhcnRzOiBBcnJheTxbc3RyaW5nLCBzdHJpbmddPiwKKTogdm9pZCB7CiAgaWYgKHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSB1bmRlZmluZWQpIHsKICAgIHJldHVybgogIH0KICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHsKICAgIHBhcnRzLnB1c2goW3ByZWZpeCwgdmFsdWUubWFwKFN0cmluZykuam9pbignLCcpXSkKICB9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHsKICAgIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKHZhbHVlKSkgewogICAgICBzZXJpYWxpemVEZWVwT2JqZWN0KGAke3ByZWZpeH1bJHtrfV1gLCB2LCBwYXJ0cykKICAgIH0KICB9IGVsc2UgewogICAgcGFydHMucHVzaChbcHJlZml4LCBTdHJpbmcodmFsdWUpXSkKICB9Cn0KCmZ1bmN0aW9uIHNlcmlhbGl6ZVBhcmFtcygKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LAopOiBBcnJheTxbc3RyaW5nLCBzdHJpbmddPiB7CiAgY29uc3QgcGFydHM6IEFycmF5PFtzdHJpbmcsIHN0cmluZ10+ID0gW10KICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwYXJhbXMpKSB7CiAgICBzZXJpYWxpemVEZWVwT2JqZWN0KGtleSwgdmFsdWUsIHBhcnRzKQogIH0KICByZXR1cm4gcGFydHMKfQoKZXhwb3J0IGZ1bmN0aW9uIHF1ZXJ5U2VyaWFsaXplcihwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogc3RyaW5nIHsKICBjb25zdCBwYXJ0cyA9IHNlcmlhbGl6ZVBhcmFtcyhwYXJhbXMpLm1hcCgKICAgIChbaywgdl0pID0+IGAke2VuY29kZVVSSUNvbXBvbmVudChrKX09JHtlbmNvZGVVUklDb21wb25lbnQodil9YCwKICApCiAgcmV0dXJuIHBhcnRzLmxlbmd0aCA/IGA/JHtwYXJ0cy5qb2luKCcmJyl9YCA6ICcnCn0KCmV4cG9ydCBmdW5jdGlvbiB0b1VSTFNlYXJjaFBhcmFtcygKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LAopOiBVUkxTZWFyY2hQYXJhbXMgewogIGNvbnN0IHNlYXJjaCA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKQogIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIHNlcmlhbGl6ZVBhcmFtcyhwYXJhbXMpKSB7CiAgICBzZWFyY2guYXBwZW5kKGtleSwgdmFsdWUpCiAgfQogIHJldHVybiBzZWFyY2gKfQoKZXhwb3J0IGZ1bmN0aW9uIGVuY29kZVNvcnQoCiAgc29ydDogeyBieT86IHN0cmluZzsgb3JkZXI/OiAnYXNjJyB8ICdkZXNjJyB9IHwgdW5kZWZpbmVkLAopOiBzdHJpbmcgfCB1bmRlZmluZWQgewogIGlmICghc29ydD8uYnkpIHsKICAgIHJldHVybiB1bmRlZmluZWQKICB9CiAgaWYgKHNvcnQub3JkZXIgPT09ICdkZXNjJykgewogICAgcmV0dXJuIGAke3NvcnQuYnl9IGRlc2NgCiAgfQogIGlmIChzb3J0Lm9yZGVyID09PSAnYXNjJykgewogICAgcmV0dXJuIGAke3NvcnQuYnl9IGFzY2AKICB9CiAgcmV0dXJuIHNvcnQuYnkKfQo=', + 'ZXhwb3J0IGZ1bmN0aW9uIGVuY29kZVBhdGgoCiAgdGVtcGxhdGU6IHN0cmluZywKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlcj4sCik6IHN0cmluZyB7CiAgcmV0dXJuIHRlbXBsYXRlLnJlcGxhY2UoL1x7KFx3KylcfS9nLCAoXywga2V5OiBzdHJpbmcpID0+IHsKICAgIGNvbnN0IHZhbHVlID0gcGFyYW1zW2tleV0KICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihgbWlzc2luZyBwYXRoIHBhcmFtZXRlcjogJHtrZXl9YCkKICAgIH0KICAgIHJldHVybiBlbmNvZGVVUklDb21wb25lbnQoU3RyaW5nKHZhbHVlKSkKICB9KQp9CgpmdW5jdGlvbiBzZXJpYWxpemVEZWVwT2JqZWN0KAogIHByZWZpeDogc3RyaW5nLAogIHZhbHVlOiB1bmtub3duLAogIHBhcnRzOiBBcnJheTxbc3RyaW5nLCBzdHJpbmddPiwKKTogdm9pZCB7CiAgaWYgKHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSB1bmRlZmluZWQpIHsKICAgIHJldHVybgogIH0KICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHsKICAgIHBhcnRzLnB1c2goW3ByZWZpeCwgdmFsdWUubWFwKFN0cmluZykuam9pbignLCcpXSkKICB9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHsKICAgIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKHZhbHVlKSkgewogICAgICBzZXJpYWxpemVEZWVwT2JqZWN0KGAke3ByZWZpeH1bJHtrfV1gLCB2LCBwYXJ0cykKICAgIH0KICB9IGVsc2UgewogICAgcGFydHMucHVzaChbcHJlZml4LCBTdHJpbmcodmFsdWUpXSkKICB9Cn0KCmZ1bmN0aW9uIHNlcmlhbGl6ZVBhcmFtcygKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LAopOiBBcnJheTxbc3RyaW5nLCBzdHJpbmddPiB7CiAgY29uc3QgcGFydHM6IEFycmF5PFtzdHJpbmcsIHN0cmluZ10+ID0gW10KICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwYXJhbXMpKSB7CiAgICBzZXJpYWxpemVEZWVwT2JqZWN0KGtleSwgdmFsdWUsIHBhcnRzKQogIH0KICByZXR1cm4gcGFydHMKfQoKZXhwb3J0IGZ1bmN0aW9uIHF1ZXJ5U2VyaWFsaXplcihwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogc3RyaW5nIHsKICBjb25zdCBwYXJ0cyA9IHNlcmlhbGl6ZVBhcmFtcyhwYXJhbXMpLm1hcCgKICAgIChbaywgdl0pID0+IGAke2VuY29kZVVSSUNvbXBvbmVudChrKX09JHtlbmNvZGVVUklDb21wb25lbnQodil9YCwKICApCiAgcmV0dXJuIHBhcnRzLmxlbmd0aCA/IGA/JHtwYXJ0cy5qb2luKCcmJyl9YCA6ICcnCn0KCmV4cG9ydCBmdW5jdGlvbiB0b1VSTFNlYXJjaFBhcmFtcygKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LAopOiBVUkxTZWFyY2hQYXJhbXMgewogIGNvbnN0IHNlYXJjaCA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKQogIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIHNlcmlhbGl6ZVBhcmFtcyhwYXJhbXMpKSB7CiAgICBzZWFyY2guYXBwZW5kKGtleSwgdmFsdWUpCiAgfQogIHJldHVybiBzZWFyY2gKfQoKZXhwb3J0IGZ1bmN0aW9uIGVuY29kZVNvcnQoCiAgc29ydDogeyBieT86IHN0cmluZzsgb3JkZXI/OiAnYXNjJyB8ICdkZXNjJyB9IHwgdW5kZWZpbmVkLAogIGVuY29kZUZpZWxkOiAoZmllbGQ6IHN0cmluZykgPT4gc3RyaW5nID0gKGZpZWxkKSA9PiBmaWVsZCwKKTogc3RyaW5nIHwgdW5kZWZpbmVkIHsKICBpZiAoIXNvcnQ/LmJ5KSB7CiAgICByZXR1cm4gdW5kZWZpbmVkCiAgfQogIGNvbnN0IGJ5ID0gZW5jb2RlRmllbGQoc29ydC5ieSkKICBpZiAoc29ydC5vcmRlciA9PT0gJ2Rlc2MnKSB7CiAgICByZXR1cm4gYCR7Ynl9IGRlc2NgCiAgfQogIGlmIChzb3J0Lm9yZGVyID09PSAnYXNjJykgewogICAgcmV0dXJuIGAke2J5fSBhc2NgCiAgfQogIHJldHVybiBieQp9Cg==', 'src/lib/to-error.ts': 'aW1wb3J0IHsgSFRUUEVycm9yIGFzIEt5SFRUUEVycm9yIH0gZnJvbSAna3knCmltcG9ydCAqIGFzIHNjaGVtYXMgZnJvbSAnLi4vbW9kZWxzL3NjaGVtYXMuanMnCmltcG9ydCB7IEhUVFBFcnJvciB9IGZyb20gJy4uL21vZGVscy9lcnJvcnMuanMnCgpleHBvcnQgYXN5bmMgZnVuY3Rpb24gdG9FcnJvcihlOiB1bmtub3duKTogUHJvbWlzZTxFcnJvcj4gewogIGlmIChlIGluc3RhbmNlb2YgS3lIVFRQRXJyb3IpIHsKICAgIC8vIGt5IGNvbnN1bWVzIHRoZSBib2R5IGludG8gZS5kYXRhOyBlLnJlc3BvbnNlLmpzb24oKSB3b3VsZCB0aHJvdy4KICAgIGNvbnN0IHBhcnNlZCA9IHNjaGVtYXMuYmFzZUVycm9yLnNhZmVQYXJzZShlLmRhdGEpCiAgICBjb25zdCBlcnJvciA9IHBhcnNlZC5zdWNjZXNzID8gcGFyc2VkLmRhdGEgOiB1bmRlZmluZWQKICAgIHJldHVybiBIVFRQRXJyb3IuZnJvbVJlc3BvbnNlKHsgcmVzcG9uc2U6IGUucmVzcG9uc2UsIGVycm9yIH0pCiAgfQogIGlmIChlIGluc3RhbmNlb2YgRXJyb3IpIHsKICAgIHJldHVybiBlCiAgfQogIHJldHVybiBuZXcgRXJyb3IoU3RyaW5nKGUpKQp9Cg==', 'src/lib/request.ts': @@ -20,7 +20,7 @@ const ENCODED: Record = { 'tests/client.spec.ts': 'aW1wb3J0IGZldGNoTW9jayBmcm9tICdAZmV0Y2gtbW9jay92aXRlc3QnCmltcG9ydCB7IGJlZm9yZUVhY2gsIGRlc2NyaWJlLCBleHBlY3QsIGl0IH0gZnJvbSAndml0ZXN0JwppbXBvcnQgeyBPcGVuTWV0ZXIsIFNlcnZlckxpc3QgfSBmcm9tICcuLi9zcmMvaW5kZXguanMnCgpjb25zdCBtZXRlciA9IHsKICBpZDogJzAxQVJaM05ERUtUU1Y0UlJGRlE2OUc1RkFWJywKICBuYW1lOiAnQVBJIGNhbGxzJywKICBrZXk6ICdhcGlfY2FsbHMnLAogIGFnZ3JlZ2F0aW9uOiAnY291bnQnLAogIGV2ZW50X3R5cGU6ICdhcGktcmVxdWVzdCcsCiAgY3JlYXRlZF9hdDogJzIwMjQtMDEtMDFUMDA6MDA6MDBaJywKICB1cGRhdGVkX2F0OiAnMjAyNC0wMS0wMVQwMDowMDowMFonLAp9CgpiZWZvcmVFYWNoKCgpID0+IHsKICBmZXRjaE1vY2subW9ja1Jlc2V0KCkKfSkKCmZ1bmN0aW9uIG1vY2tNZXRlcigpIHsKICBmZXRjaE1vY2sucm91dGUoJyonLCB7IGJvZHk6IG1ldGVyLCBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSB9KQp9CgpmdW5jdGlvbiBsYXN0VXJsKCk6IHN0cmluZyB7CiAgcmV0dXJuIGZldGNoTW9jay5jYWxsSGlzdG9yeS5sYXN0Q2FsbCgpIS51cmwKfQoKZnVuY3Rpb24gbGFzdEF1dGgoKTogc3RyaW5nIHwgbnVsbCB7CiAgY29uc3QgaGVhZGVycyA9IGZldGNoTW9jay5jYWxsSGlzdG9yeS5sYXN0Q2FsbCgpIS5vcHRpb25zLmhlYWRlcnMgYXMgSGVhZGVycwogIHJldHVybiBuZXcgSGVhZGVycyhoZWFkZXJzKS5nZXQoJ2F1dGhvcml6YXRpb24nKQp9Cgpjb25zdCBmZXRjaCA9IGZldGNoTW9jay5mZXRjaEhhbmRsZXIKCmRlc2NyaWJlKCdiYXNlIFVSTCBjb25zdHJ1Y3Rpb24nLCAoKSA9PiB7CiAgaXQoJ3ByZXNlcnZlcyB0aGUgYmFzZSBwYXRoIHNlZ21lbnQgKC92MyknLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTWV0ZXIoKQogICAgY29uc3Qgc2RrID0gbmV3IE9wZW5NZXRlcih7IGJhc2VVcmw6ICdodHRwczovL2V1LmFwaS5rb25naHEuY29tL3YzJywgYXBpS2V5OiAnaycsIGZldGNoIH0pCiAgICBhd2FpdCBzZGsubWV0ZXJzLmdldCh7IG1ldGVySWQ6ICdtJyB9KQogICAgZXhwZWN0KGxhc3RVcmwoKSkudG9CZSgnaHR0cHM6Ly9ldS5hcGkua29uZ2hxLmNvbS92My9vcGVubWV0ZXIvbWV0ZXJzL20nKQogIH0pCgogIGl0KCdhY2NlcHRzIGEgVVJMIG9iamVjdCBhcyBiYXNlVXJsJywgYXN5bmMgKCkgPT4gewogICAgbW9ja01ldGVyKCkKICAgIGNvbnN0IHNkayA9IG5ldyBPcGVuTWV0ZXIoeyBiYXNlVXJsOiBuZXcgVVJMKCdodHRwczovL3VzLmFwaS5rb25naHEuY29tL3YzJyksIGFwaUtleTogJ2snLCBmZXRjaCB9KQogICAgYXdhaXQgc2RrLm1ldGVycy5nZXQoeyBtZXRlcklkOiAnbScgfSkKICAgIGV4cGVjdChsYXN0VXJsKCkpLnRvQmUoJ2h0dHBzOi8vdXMuYXBpLmtvbmdocS5jb20vdjMvb3Blbm1ldGVyL21ldGVycy9tJykKICB9KQoKICBpdCgnc2V0cyB0aGUgYmVhcmVyIGF1dGggaGVhZGVyJywgYXN5bmMgKCkgPT4gewogICAgbW9ja01ldGVyKCkKICAgIGNvbnN0IHNkayA9IG5ldyBPcGVuTWV0ZXIoeyBiYXNlVXJsOiAnaHR0cHM6Ly9ldS5hcGkua29uZ2hxLmNvbS92MycsIGFwaUtleTogJ2snLCBmZXRjaCB9KQogICAgYXdhaXQgc2RrLm1ldGVycy5nZXQoeyBtZXRlcklkOiAnbScgfSkKICAgIGV4cGVjdChsYXN0QXV0aCgpKS50b0JlKCdCZWFyZXIgaycpCiAgfSkKfSkKCmRlc2NyaWJlKCdzZXJ2ZXItdmFyaWFibGUgdGVtcGxhdGluZycsICgpID0+IHsKICBpdCgnc3Vic3RpdHV0ZXMgYSByZWdpb24gdmFyaWFibGUnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTWV0ZXIoKQogICAgY29uc3Qgc2RrID0gbmV3IE9wZW5NZXRlcih7CiAgICAgIGJhc2VVcmw6IFNlcnZlckxpc3RbMF0sCiAgICAgIHNlcnZlclZhcmlhYmxlczogeyByZWdpb246ICdldScgfSwKICAgICAgYXBpS2V5OiAnaycsCiAgICAgIGZldGNoLAogICAgfSkKICAgIGF3YWl0IHNkay5tZXRlcnMuZ2V0KHsgbWV0ZXJJZDogJ20nIH0pCiAgICBleHBlY3QobGFzdFVybCgpKS50b0JlKCdodHRwczovL2V1LmFwaS5rb25naHEuY29tL3YzL29wZW5tZXRlci9tZXRlcnMvbScpCiAgfSkKCiAgaXQoJ3Rocm93cyB3aGVuIGEgcmVxdWlyZWQgdGVtcGxhdGUgdmFyaWFibGUgaXMgbWlzc2luZycsICgpID0+IHsKICAgIGV4cGVjdCgoKSA9PiBuZXcgT3Blbk1ldGVyKHsgYmFzZVVybDogU2VydmVyTGlzdFswXSwgYXBpS2V5OiAnaycsIGZldGNoIH0pKS50b1Rocm93KCkKICB9KQp9KQoKZGVzY3JpYmUoJ29wdGlvbiBjbG9iYmVyaW5nIGlzIHByZXZlbnRlZCcsICgpID0+IHsKICBpdCgnaWdub3JlcyBhIHVzZXItc3VwcGxpZWQgcHJlZml4IHRoYXQgd291bGQgcmVkaXJlY3QgcmVxdWVzdHMnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTWV0ZXIoKQogICAgY29uc3Qgc2RrID0gbmV3IE9wZW5NZXRlcih7CiAgICAgIGJhc2VVcmw6ICdodHRwczovL2V1LmFwaS5rb25naHEuY29tL3YzJywKICAgICAgYXBpS2V5OiAnaycsCiAgICAgIHByZWZpeDogJ2h0dHA6Ly9ldmlsLnRlc3QvJywKICAgICAgZmV0Y2gsCiAgICB9KQogICAgYXdhaXQgc2RrLm1ldGVycy5nZXQoeyBtZXRlcklkOiAnbScgfSkKICAgIGV4cGVjdChsYXN0VXJsKCkpLnRvQmUoJ2h0dHBzOi8vZXUuYXBpLmtvbmdocS5jb20vdjMvb3Blbm1ldGVyL21ldGVycy9tJykKICB9KQoKICBpdCgnYXBwbGllcyBTREsgYXV0aCBhZnRlciB1c2VyIGJlZm9yZVJlcXVlc3QgaG9va3MnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTWV0ZXIoKQogICAgY29uc3Qgc2RrID0gbmV3IE9wZW5NZXRlcih7CiAgICAgIGJhc2VVcmw6ICdodHRwczovL2V1LmFwaS5rb25naHEuY29tL3YzJywKICAgICAgYXBpS2V5OiAncmVhbC1rZXknLAogICAgICBmZXRjaCwKICAgICAgaG9va3M6IHsKICAgICAgICBiZWZvcmVSZXF1ZXN0OiBbCiAgICAgICAgICAoeyByZXF1ZXN0IH0pID0+IHsKICAgICAgICAgICAgcmVxdWVzdC5oZWFkZXJzLnNldCgnQXV0aG9yaXphdGlvbicsICdCZWFyZXIgQVRUQUNLRVInKQogICAgICAgICAgfSwKICAgICAgICBdLAogICAgICB9LAogICAgfSkKICAgIGF3YWl0IHNkay5tZXRlcnMuZ2V0KHsgbWV0ZXJJZDogJ20nIH0pCiAgICBleHBlY3QobGFzdEF1dGgoKSkudG9CZSgnQmVhcmVyIHJlYWwta2V5JykKICB9KQp9KQoKZGVzY3JpYmUoJ25hbWVzcGFjZSBjb21wb3NpdGlvbicsICgpID0+IHsKICBpdCgnbWVtb2l6ZXMgbmFtZXNwYWNlIGFjY2Vzc29ycycsICgpID0+IHsKICAgIGNvbnN0IHNkayA9IG5ldyBPcGVuTWV0ZXIoeyBiYXNlVXJsOiAnaHR0cHM6Ly9ldS5hcGkua29uZ2hxLmNvbS92MycsIGFwaUtleTogJ2snLCBmZXRjaCB9KQogICAgZXhwZWN0KHNkay5tZXRlcnMpLnRvQmUoc2RrLm1ldGVycykKICB9KQoKICBpdCgncm91dGVzIG5hbWVzcGFjZSBjYWxscyB0aHJvdWdoIHRoZSByb290IHRyYW5zcG9ydCcsIGFzeW5jICgpID0+IHsKICAgIG1vY2tNZXRlcigpCiAgICBjb25zdCBzZGsgPSBuZXcgT3Blbk1ldGVyKHsgYmFzZVVybDogJ2h0dHBzOi8vZXUuYXBpLmtvbmdocS5jb20vdjMnLCBhcGlLZXk6ICdrJywgZmV0Y2ggfSkKICAgIGF3YWl0IHNkay5tZXRlcnMuZ2V0KHsgbWV0ZXJJZDogJ20nIH0pCiAgICBleHBlY3QobGFzdFVybCgpKS50b0JlKCdodHRwczovL2V1LmFwaS5rb25naHEuY29tL3YzL29wZW5tZXRlci9tZXRlcnMvbScpCiAgICBleHBlY3QobGFzdEF1dGgoKSkudG9CZSgnQmVhcmVyIGsnKQogIH0pCn0pCg==', 'tests/meters.spec.ts': - 'aW1wb3J0IGZldGNoTW9jayBmcm9tICdAZmV0Y2gtbW9jay92aXRlc3QnCmltcG9ydCB7IGJlZm9yZUVhY2gsIGRlc2NyaWJlLCBleHBlY3QsIGl0IH0gZnJvbSAndml0ZXN0JwppbXBvcnQgeyBDbGllbnQsIGZ1bmNzIH0gZnJvbSAnLi4vc3JjL2luZGV4LmpzJwoKY29uc3QgbWV0ZXIgPSB7CiAgaWQ6ICcwMUFSWjNOREVLVFNWNFJSRkZRNjlHNUZBVicsCiAgbmFtZTogJ0FQSSBjYWxscycsCiAga2V5OiAnYXBpX2NhbGxzJywKICBhZ2dyZWdhdGlvbjogJ2NvdW50JywKICBldmVudF90eXBlOiAnYXBpLXJlcXVlc3QnLAogIGNyZWF0ZWRfYXQ6ICcyMDI0LTAxLTAxVDAwOjAwOjAwWicsCiAgdXBkYXRlZF9hdDogJzIwMjQtMDEtMDFUMDA6MDA6MDBaJywKfQoKYmVmb3JlRWFjaCgoKSA9PiB7CiAgZmV0Y2hNb2NrLm1vY2tSZXNldCgpCn0pCgpmdW5jdGlvbiBjbGllbnQoKSB7CiAgcmV0dXJuIG5ldyBDbGllbnQoewogICAgYmFzZVVybDogJ2h0dHBzOi8vZXUuYXBpLmtvbmdocS5jb20vdjMnLAogICAgYXBpS2V5OiAnaycsCiAgICBmZXRjaDogZmV0Y2hNb2NrLmZldGNoSGFuZGxlciwKICB9KQp9CgpmdW5jdGlvbiBtb2NrTGlzdCgpIHsKICBmZXRjaE1vY2sucm91dGUoJyonLCB7CiAgICBib2R5OiB7IGRhdGE6IFttZXRlcl0sIG1ldGE6IHsgcGFnZTogeyBudW1iZXI6IDEsIHNpemU6IDEwLCB0b3RhbDogMSB9IH0gfSwKICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LAogIH0pCn0KCmZ1bmN0aW9uIGxhc3RRdWVyeSgpOiBVUkxTZWFyY2hQYXJhbXMgewogIHJldHVybiBuZXcgVVJMKGZldGNoTW9jay5jYWxsSGlzdG9yeS5sYXN0Q2FsbCgpIS51cmwpLnNlYXJjaFBhcmFtcwp9CgpkZXNjcmliZSgnbGlzdE1ldGVycyBxdWVyeSBzZXJpYWxpemF0aW9uJywgKCkgPT4gewogIGl0KCdlbmNvZGVzIHBhZ2UgYXMgYSBkZWVwIG9iamVjdCcsIGFzeW5jICgpID0+IHsKICAgIG1vY2tMaXN0KCkKICAgIGF3YWl0IGZ1bmNzLmxpc3RNZXRlcnMoY2xpZW50KCksIHsgcGFnZTogeyBzaXplOiAxMCwgbnVtYmVyOiAyIH0gfSkKICAgIGNvbnN0IHEgPSBsYXN0UXVlcnkoKQogICAgZXhwZWN0KHEuZ2V0KCdwYWdlW3NpemVdJykpLnRvQmUoJzEwJykKICAgIGV4cGVjdChxLmdldCgncGFnZVtudW1iZXJdJykpLnRvQmUoJzInKQogIH0pCgogIGl0KCdlbmNvZGVzIGEgc2NhbGFyIGZpbHRlcicsIGFzeW5jICgpID0+IHsKICAgIG1vY2tMaXN0KCkKICAgIGF3YWl0IGZ1bmNzLmxpc3RNZXRlcnMoY2xpZW50KCksIHsgZmlsdGVyOiB7IGtleTogJ20nIH0gfSkKICAgIGV4cGVjdChsYXN0UXVlcnkoKS5nZXQoJ2ZpbHRlcltrZXldJykpLnRvQmUoJ20nKQogIH0pCgogIGl0KCdlbmNvZGVzIGEgbmVzdGVkIGZpbHRlciBvcGVyYW5kIGFzIGEgZGVlcCBvYmplY3QnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTGlzdCgpCiAgICBhd2FpdCBmdW5jcy5saXN0TWV0ZXJzKGNsaWVudCgpLCB7IGZpbHRlcjogeyBrZXk6IHsgZXE6ICdmb28nIH0gfSB9KQogICAgZXhwZWN0KGxhc3RRdWVyeSgpLmdldCgnZmlsdGVyW2tleV1bZXFdJykpLnRvQmUoJ2ZvbycpCiAgfSkKCiAgaXQoJ2NvbW1hLWpvaW5zIGFycmF5IG9wZXJhbmRzIGludG8gYSBzaW5nbGUgcGFyYW1ldGVyJywgYXN5bmMgKCkgPT4gewogICAgbW9ja0xpc3QoKQogICAgYXdhaXQgZnVuY3MubGlzdE1ldGVycyhjbGllbnQoKSwgeyBmaWx0ZXI6IHsga2V5OiB7IG9lcTogWydhJywgJ2InLCAnYyddIH0gfSB9KQogICAgY29uc3QgcSA9IGxhc3RRdWVyeSgpCiAgICBleHBlY3QocS5nZXRBbGwoJ2ZpbHRlcltrZXldW29lcV0nKSkudG9IYXZlTGVuZ3RoKDEpCiAgICBleHBlY3QocS5nZXQoJ2ZpbHRlcltrZXldW29lcV0nKSkudG9CZSgnYSxiLGMnKQogIH0pCgogIGl0KCdzZXJpYWxpemVzIHNvcnQgYXMgYSBwbGFpbiBzdHJpbmcnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTGlzdCgpCiAgICBhd2FpdCBmdW5jcy5saXN0TWV0ZXJzKGNsaWVudCgpLCB7IHNvcnQ6IHsgYnk6ICdjcmVhdGVkX2F0Jywgb3JkZXI6ICdkZXNjJyB9IH0pCiAgICBjb25zdCBxID0gbGFzdFF1ZXJ5KCkKICAgIGV4cGVjdChxLmdldCgnc29ydCcpKS50b0JlKCdjcmVhdGVkX2F0IGRlc2MnKQogICAgZXhwZWN0KHEuZ2V0KCdzb3J0W2J5XScpKS50b0JlTnVsbCgpCiAgfSkKCiAgaXQoJ29taXRzIHNvcnQgb3JkZXIgd2hlbiBhc2NlbmRpbmcgaXMgaW1wbGllZCcsIGFzeW5jICgpID0+IHsKICAgIG1vY2tMaXN0KCkKICAgIGF3YWl0IGZ1bmNzLmxpc3RNZXRlcnMoY2xpZW50KCksIHsgc29ydDogeyBieTogJ25hbWUnIH0gfSkKICAgIGV4cGVjdChsYXN0UXVlcnkoKS5nZXQoJ3NvcnQnKSkudG9CZSgnbmFtZScpCiAgfSkKfSkKCmRlc2NyaWJlKCdyZXNwb25zZXMgYXJlIG5vdCB2YWxpZGF0ZWQnLCAoKSA9PiB7CiAgaXQoJ3Bhc3NlcyB0aHJvdWdoIGFkZGl0aXZlIHJlc3BvbnNlIGZpZWxkcyB1bnRvdWNoZWQnLCBhc3luYyAoKSA9PiB7CiAgICBmZXRjaE1vY2sucm91dGUoJyonLCB7CiAgICAgIGJvZHk6IHsgLi4ubWV0ZXIsIGJyYW5kX25ld19maWVsZDogJ2tlcHQnIH0sCiAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LAogICAgfSkKICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGZ1bmNzLmdldE1ldGVyKGNsaWVudCgpLCB7IG1ldGVySWQ6ICdtJyB9KQogICAgZXhwZWN0KHJlc3VsdC5vaykudG9CZSh0cnVlKQogICAgZXhwZWN0KHJlc3VsdC52YWx1ZSkudG9NYXRjaE9iamVjdCh7IGlkOiBtZXRlci5pZCwgYnJhbmRfbmV3X2ZpZWxkOiAna2VwdCcgfSkKICB9KQp9KQo=', + 'aW1wb3J0IGZldGNoTW9jayBmcm9tICdAZmV0Y2gtbW9jay92aXRlc3QnCmltcG9ydCB7IGJlZm9yZUVhY2gsIGRlc2NyaWJlLCBleHBlY3QsIGl0IH0gZnJvbSAndml0ZXN0JwppbXBvcnQgeyBDbGllbnQsIGZ1bmNzIH0gZnJvbSAnLi4vc3JjL2luZGV4LmpzJwoKY29uc3QgbWV0ZXIgPSB7CiAgaWQ6ICcwMUFSWjNOREVLVFNWNFJSRkZRNjlHNUZBVicsCiAgbmFtZTogJ0FQSSBjYWxscycsCiAga2V5OiAnYXBpX2NhbGxzJywKICBhZ2dyZWdhdGlvbjogJ2NvdW50JywKICBldmVudF90eXBlOiAnYXBpLXJlcXVlc3QnLAogIGNyZWF0ZWRfYXQ6ICcyMDI0LTAxLTAxVDAwOjAwOjAwWicsCiAgdXBkYXRlZF9hdDogJzIwMjQtMDEtMDFUMDA6MDA6MDBaJywKfQoKYmVmb3JlRWFjaCgoKSA9PiB7CiAgZmV0Y2hNb2NrLm1vY2tSZXNldCgpCn0pCgpmdW5jdGlvbiBjbGllbnQoKSB7CiAgcmV0dXJuIG5ldyBDbGllbnQoewogICAgYmFzZVVybDogJ2h0dHBzOi8vZXUuYXBpLmtvbmdocS5jb20vdjMnLAogICAgYXBpS2V5OiAnaycsCiAgICBmZXRjaDogZmV0Y2hNb2NrLmZldGNoSGFuZGxlciwKICB9KQp9CgpmdW5jdGlvbiBtb2NrTGlzdCgpIHsKICBmZXRjaE1vY2sucm91dGUoJyonLCB7CiAgICBib2R5OiB7IGRhdGE6IFttZXRlcl0sIG1ldGE6IHsgcGFnZTogeyBudW1iZXI6IDEsIHNpemU6IDEwLCB0b3RhbDogMSB9IH0gfSwKICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LAogIH0pCn0KCmZ1bmN0aW9uIGxhc3RRdWVyeSgpOiBVUkxTZWFyY2hQYXJhbXMgewogIHJldHVybiBuZXcgVVJMKGZldGNoTW9jay5jYWxsSGlzdG9yeS5sYXN0Q2FsbCgpIS51cmwpLnNlYXJjaFBhcmFtcwp9CgpkZXNjcmliZSgnbGlzdE1ldGVycyBxdWVyeSBzZXJpYWxpemF0aW9uJywgKCkgPT4gewogIGl0KCdlbmNvZGVzIHBhZ2UgYXMgYSBkZWVwIG9iamVjdCcsIGFzeW5jICgpID0+IHsKICAgIG1vY2tMaXN0KCkKICAgIGF3YWl0IGZ1bmNzLmxpc3RNZXRlcnMoY2xpZW50KCksIHsgcGFnZTogeyBzaXplOiAxMCwgbnVtYmVyOiAyIH0gfSkKICAgIGNvbnN0IHEgPSBsYXN0UXVlcnkoKQogICAgZXhwZWN0KHEuZ2V0KCdwYWdlW3NpemVdJykpLnRvQmUoJzEwJykKICAgIGV4cGVjdChxLmdldCgncGFnZVtudW1iZXJdJykpLnRvQmUoJzInKQogIH0pCgogIGl0KCdlbmNvZGVzIGEgc2NhbGFyIGZpbHRlcicsIGFzeW5jICgpID0+IHsKICAgIG1vY2tMaXN0KCkKICAgIGF3YWl0IGZ1bmNzLmxpc3RNZXRlcnMoY2xpZW50KCksIHsgZmlsdGVyOiB7IGtleTogJ20nIH0gfSkKICAgIGV4cGVjdChsYXN0UXVlcnkoKS5nZXQoJ2ZpbHRlcltrZXldJykpLnRvQmUoJ20nKQogIH0pCgogIGl0KCdlbmNvZGVzIGEgbmVzdGVkIGZpbHRlciBvcGVyYW5kIGFzIGEgZGVlcCBvYmplY3QnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTGlzdCgpCiAgICBhd2FpdCBmdW5jcy5saXN0TWV0ZXJzKGNsaWVudCgpLCB7IGZpbHRlcjogeyBrZXk6IHsgZXE6ICdmb28nIH0gfSB9KQogICAgZXhwZWN0KGxhc3RRdWVyeSgpLmdldCgnZmlsdGVyW2tleV1bZXFdJykpLnRvQmUoJ2ZvbycpCiAgfSkKCiAgaXQoJ2NvbW1hLWpvaW5zIGFycmF5IG9wZXJhbmRzIGludG8gYSBzaW5nbGUgcGFyYW1ldGVyJywgYXN5bmMgKCkgPT4gewogICAgbW9ja0xpc3QoKQogICAgYXdhaXQgZnVuY3MubGlzdE1ldGVycyhjbGllbnQoKSwgeyBmaWx0ZXI6IHsga2V5OiB7IG9lcTogWydhJywgJ2InLCAnYyddIH0gfSB9KQogICAgY29uc3QgcSA9IGxhc3RRdWVyeSgpCiAgICBleHBlY3QocS5nZXRBbGwoJ2ZpbHRlcltrZXldW29lcV0nKSkudG9IYXZlTGVuZ3RoKDEpCiAgICBleHBlY3QocS5nZXQoJ2ZpbHRlcltrZXldW29lcV0nKSkudG9CZSgnYSxiLGMnKQogIH0pCgogIGl0KCdzZXJpYWxpemVzIHNvcnQgYXMgYSBwbGFpbiBzdHJpbmcsIHNuYWtlLWlmeWluZyB0aGUgZmllbGQgbmFtZScsIGFzeW5jICgpID0+IHsKICAgIG1vY2tMaXN0KCkKICAgIGF3YWl0IGZ1bmNzLmxpc3RNZXRlcnMoY2xpZW50KCksIHsgc29ydDogeyBieTogJ2NyZWF0ZWRBdCcsIG9yZGVyOiAnZGVzYycgfSB9KQogICAgY29uc3QgcSA9IGxhc3RRdWVyeSgpCiAgICBleHBlY3QocS5nZXQoJ3NvcnQnKSkudG9CZSgnY3JlYXRlZF9hdCBkZXNjJykKICAgIGV4cGVjdChxLmdldCgnc29ydFtieV0nKSkudG9CZU51bGwoKQogIH0pCgogIGl0KCdvbWl0cyBzb3J0IG9yZGVyIHdoZW4gYXNjZW5kaW5nIGlzIGltcGxpZWQnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrTGlzdCgpCiAgICBhd2FpdCBmdW5jcy5saXN0TWV0ZXJzKGNsaWVudCgpLCB7IHNvcnQ6IHsgYnk6ICduYW1lJyB9IH0pCiAgICBleHBlY3QobGFzdFF1ZXJ5KCkuZ2V0KCdzb3J0JykpLnRvQmUoJ25hbWUnKQogIH0pCn0pCgpkZXNjcmliZSgncmVzcG9uc2VzIGFyZSBtYXBwZWQgdG8gdGhlIGNhbWVsQ2FzZSBzaGFwZScsICgpID0+IHsKICBpdCgnY2FtZWxpemVzIGtub3duIGZpZWxkcyBhbmQgZHJvcHMgZmllbGRzIG5vdCBpbiB0aGUgc2NoZW1hJywgYXN5bmMgKCkgPT4gewogICAgZmV0Y2hNb2NrLnJvdXRlKCcqJywgewogICAgICBib2R5OiB7IC4uLm1ldGVyLCBicmFuZF9uZXdfZmllbGQ6ICdkcm9wcGVkJyB9LAogICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSwKICAgIH0pCiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBmdW5jcy5nZXRNZXRlcihjbGllbnQoKSwgeyBtZXRlcklkOiAnbScgfSkKICAgIGV4cGVjdChyZXN1bHQub2spLnRvQmUodHJ1ZSkKICAgIGV4cGVjdChyZXN1bHQudmFsdWUpLnRvTWF0Y2hPYmplY3QoewogICAgICBpZDogbWV0ZXIuaWQsCiAgICAgIGV2ZW50VHlwZTogbWV0ZXIuZXZlbnRfdHlwZSwKICAgICAgY3JlYXRlZEF0OiBtZXRlci5jcmVhdGVkX2F0LAogICAgfSkKICAgIGV4cGVjdCgnZXZlbnRfdHlwZScgaW4gcmVzdWx0LnZhbHVlISkudG9CZShmYWxzZSkKICAgIGV4cGVjdCgnYnJhbmRfbmV3X2ZpZWxkJyBpbiByZXN1bHQudmFsdWUhKS50b0JlKGZhbHNlKQogIH0pCn0pCg==', 'tests/errors.spec.ts': 'aW1wb3J0IGZldGNoTW9jayBmcm9tICdAZmV0Y2gtbW9jay92aXRlc3QnCmltcG9ydCB7IGJlZm9yZUVhY2gsIGRlc2NyaWJlLCBleHBlY3QsIGl0IH0gZnJvbSAndml0ZXN0JwppbXBvcnQgeyBDbGllbnQsIEhUVFBFcnJvciwgZnVuY3MgfSBmcm9tICcuLi9zcmMvaW5kZXguanMnCgpiZWZvcmVFYWNoKCgpID0+IHsKICBmZXRjaE1vY2subW9ja1Jlc2V0KCkKfSkKCmZ1bmN0aW9uIGNsaWVudCgpIHsKICByZXR1cm4gbmV3IENsaWVudCh7CiAgICBiYXNlVXJsOiAnaHR0cHM6Ly9ldS5hcGkua29uZ2hxLmNvbS92MycsCiAgICBhcGlLZXk6ICdrJywKICAgIGZldGNoOiBmZXRjaE1vY2suZmV0Y2hIYW5kbGVyLAogIH0pCn0KCmZ1bmN0aW9uIG1vY2tQcm9ibGVtKHN0YXR1czogbnVtYmVyLCBib2R5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgewogIGZldGNoTW9jay5yb3V0ZSgnKicsIHsKICAgIHN0YXR1cywKICAgIGJvZHksCiAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vcHJvYmxlbStqc29uOyBjaGFyc2V0PXV0Zi04JyB9LAogIH0pCn0KCmRlc2NyaWJlKCdlcnJvciBtYXBwaW5nJywgKCkgPT4gewogIGl0KCdtYXBzIHByb2JsZW0ranNvbiB0byBhIHR5cGVkIEhUVFBFcnJvciB3aXRoIHBhcnNlZCBmaWVsZHMnLCBhc3luYyAoKSA9PiB7CiAgICBtb2NrUHJvYmxlbSg0MDQsIHsKICAgICAgdHlwZTogJ3QnLAogICAgICB0aXRsZTogJ05vdCBGb3VuZCcsCiAgICAgIHN0YXR1czogNDA0LAogICAgICBkZXRhaWw6ICdub3BlJywKICAgICAgaW5zdGFuY2U6ICcveCcsCiAgICB9KQogICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZnVuY3MuZ2V0TWV0ZXIoY2xpZW50KCksIHsgbWV0ZXJJZDogJ3gnIH0pCiAgICBleHBlY3QocmVzdWx0Lm9rKS50b0JlKGZhbHNlKQogICAgZXhwZWN0KHJlc3VsdC5lcnJvcikudG9CZUluc3RhbmNlT2YoSFRUUEVycm9yKQogICAgY29uc3QgaHR0cEVycm9yID0gcmVzdWx0LmVycm9yIGFzIEhUVFBFcnJvcgogICAgZXhwZWN0KGh0dHBFcnJvci5zdGF0dXMpLnRvQmUoNDA0KQogICAgZXhwZWN0KGh0dHBFcnJvci50aXRsZSkudG9CZSgnTm90IEZvdW5kJykKICAgIGV4cGVjdChodHRwRXJyb3IubWVzc2FnZSkudG9CZSgnbm9wZScpCiAgfSkKCiAgaXQoJ2V4cG9zZXMgaW52YWxpZF9wYXJhbWV0ZXJzIHZpYSBnZXRGaWVsZCcsIGFzeW5jICgpID0+IHsKICAgIG1vY2tQcm9ibGVtKDQwMCwgewogICAgICB0eXBlOiAndCcsCiAgICAgIHRpdGxlOiAnQmFkIFJlcXVlc3QnLAogICAgICBzdGF0dXM6IDQwMCwKICAgICAgZGV0YWlsOiAndmFsaWRhdGlvbiBmYWlsZWQnLAogICAgICBpbnN0YW5jZTogJy94JywKICAgICAgaW52YWxpZF9wYXJhbWV0ZXJzOiBbeyBmaWVsZDogJ25hbWUnLCByZWFzb246ICdpcyByZXF1aXJlZCcgfV0sCiAgICB9KQogICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZnVuY3MuZ2V0TWV0ZXIoY2xpZW50KCksIHsgbWV0ZXJJZDogJ3gnIH0pCiAgICBjb25zdCBodHRwRXJyb3IgPSByZXN1bHQuZXJyb3IgYXMgSFRUUEVycm9yCiAgICBleHBlY3QoaHR0cEVycm9yLmdldEZpZWxkKCdpbnZhbGlkX3BhcmFtZXRlcnMnKSkudG9FcXVhbChbCiAgICAgIHsgZmllbGQ6ICduYW1lJywgcmVhc29uOiAnaXMgcmVxdWlyZWQnIH0sCiAgICBdKQogIH0pCgogIGl0KCdmYWxscyBiYWNrIHRvIGEgc3RhdHVzLW9ubHkgZXJyb3IgZm9yIG5vbi1wcm9ibGVtIHJlc3BvbnNlcycsIGFzeW5jICgpID0+IHsKICAgIGZldGNoTW9jay5yb3V0ZSgnKicsIHsgc3RhdHVzOiA1MDAsIGJvZHk6ICdvb3BzJywgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ3RleHQvcGxhaW4nIH0gfSkKICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGZ1bmNzLmdldE1ldGVyKGNsaWVudCgpLCB7IG1ldGVySWQ6ICd4JyB9KQogICAgZXhwZWN0KHJlc3VsdC5lcnJvcikudG9CZUluc3RhbmNlT2YoSFRUUEVycm9yKQogICAgY29uc3QgaHR0cEVycm9yID0gcmVzdWx0LmVycm9yIGFzIEhUVFBFcnJvcgogICAgZXhwZWN0KGh0dHBFcnJvci5zdGF0dXMpLnRvQmUoNTAwKQogIH0pCn0pCg==', 'tests/nesting.spec.ts': diff --git a/api/spec/packages/typespec-typescript/src/runtime/wire.ts b/api/spec/packages/typespec-typescript/src/runtime/wire.ts new file mode 100644 index 0000000000..3e24a49869 --- /dev/null +++ b/api/spec/packages/typespec-typescript/src/runtime/wire.ts @@ -0,0 +1,280 @@ +import type { output, ZodType } from 'zod' + +export function toCamelCase(name: string): string { + return name.replace(/_([a-z0-9])/g, (_m, c: string) => c.toUpperCase()) +} + +export function toSnakeCase(name: string): string { + return name.replace(/([A-Z])/g, (_m, c: string) => `_${c.toLowerCase()}`) +} + +type ZodDef = { + type: string + innerType?: ZodType + valueType?: ZodType + element?: ZodType + discriminator?: string + options?: ZodType[] +} + +function def(schema: ZodType | undefined): ZodDef | undefined { + return (schema as { def?: ZodDef } | undefined)?.def +} + +// Unwrap optional/nullable/default to the schema that describes the value's +// shape. These wrappers never change keys, so the walker looks through them +// before classifying a node. +function unwrap(schema: ZodType | undefined): ZodType | undefined { + let current = schema + for (let i = 0; i < 100 && current; i++) { + const d = def(current) + if ( + d && + (d.type === 'optional' || + d.type === 'nullable' || + d.type === 'default') && + d.innerType + ) { + current = d.innerType + continue + } + return current + } + /* v8 ignore next -- loop returns inside; reached only past the cycle guard */ + return current +} + +function shapeOf( + schema: ZodType | undefined, +): Record | undefined { + return (schema as { shape?: Record } | undefined)?.shape +} + +// The element schema for array data: the schema's own element when it is an +// array, or the array variant's element when it is a union of T and T[] +// (the single-or-batch body shape). +function arrayElement(schema: ZodType | undefined): ZodType | undefined { + const d = def(schema) + if (d?.type === 'array') { + return d.element + } + if (d?.type === 'union') { + for (const option of d.options ?? []) { + const od = def(unwrap(option)) + if (od?.type === 'array') { + return od.element + } + } + } + return undefined +} + +// Whether a value schema carries renamable fields (object/record/array/union of +// such), so a record value is recursed only when it is model-shaped. Scalars, +// literals, unknown, and any are left untouched. +function hasRenamableShape(schema: ZodType | undefined): boolean { + const s = unwrap(schema) + const d = def(s) + /* v8 ignore next 3 -- a record always has a value schema; defensive only */ + if (!d) { + return false + } + if (d.type === 'object' || d.type === 'record') { + return true + } + if (d.type === 'array') { + return hasRenamableShape(d.element) + } + if (d.type === 'union') { + return (d.options ?? []).some(hasRenamableShape) + } + return false +} + +type Direction = { + // The wire→public or public→wire key rename for object fields. + rename: (key: string) => string + // The data key holding a discriminated union's discriminator, given the + // schema's (camelCase) discriminator key. + discriminatorKey: (camelKey: string) => string +} + +function walk( + data: unknown, + schema: ZodType | undefined, + dir: Direction, +): unknown { + if (data === null || data === undefined) { + return data + } + const s = unwrap(schema) + const d = def(s) + if (Array.isArray(data)) { + // The schema may be the array itself or a union with an array variant + // (e.g. a single-or-batch body `T | T[]`); resolve the element schema from + // whichever applies so array items are still walked with their shape. + const element = arrayElement(s) + return data.map((item) => walk(item, element, dir)) + } + if (typeof data !== 'object') { + return data + } + const record = data as Record + + if (d?.type === 'record') { + // Record keys are user data (label/dimension names) — preserved verbatim. + // Only the value is walked, and only when it is model-shaped. + const valueSchema = hasRenamableShape(d.valueType) ? d.valueType : undefined + const out: Record = {} + for (const [key, value] of Object.entries(record)) { + out[key] = valueSchema ? walk(value, valueSchema, dir) : value + } + return out + } + + if (d?.type === 'union') { + const variant = selectVariant(record, s, dir) + if (!variant) { + // No confident match: leave keys untransformed rather than guess. + return data + } + return walk(data, variant, dir) + } + + if (d?.type === 'object') { + const shape = shapeOf(s) ?? {} + const out: Record = {} + for (const [key, value] of Object.entries(record)) { + const fieldSchema = fieldFor(shape, key) + // Keys the schema does not declare are dropped, so the result matches the + // typed shape exactly (a server-added field has no place in the type). + if (fieldSchema === undefined) { + continue + } + out[dir.rename(key)] = walk(value, fieldSchema, dir) + } + return out + } + + // Scalar or unknown schema: pass through untransformed. + return data +} + +// Resolve a data key to its field schema. The schema is camelCase-keyed; a +// wire→public data key is snake, so it is camelized to index the shape. +function fieldFor( + shape: Record, + dataKey: string, +): ZodType | undefined { + return shape[dataKey] ?? shape[toCamelCase(dataKey)] +} + +function selectVariant( + data: Record, + schema: ZodType | undefined, + dir: Direction, +): ZodType | undefined { + const d = def(schema) + const options = d?.options ?? [] + if (d?.discriminator && schema) { + // O(1) dispatch on the discriminator literal. The data key is the wire-name in + // fromWire (snake) and the public name in toWire (camel); the variant map is + // keyed by the literal value, which is identical in both directions. + const dataKey = dir.discriminatorKey(d.discriminator) + return variantsByDiscriminator(schema, d).get(data[dataKey]) + } + // Non-discriminated union: the codegen gate guarantees at most one object + // variant (it fails the build for a mapped union with two or more), so the single + // object-shaped option is unambiguous. Other variants (scalars, arrays) reach the + // walk through their own data-kind branches, not here. + return options.find((option) => def(unwrap(option))?.type === 'object') +} + +// Memoized literal→variant map for a discriminated union, built once per schema. +const variantMapCache = new WeakMap>() + +function variantsByDiscriminator( + schema: ZodType, + d: ZodDef, +): Map { + const cached = variantMapCache.get(schema) + if (cached) { + return cached + } + const map = new Map() + for (const option of d.options ?? []) { + const shape = shapeOf(unwrap(option)) + const literal = literalValue(shape?.[d.discriminator as string]) + if (literal !== undefined) { + map.set(literal, option) + } + } + variantMapCache.set(schema, map) + return map +} + +function literalValue(schema: ZodType | undefined): unknown { + const s = unwrap(schema) + if (def(s)?.type === 'literal') { + return (s as { value?: unknown }).value + } + /* v8 ignore next -- a discriminated-union variant's discriminator is a literal */ + return undefined +} + +const toWireDirection: Direction = { + rename: toSnakeCase, + discriminatorKey: (camelKey) => camelKey, +} + +const fromWireDirection: Direction = { + rename: toCamelCase, + discriminatorKey: (camelKey) => toSnakeCase(camelKey), +} + +// Rewrite a request body or query object from the camelCase public shape to the +// snake_case wire shape, driven by its schema. Record keys (label/dimension names) +// are preserved. The return is typed as the input `T` so call sites stay cast-free +// (the runtime object has snake keys, but the value is write-only — it flows +// straight into `json:`/`toURLSearchParams`, both of which accept any object). +export function toWire(data: T, schema: ZodType): T { + return walk(data, schema, toWireDirection) as T +} + +// Rewrite a response body from the snake_case wire shape to the camelCase public +// shape. Renames keys only — never coerces values or applies defaults. The result +// is the schema's output shape: `walk` produces exactly the schema's known fields +// in camelCase, so the inferred `output` type describes the runtime value (the +// same wire-trust boundary as a plain `.json()`, with no `.parse()`). +export function fromWire( + data: unknown, + schema: S, +): output { + return walk(data, schema, fromWireDirection) as output +} + +// Thrown by assertValid when the optional `validate` client option is on and data +// fails its schema. request() catches it like any Error and surfaces it as +// Result.error. +export class ValidationError extends Error { + constructor( + message: string, + public readonly issues: unknown, + ) { + super(message) + this.name = 'ValidationError' + } +} + +// Opt-in schema check used by the funcs (when the validate option is on) against +// the snake_case wire payload: the request body after toWire, the raw response +// before fromWire, each against its generated `…Wire` schema. It is a GATE, not a +// transform — the safeParse output (coercions/defaults) is discarded, so validation +// never mutates the payload or return value. Off by default; the SDK does not +// validate by default (additive server fields must not break clients). +export function assertValid(schema: ZodType, data: unknown): void { + const result = schema.safeParse(data) + if (!result.success) { + throw new ValidationError('schema validation failed', result.error.issues) + } +} diff --git a/api/spec/packages/typespec-typescript/src/sdk-files.ts b/api/spec/packages/typespec-typescript/src/sdk-files.ts index f69528606c..5cf1971831 100644 --- a/api/spec/packages/typespec-typescript/src/sdk-files.ts +++ b/api/spec/packages/typespec-typescript/src/sdk-files.ts @@ -1,11 +1,20 @@ import type { SdkOperation } from './sdk-operations.js' import { namespaceNames } from './sdk-operations.js' import type { RequestTypes } from './request-types.js' +import { toCamelCase } from './casing.js' function pathExpr(op: SdkOperation): string { - const template = op.path.replace(/^\//, '') - const params = op.pathParams.map((p) => `${p}: req.${p}`).join(', ') - return `encodePath('${template}', { ${params} })` + // Inline the path as a template literal: each `{param}` becomes the + // URL-encoded request field. Path params are required strings, so no + // missing-value guard is needed (unlike the generic encodePath used for the + // server-variable baseUrl). + const path = op.path + .replace(/^\//, '') + .replace( + /\{(\w+)\}/g, + (_, p: string) => `\${encodeURIComponent(String(req.${p}))}`, + ) + return `\`${path}\`` } function funcBody(op: SdkOperation): string { @@ -26,8 +35,9 @@ function funcBody(op: SdkOperation): string { kyOpts.push('searchParams') } if (op.hasBody) { - const wrapped = hasPath || hasQuery - kyOpts.push(wrapped ? 'json: req.body' : 'json: req') + // The snake_case wire body is computed into `body` inside the closure (so the + // optional validate check can run against the actual payload before sending). + kyOpts.push('json: body') } if (op.textResponseContentType) { kyOpts.push('headers') @@ -44,10 +54,24 @@ function funcBody(op: SdkOperation): string { `): Promise> {`, ) if (hasQuery) { - const entries = op.queryParams.map((p) => - p === 'sort' ? ` sort: encodeSort(req.sort),` : ` ${p}: req.${p},`, + // The query object is camelCase (sort pre-encoded to its wire string); toWire + // snake-ifies the keys, including typed filter field names, against the query + // schema. Record keys (label/dimension names) are preserved by the walker. + const entries = op.queryParams.map((p) => { + const key = toCamelCase(p) + // sort.by names a field in the public (camelCase) surface; the server + // expects the snake_case field name, so translate the value here. + return p === 'sort' + ? ` sort: encodeSort(req.sort, toSnakeCase),` + : ` ${key}: req.${key},` + }) + lines.push( + ` const searchParams = toURLSearchParams(`, + ` toWire({`, + ...entries, + ` }, schemas.${op.funcName}QueryParams),`, + ` )`, ) - lines.push(` const searchParams = toURLSearchParams({`, ...entries, ` })`) } if (op.textResponseContentType) { // The server negotiates this variant on the exact Accept media type; user @@ -62,20 +86,59 @@ function funcBody(op: SdkOperation): string { } const target = hasPath ? 'path' : url const optsArg = optsObj === 'options' ? ', options' : `, ${optsObj}` + // Maps the request body to the wire and, when validation is on, checks the actual + // snake_case payload against the strict wire schema before sending. Runs inside the + // request() closure so a failure becomes Result.error, not a synchronous throw. + const bodyValue = hasPath || hasQuery ? 'req.body' : 'req' + const prepareBody = op.hasBody + ? [ + ` const body = toWire(${bodyValue}, schemas.${op.funcName}Body)`, + ` if (client._options.validate) {`, + ` assertValid(schemas.${op.funcName}BodyWire, body)`, + ` }`, + ] + : [] + // A request-body op runs its validation inside a block-arrow request() closure; + // bodyless ops keep the terser expression form (no behavior change). + const open = op.hasBody + ? ` return request(() => {` + : ` return request(() =>` + const ret = op.hasBody ? ` return http(client)` : ` http(client)` if (op.hasResponse) { - lines.push( - ` return request(() =>`, - ` http(client)`, - ` .${op.verb}(${target}${optsArg})`, - op.textResponseContentType - ? ` .text(),` - : ` .json<${resType}>(),`, - ` )`, - `}`, - ) + if (op.textResponseContentType) { + lines.push( + open, + ...prepareBody, + ret, + ` .${op.verb}(${target}${optsArg})`, + op.hasBody ? ` .text()` : ` .text(),`, + op.hasBody ? ` })` : ` )`, + `}`, + ) + } else { + // When validation is on, the raw snake_case wire response is checked against + // the strict wire schema before fromWire maps it to the camelCase public shape. + lines.push( + open, + ...prepareBody, + ret, + ` .${op.verb}(${target}${optsArg})`, + ` .json()`, + ` .then((data) => {`, + ` if (client._options.validate) {`, + ` assertValid(schemas.${op.funcName}ResponseWire, data)`, + ` }`, + ` return fromWire(data, schemas.${op.funcName}Response)`, + op.hasBody ? ` })` : ` }),`, + op.hasBody ? ` })` : ` )`, + `}`, + ) + } } else { + // Bodyless void ops already used an async block; body void ops add validation. lines.push( ` return request(async () => {`, + ...prepareBody, ` await http(client).${op.verb}(${target}${optsArg})`, ` })`, `}`, @@ -138,13 +201,43 @@ export function operationsAssertFile( export function funcsFile(tag: string, ops: SdkOperation[]): string { const file = namespaceFile(tag) + // toWire maps request bodies and query objects to the wire; fromWire maps JSON + // responses back. Both reference per-op schema values, so the schema namespace + // is imported whenever either is used. + const usesToWire = ops.some((op) => op.hasBody || op.queryParams.length > 0) + const usesFromWire = ops.some( + (op) => op.hasResponse && !op.textResponseContentType, + ) + const usesSnake = ops.some((op) => op.hasSort) + // assertValid runs (when the validate option is on) for any op with a request + // body or a JSON response. + const usesValidate = ops.some( + (op) => op.hasBody || (op.hasResponse && !op.textResponseContentType), + ) + const mapperNames = [ + ...(usesToWire ? ['toWire'] : []), + ...(usesFromWire ? ['fromWire'] : []), + ...(usesValidate ? ['assertValid'] : []), + ...(usesSnake ? ['toSnakeCase'] : []), + ] // The funcs reference only the per-op `…Request`/`…Response` aliases, which // live in the operations types module. + // Path params are now inlined as template literals, so encodePath is no longer + // imported; only query ops use the URL serializer and sort encoder. + const hasAnyQuery = ops.some((op) => op.queryParams.length > 0) const imports = [ `import { type Client, http } from '../core.js'`, `import { type Result, type RequestOptions } from '../lib/types.js'`, `import { request } from '../lib/request.js'`, - `import { encodePath, toURLSearchParams, encodeSort } from '../lib/encodings.js'`, + ...(hasAnyQuery + ? [`import { toURLSearchParams, encodeSort } from '../lib/encodings.js'`] + : []), + ...(mapperNames.length > 0 + ? [`import { ${mapperNames.join(', ')} } from '../lib/wire.js'`] + : []), + ...(mapperNames.length > 0 + ? [`import * as schemas from '../models/schemas.js'`] + : []), `import type {`, ...ops.flatMap((op) => [` ${op.base}Request,`, ` ${op.base}Response,`]), `} from '../models/operations/${file}.js'`, @@ -309,6 +402,7 @@ export function indexFile(tags: string[], modelTypeNames: string[]): string { namespaceExports, `export { Client } from './core.js'`, `export { HTTPError } from './models/errors.js'`, + `export { ValidationError } from './lib/wire.js'`, ``, `export { ServerList, Regions } from './lib/config.js'`, `export type { SDKOptions, Region, ServerVariables } from './lib/config.js'`, diff --git a/api/spec/packages/typespec-typescript/src/ts-types.ts b/api/spec/packages/typespec-typescript/src/ts-types.ts index 4591241444..390b0d94a2 100644 --- a/api/spec/packages/typespec-typescript/src/ts-types.ts +++ b/api/spec/packages/typespec-typescript/src/ts-types.ts @@ -11,7 +11,7 @@ import { } from '@typespec/compiler' import { $ } from '@typespec/compiler/typekit' import type { Typekit } from '@typespec/compiler/typekit' -import { bodyProperties, isRecord } from './utils.jsx' +import { bodyProperties, isRecord, publicPropertyName } from './utils.jsx' /** * A reference resolver: returns the TypeScript interface name for a type that is @@ -225,7 +225,7 @@ function anonymousObjectType( ): string { const fields = bodyProperties(program, type).map((prop) => { const optional = isOptional(prop, io) ? '?' : '' - return `${prop.name}${optional}: ${tsTypeOf(program, prop.type, refName, io)}` + return `${publicPropertyName(program, prop)}${optional}: ${tsTypeOf(program, prop.type, refName, io)}` }) if (type.baseModel) { const base = refName(type.baseModel) diff --git a/api/spec/packages/typespec-typescript/src/utils.tsx b/api/spec/packages/typespec-typescript/src/utils.tsx index cdad0a4f7c..1b136934fd 100644 --- a/api/spec/packages/typespec-typescript/src/utils.tsx +++ b/api/spec/packages/typespec-typescript/src/utils.tsx @@ -6,7 +6,13 @@ import { useContext, } from '@alloy-js/core' import { FunctionCallExpression, MemberExpression } from '@alloy-js/typescript' -import type { Model, ModelProperty, Program, Type } from '@typespec/compiler' +import { + type Model, + type ModelProperty, + type Program, + resolveEncodedName, + type Type, +} from '@typespec/compiler' import { $ } from '@typespec/compiler/typekit' import { isBody, isStatusCode } from '@typespec/http' import { @@ -15,9 +21,74 @@ import { } from './context/zod-options.js' import { zod } from './external-packages/zod.js' import { isReadVisible } from './visibility.js' +import { toCamelCase } from './casing.js' export const refkeySym = Symbol.for('typespec-typescript.refkey') +export { isCasingDerivable, toCamelCase, toSnakeCase } from './casing.js' + +/** + * The public (camelCase) key for a body property: its JSON wire name camelized. + * Sourcing from the wire name (not the declared name) keeps the public key in + * lockstep with what the boundary mapper snake-ifies back, which the codegen gate + * guarantees is reversible. The one shared key helper for both the interface and + * the zod emitter, so the two artifacts stay byte-identical and the conformance + * guard keeps proving interface ≡ schema. + */ +export function publicPropertyName( + program: Program, + prop: ModelProperty, +): string { + return toCamelCase(wireName(program, prop)) +} + +/** The JSON wire name of a property (its `@encodedName` or declared name). */ +export function wireName(program: Program, prop: ModelProperty): string { + return resolveEncodedName( + program, + prop as ModelProperty & { name: string }, + 'application/json', + ) +} + +/** + * Refkey namespace for the second, snake_case "wire" schema pass. The same models + * are emitted twice — once camelCase (validation-agnostic public surface) and once + * snake_case strict (the optional `validate` option). Each pass cross-references + * its own variant, so wire schemas reference wire schemas via this symbol while the + * camel pass keeps using {@link refkeySym}. + */ +export const wireRefkeySym = Symbol.for('typespec-typescript.wire-refkey') + +/** + * When set, the zod schema emitter is producing the snake_case wire schema: object + * keys are the raw JSON wire name (not camelized), closed objects are strict, and + * cross-references resolve in the wire refkey namespace. + */ +export const WireModeContext: ComponentContext = + createContext(false) + +export function useWireMode(): boolean { + return useContext(WireModeContext) ?? false +} + +/** The refkey symbol for the active schema pass (wire vs camel). */ +export function activeRefkeySym(wire: boolean): symbol { + return wire ? wireRefkeySym : refkeySym +} + +/** + * The object key for a property in the active schema pass: the raw wire name in + * wire mode, the camelized public name otherwise. + */ +export function schemaPropertyName( + program: Program, + prop: ModelProperty, + wire: boolean, +): string { + return wire ? wireName(program, prop) : publicPropertyName(program, prop) +} + /** * Tracks the type currently being declared, so that properties whose subtree * loops back to it can be emitted as object getters (`get name() { ... }`). diff --git a/api/spec/packages/typespec-typescript/src/wire-runtime.ts b/api/spec/packages/typespec-typescript/src/wire-runtime.ts new file mode 100644 index 0000000000..dd11065af8 --- /dev/null +++ b/api/spec/packages/typespec-typescript/src/wire-runtime.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' + +/** + * The boundary mapper source, emitted verbatim as `src/lib/wire.ts` in the + * generated SDK. Authored as a real module under `runtime/` (type-checked and + * unit-tested in this package) and read here so the emitted file stays in lockstep + * with what the tests exercise. + */ +export const WIRE_RUNTIME = readFileSync( + fileURLToPath(new URL('../src/runtime/wire.ts', import.meta.url)), + 'utf8', +) diff --git a/api/spec/packages/typespec-typescript/src/zodBaseSchema.tsx b/api/spec/packages/typespec-typescript/src/zodBaseSchema.tsx index 34b190bfc2..8184cd7a9c 100644 --- a/api/spec/packages/typespec-typescript/src/zodBaseSchema.tsx +++ b/api/spec/packages/typespec-typescript/src/zodBaseSchema.tsx @@ -19,17 +19,20 @@ import { useTsp } from '@typespec/emitter-framework' import { ZodCustomTypeComponent } from './components/ZodCustomTypeComponent.jsx' import { ZodSchema } from './components/ZodSchema.jsx' import { + activeRefkeySym, bodyProperties, callPart, emitsAsIntersection, idPart, isDeclaration, isRecord, - refkeySym, + schemaPropertyName, shouldReference, subtreeReachesType, + toCamelCase, useCoerce, useDeclaringType, + useWireMode, zodMemberExpr, } from './utils.jsx' @@ -115,7 +118,11 @@ function literalBaseType(type: LiteralType) { function scalarBaseType($: Typekit, type: Scalar) { if (type.baseScalar && shouldReference($.program, type.baseScalar)) { - return + return ( + + ) } if ($.scalar.extendsBoolean(type)) { @@ -227,6 +234,13 @@ function tupleBaseType(type: Tuple) { function modelBaseType(type: Model) { const { $ } = useTsp() + const wire = useWireMode() + const rkSym = activeRefkeySym(wire) + // Closed objects are strict in the wire pass so a leaked-camelCase or unknown + // wire key is rejected. Open models (record spread, `emitsAsIntersection`) stay + // permissive — strict would defeat the record arm that exists to accept them. + const objectCall = + wire && !emitsAsIntersection($.program, type) ? 'strictObject' : 'object' if ($.array.is(type)) { return zodMemberExpr( @@ -262,17 +276,18 @@ function modelBaseType(type: Model) { {(prop) => { + const key = schemaPropertyName($.program, prop, wire) const isCyclic = declaringType !== undefined && subtreeReachesType($.program, prop.type, declaringType) const propertyContent = isCyclic ? ( <> - get {prop.name}() {'{ return '} + get {key}() {'{ return '} {'; }'} ) : ( - + ) @@ -281,7 +296,7 @@ function modelBaseType(type: Model) { type={prop} declare Declaration={ObjectProperty} - declarationProps={{ name: prop.name }} + declarationProps={{ name: key }} > {propertyContent} @@ -290,7 +305,7 @@ function modelBaseType(type: Model) { ) - memberPart = zodMemberExpr(callPart('object', members)) + memberPart = zodMemberExpr(callPart(objectCall, members)) } const hasReferencedBase = @@ -303,11 +318,9 @@ function modelBaseType(type: Model) { // property was the stripped `@statusCode`) is just its base schema; avoid // an empty `base.merge(z.object({}))`. if (hasReferencedBase) { - return ( - - ) + return } - parts = zodMemberExpr(callPart('object', )) + parts = zodMemberExpr(callPart(objectCall, )) } else if (memberPart && recordPart) { parts = zodMemberExpr(callPart('intersection', memberPart, recordPart)) } else { @@ -321,14 +334,14 @@ function modelBaseType(type: Model) { if (emitsAsIntersection($.program, type.baseModel!)) { const baseRef = ( - + ) return zodMemberExpr(callPart('intersection', baseRef, parts)) } return ( - + @@ -356,8 +369,17 @@ function unionBaseType(type: Union) { ) } - const propKey = discriminated.options.discriminatorPropertyName - const envKey = discriminated.options.envelopePropertyName + // The discriminator key tracks the variant member keys: camelized in the public + // pass, raw wire name in the wire pass, so `z.discriminatedUnion`'s key matches + // the emitted variant shapes. The discriminator value (`variant.name`) is a + // literal and is never renamed. + const wire = useWireMode() + const propKey = wire + ? discriminated.options.discriminatorPropertyName + : toCamelCase(discriminated.options.discriminatorPropertyName) + const envKey = wire + ? discriminated.options.envelopePropertyName + : toCamelCase(discriminated.options.envelopePropertyName) const unionArgs = [ `"${propKey}"`, diff --git a/api/spec/packages/typespec-typescript/test/casing.test.ts b/api/spec/packages/typespec-typescript/test/casing.test.ts new file mode 100644 index 0000000000..4a5ac09ff1 --- /dev/null +++ b/api/spec/packages/typespec-typescript/test/casing.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'vitest' +import { isCasingDerivable, toCamelCase, toSnakeCase } from '../src/casing.js' + +describe('toCamelCase', () => { + it('camelizes single and multi-word snake names', () => { + expect(toCamelCase('id')).toBe('id') + expect(toCamelCase('meter_id')).toBe('meterId') + expect(toCamelCase('amount_before_proration')).toBe('amountBeforeProration') + }) + + it('handles digits after an underscore', () => { + expect(toCamelCase('cache_read_per_token')).toBe('cacheReadPerToken') + expect(toCamelCase('int_64')).toBe('int64') + }) + + it('leaves already-camel names unchanged', () => { + expect(toCamelCase('createdAt')).toBe('createdAt') + }) +}) + +describe('toSnakeCase', () => { + it('snakeifies camel names', () => { + expect(toSnakeCase('meterId')).toBe('meter_id') + expect(toSnakeCase('amountBeforeProration')).toBe('amount_before_proration') + }) + + it('leaves single-word names unchanged', () => { + expect(toSnakeCase('id')).toBe('id') + expect(toSnakeCase('eq')).toBe('eq') + }) +}) + +describe('round-trip', () => { + const snakeNames = [ + 'id', + 'meter_id', + 'amount_before_proration', + 'has_access', + 'cache_read_per_token', + 'created_at', + 'include_deleted', + ] + + it('snake → camel → snake is identity for derivable names', () => { + for (const name of snakeNames) { + expect(toSnakeCase(toCamelCase(name))).toBe(name) + expect(isCasingDerivable(name)).toBe(true) + } + }) + + it('flags names that do not round-trip', () => { + expect(isCasingDerivable('weird_APIKey')).toBe(false) + expect(isCasingDerivable('alreadyCamel')).toBe(false) + }) +}) diff --git a/api/spec/pnpm-lock.yaml b/api/spec/pnpm-lock.yaml index 67c2830aa0..efbab1a0b1 100644 --- a/api/spec/pnpm-lock.yaml +++ b/api/spec/pnpm-lock.yaml @@ -9,25 +9,25 @@ overrides: patchedDependencies: '@typespec/compiler': - hash: 3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7 + hash: riwjrattawzpgwt74lnbqx3sdy path: patches/@typespec__compiler.patch '@typespec/emitter-framework@0.17.0': - hash: 1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd + hash: jqz7bjrpz73vbtzb63j5jq5p4q path: patches/@typespec__emitter-framework@0.17.0.patch '@typespec/http': - hash: e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410 + hash: 7net4uhpgzyfqswqb76vhoaleu path: patches/@typespec__http.patch '@typespec/http-client': - hash: 868c8bb8ad6e321347846c4a7fee764bae6a04300db4dab27521e11dcfec61af + hash: hzsn74ah7pvsbuy64whbvwa4my path: patches/@typespec__http-client.patch '@typespec/http-client-python': - hash: 9dcf4fca858e9dbe3b04ba9a577a6a45b074b6ebbab97ab781834c3b609a22b5 + hash: junrvxocxwjwjp5jztgergifke path: patches/@typespec__http-client-python.patch '@typespec/openapi': - hash: f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827 + hash: 26lua3u5ftfsgp7iobehpb3hn4 path: patches/@typespec__openapi.patch '@typespec/openapi3': - hash: cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95 + hash: pyfaeqet7wh7fkbeodck2dgrki path: patches/@typespec__openapi3.patch importers: @@ -36,16 +36,19 @@ importers: devDependencies: '@fetch-mock/vitest': specifier: 0.2.18 - version: 0.2.18(vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0))) + version: 0.2.18(vitest@4.1.8) '@typespec/prettier-plugin-typespec': specifier: 1.12.0 version: 1.12.0 + '@vitest/coverage-v8': + specifier: 4.1.8 + version: 4.1.8(vitest@4.1.8) prettier: specifier: 3.8.3 version: 3.8.3 vitest: specifier: 4.1.8 - version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)) packages/aip: devDependencies: @@ -60,25 +63,25 @@ importers: version: 25.9.2 '@typespec/compiler': specifier: 1.11.0 - version: 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + version: 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) '@typespec/http': specifier: 1.11.0 - version: 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/json-schema': specifier: 1.11.0 - version: 1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + version: 1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) '@typespec/openapi': specifier: 1.11.0 - version: 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/openapi3': specifier: 1.11.0 - version: 1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(972722f3b2beb4f4aa97c5f6210f12c6) + version: 1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(m3dgrauyqqqdsmhf7bdsf4jfoi) '@typespec/prettier-plugin-typespec': specifier: 1.12.0 version: 1.12.0 '@typespec/rest': specifier: 0.81.0 - version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) prettier: specifier: 3.8.3 version: 3.8.3 @@ -106,34 +109,34 @@ importers: devDependencies: '@azure-tools/typespec-client-generator-core': specifier: 0.68.4 - version: 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) + version: 0.68.4(iddsji6x7kkq6q74egd42apaja) '@types/node': specifier: 25.9.2 version: 25.9.2 '@typespec/compiler': specifier: 1.11.0 - version: 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + version: 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) '@typespec/http': specifier: 1.11.0 - version: 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/http-client-python': specifier: 0.28.3 - version: 0.28.3(patch_hash=9dcf4fca858e9dbe3b04ba9a577a6a45b074b6ebbab97ab781834c3b609a22b5)(ab42fd472452fee622155669e574e69a) + version: 0.28.3(patch_hash=junrvxocxwjwjp5jztgergifke)(6inkwr6cbtxv6flr665yduyu2y) '@typespec/openapi': specifier: 1.11.0 - version: 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/openapi3': specifier: 1.11.0 - version: 1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(f243169b9b94d9a7b11d6d0ca80911b9) + version: 1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/openapi@1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/prettier-plugin-typespec': specifier: 1.12.0 version: 1.12.0 '@typespec/rest': specifier: 0.81.0 - version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/versioning': specifier: 0.81.0 - version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) packages/typespec-typescript: dependencies: @@ -145,19 +148,19 @@ importers: version: 0.23.0 '@typespec/compiler': specifier: 1.11.0 - version: 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + version: 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) '@typespec/emitter-framework': specifier: 0.17.0 - version: 0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + version: 0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) '@typespec/http': specifier: 1.11.0 - version: 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/http-client': specifier: 0.15.1 - version: 0.15.1(patch_hash=868c8bb8ad6e321347846c4a7fee764bae6a04300db4dab27521e11dcfec61af)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) + version: 0.15.1(patch_hash=hzsn74ah7pvsbuy64whbvwa4my)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) '@typespec/openapi': specifier: 1.11.0 - version: 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) typescript: specifier: 6.0.3 version: 6.0.3 @@ -415,6 +418,10 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -958,42 +965,36 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.3': resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.3': resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.3': resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.3': resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.3': resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.3': resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} @@ -1217,6 +1218,15 @@ packages: peerDependencies: '@typespec/compiler': ^1.11.0 + '@vitest/coverage-v8@4.1.8': + resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==} + peerDependencies: + '@vitest/browser': 4.1.8 + vitest: 4.1.8 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/expect@4.1.8': resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} @@ -1301,6 +1311,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@1.0.4: + resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1584,6 +1597,9 @@ packages: html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http2-client@1.3.5: resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} @@ -1630,10 +1646,25 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + js-levenshtein@1.1.6: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1720,28 +1751,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -1779,6 +1806,13 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} @@ -2566,52 +2600,52 @@ snapshots: - bufferutil - utf-8-validate - '@azure-tools/typespec-autorest@0.67.0(f8fb9bd330bad0373fc685cd461b75e9)': + '@azure-tools/typespec-autorest@0.67.0(byhmcmjm5wvcyili6vfnju77si)': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) - '@azure-tools/typespec-azure-resource-manager': 0.67.0(f6c56f1404e3e5f37008eebf829701b8) - '@azure-tools/typespec-client-generator-core': 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) + '@azure-tools/typespec-azure-resource-manager': 0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4) + '@azure-tools/typespec-client-generator-core': 0.68.4(iddsji6x7kkq6q74egd42apaja) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) optionalDependencies: - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))': + '@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@azure-tools/typespec-azure-resource-manager@0.67.0(f6c56f1404e3e5f37008eebf829701b8)': + '@azure-tools/typespec-azure-resource-manager@0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4)': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) change-case: 5.4.4 pluralize: 8.0.0 - '@azure-tools/typespec-azure-rulesets@0.67.0(e0cf5ccc6837ab58d03df92798efbe68)': + '@azure-tools/typespec-azure-rulesets@0.67.0(@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))))(@azure-tools/typespec-azure-resource-manager@0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4))(@azure-tools/typespec-client-generator-core@0.68.4(iddsji6x7kkq6q74egd42apaja))(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) - '@azure-tools/typespec-azure-resource-manager': 0.67.0(f6c56f1404e3e5f37008eebf829701b8) - '@azure-tools/typespec-client-generator-core': 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) + '@azure-tools/typespec-azure-resource-manager': 0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4) + '@azure-tools/typespec-client-generator-core': 0.68.4(iddsji6x7kkq6q74egd42apaja) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@azure-tools/typespec-client-generator-core@0.68.4(d52a96869aeec1cdda9cd7e81cd231b5)': + '@azure-tools/typespec-client-generator-core@0.68.4(iddsji6x7kkq6q74egd42apaja)': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) change-case: 5.4.4 pluralize: 8.0.0 yaml: 2.9.0 @@ -2819,6 +2853,8 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@1.0.2': {} + '@colors/colors@1.5.0': optional: true @@ -2926,10 +2962,10 @@ snapshots: '@faker-js/faker@7.6.0': {} - '@fetch-mock/vitest@0.2.18(vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)))': + '@fetch-mock/vitest@0.2.18(vitest@4.1.8)': dependencies: fetch-mock: 12.6.0 - vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)) '@humanwhocodes/momoa@2.0.4': {} @@ -3418,11 +3454,11 @@ snapshots: dependencies: '@types/node': 25.9.2 - '@typespec/asset-emitter@0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': + '@typespec/asset-emitter@0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)': + '@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)': dependencies: '@babel/code-frame': 7.29.0 '@inquirer/prompts': 8.4.2(@types/node@25.9.2) @@ -3444,32 +3480,32 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@typespec/emitter-framework@0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': + '@typespec/emitter-framework@0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: '@alloy-js/core': 0.23.1 '@alloy-js/csharp': 0.22.0 '@alloy-js/python': 0.3.0 '@alloy-js/typescript': 0.23.0 - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': + '@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) optional: true - '@typespec/http-client-python@0.28.3(patch_hash=9dcf4fca858e9dbe3b04ba9a577a6a45b074b6ebbab97ab781834c3b609a22b5)(ab42fd472452fee622155669e574e69a)': - dependencies: - '@azure-tools/typespec-autorest': 0.67.0(f8fb9bd330bad0373fc685cd461b75e9) - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) - '@azure-tools/typespec-azure-resource-manager': 0.67.0(f6c56f1404e3e5f37008eebf829701b8) - '@azure-tools/typespec-azure-rulesets': 0.67.0(e0cf5ccc6837ab58d03df92798efbe68) - '@azure-tools/typespec-client-generator-core': 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/http-client-python@0.28.3(patch_hash=junrvxocxwjwjp5jztgergifke)(6inkwr6cbtxv6flr665yduyu2y)': + dependencies: + '@azure-tools/typespec-autorest': 0.67.0(byhmcmjm5wvcyili6vfnju77si) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) + '@azure-tools/typespec-azure-resource-manager': 0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4) + '@azure-tools/typespec-azure-rulesets': 0.67.0(@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))))(@azure-tools/typespec-azure-resource-manager@0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4))(@azure-tools/typespec-client-generator-core@0.68.4(iddsji6x7kkq6q74egd42apaja))(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@azure-tools/typespec-client-generator-core': 0.68.4(iddsji6x7kkq6q74egd42apaja) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) js-yaml: 4.1.1 marked: 15.0.12 pyodide: 0.26.2 @@ -3479,92 +3515,106 @@ snapshots: - bufferutil - utf-8-validate - '@typespec/http-client@0.15.1(patch_hash=868c8bb8ad6e321347846c4a7fee764bae6a04300db4dab27521e11dcfec61af)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))': + '@typespec/http-client@0.15.1(patch_hash=hzsn74ah7pvsbuy64whbvwa4my)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))': dependencies: '@alloy-js/core': 0.23.1 '@alloy-js/typescript': 0.23.0 - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/emitter-framework': 0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/emitter-framework': 0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': + '@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) optionalDependencies: - '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': + '@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: - '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) yaml: 2.8.3 - '@typespec/openapi3@1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(972722f3b2beb4f4aa97c5f6210f12c6)': + '@typespec/openapi3@1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/openapi@1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': dependencies: '@scalar/json-magic': 0.11.7 '@scalar/openapi-parser': 0.24.17 '@scalar/openapi-types': 0.5.4 - '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) yaml: 2.8.3 optionalDependencies: - '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/sse': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/openapi3@1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(f243169b9b94d9a7b11d6d0ca80911b9)': + '@typespec/openapi3@1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(m3dgrauyqqqdsmhf7bdsf4jfoi)': dependencies: '@scalar/json-magic': 0.11.7 '@scalar/openapi-parser': 0.24.17 '@scalar/openapi-types': 0.5.4 - '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) yaml: 2.8.3 optionalDependencies: - '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/sse': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/openapi@1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': + '@typespec/openapi@1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) '@typespec/prettier-plugin-typespec@1.12.0': dependencies: prettier: 3.8.3 - '@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': + '@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/sse@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': + '@typespec/sse@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) optional: true - '@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': + '@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) optional: true - '@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': + '@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': + '@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + + '@vitest/coverage-v8@4.1.8(vitest@4.1.8)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.8 + ast-v8-to-istanbul: 1.0.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.2 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/expect@4.1.8': dependencies: @@ -3655,6 +3705,12 @@ snapshots: assertion-error@2.0.1: {} + ast-v8-to-istanbul@1.0.4: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + balanced-match@1.0.2: {} balanced-match@4.0.4: {} @@ -3938,6 +3994,8 @@ snapshots: html-entities@2.6.0: {} + html-escaper@2.0.2: {} + http2-client@1.3.5: {} https-proxy-agent@7.0.6: @@ -3971,8 +4029,23 @@ snapshots: is-unicode-supported@2.1.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + js-levenshtein@1.1.6: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -4076,6 +4149,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.2 + mark.js@8.11.1: {} marked@15.0.12: {} @@ -4573,7 +4656,7 @@ snapshots: tsx: 4.21.0 yaml: 2.9.0 - vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)): + vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(tsx@4.21.0)(yaml@2.9.0)) @@ -4598,6 +4681,7 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.9.2 + '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) transitivePeerDependencies: - msw From 01ede2fb8978b4cea489987050e82ab6a39634b3 Mon Sep 17 00:00:00 2001 From: Andras Toth <4157749+tothandras@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:46:41 +0200 Subject: [PATCH 2/4] fix: lockfile --- api/spec/pnpm-lock.yaml | 260 +++++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 125 deletions(-) diff --git a/api/spec/pnpm-lock.yaml b/api/spec/pnpm-lock.yaml index efbab1a0b1..2dfeae4001 100644 --- a/api/spec/pnpm-lock.yaml +++ b/api/spec/pnpm-lock.yaml @@ -9,25 +9,25 @@ overrides: patchedDependencies: '@typespec/compiler': - hash: riwjrattawzpgwt74lnbqx3sdy + hash: 3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7 path: patches/@typespec__compiler.patch '@typespec/emitter-framework@0.17.0': - hash: jqz7bjrpz73vbtzb63j5jq5p4q + hash: 1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd path: patches/@typespec__emitter-framework@0.17.0.patch '@typespec/http': - hash: 7net4uhpgzyfqswqb76vhoaleu + hash: e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410 path: patches/@typespec__http.patch '@typespec/http-client': - hash: hzsn74ah7pvsbuy64whbvwa4my + hash: 868c8bb8ad6e321347846c4a7fee764bae6a04300db4dab27521e11dcfec61af path: patches/@typespec__http-client.patch '@typespec/http-client-python': - hash: junrvxocxwjwjp5jztgergifke + hash: 9dcf4fca858e9dbe3b04ba9a577a6a45b074b6ebbab97ab781834c3b609a22b5 path: patches/@typespec__http-client-python.patch '@typespec/openapi': - hash: 26lua3u5ftfsgp7iobehpb3hn4 + hash: f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827 path: patches/@typespec__openapi.patch '@typespec/openapi3': - hash: pyfaeqet7wh7fkbeodck2dgrki + hash: cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95 path: patches/@typespec__openapi3.patch importers: @@ -63,25 +63,25 @@ importers: version: 25.9.2 '@typespec/compiler': specifier: 1.11.0 - version: 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + version: 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) '@typespec/http': specifier: 1.11.0 - version: 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) '@typespec/json-schema': specifier: 1.11.0 - version: 1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + version: 1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) '@typespec/openapi': specifier: 1.11.0 - version: 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) '@typespec/openapi3': specifier: 1.11.0 - version: 1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(m3dgrauyqqqdsmhf7bdsf4jfoi) + version: 1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(972722f3b2beb4f4aa97c5f6210f12c6) '@typespec/prettier-plugin-typespec': specifier: 1.12.0 version: 1.12.0 '@typespec/rest': specifier: 0.81.0 - version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) prettier: specifier: 3.8.3 version: 3.8.3 @@ -109,34 +109,34 @@ importers: devDependencies: '@azure-tools/typespec-client-generator-core': specifier: 0.68.4 - version: 0.68.4(iddsji6x7kkq6q74egd42apaja) + version: 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) '@types/node': specifier: 25.9.2 version: 25.9.2 '@typespec/compiler': specifier: 1.11.0 - version: 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + version: 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) '@typespec/http': specifier: 1.11.0 - version: 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) '@typespec/http-client-python': specifier: 0.28.3 - version: 0.28.3(patch_hash=junrvxocxwjwjp5jztgergifke)(6inkwr6cbtxv6flr665yduyu2y) + version: 0.28.3(patch_hash=9dcf4fca858e9dbe3b04ba9a577a6a45b074b6ebbab97ab781834c3b609a22b5)(ab42fd472452fee622155669e574e69a) '@typespec/openapi': specifier: 1.11.0 - version: 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) '@typespec/openapi3': specifier: 1.11.0 - version: 1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/openapi@1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(f243169b9b94d9a7b11d6d0ca80911b9) '@typespec/prettier-plugin-typespec': specifier: 1.12.0 version: 1.12.0 '@typespec/rest': specifier: 0.81.0 - version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) '@typespec/versioning': specifier: 0.81.0 - version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + version: 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) packages/typespec-typescript: dependencies: @@ -148,19 +148,19 @@ importers: version: 0.23.0 '@typespec/compiler': specifier: 1.11.0 - version: 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + version: 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) '@typespec/emitter-framework': specifier: 0.17.0 - version: 0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + version: 0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) '@typespec/http': specifier: 1.11.0 - version: 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) '@typespec/http-client': specifier: 0.15.1 - version: 0.15.1(patch_hash=hzsn74ah7pvsbuy64whbvwa4my)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) + version: 0.15.1(patch_hash=868c8bb8ad6e321347846c4a7fee764bae6a04300db4dab27521e11dcfec61af)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) '@typespec/openapi': specifier: 1.11.0 - version: 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + version: 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) typescript: specifier: 6.0.3 version: 6.0.3 @@ -965,36 +965,42 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.3': resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.3': resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.3': resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.3': resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.3': resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.3': resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} @@ -1751,24 +1757,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -2600,52 +2610,52 @@ snapshots: - bufferutil - utf-8-validate - '@azure-tools/typespec-autorest@0.67.0(byhmcmjm5wvcyili6vfnju77si)': + '@azure-tools/typespec-autorest@0.67.0(f8fb9bd330bad0373fc685cd461b75e9)': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) - '@azure-tools/typespec-azure-resource-manager': 0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4) - '@azure-tools/typespec-client-generator-core': 0.68.4(iddsji6x7kkq6q74egd42apaja) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) + '@azure-tools/typespec-azure-resource-manager': 0.67.0(f6c56f1404e3e5f37008eebf829701b8) + '@azure-tools/typespec-client-generator-core': 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) optionalDependencies: - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))': + '@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@azure-tools/typespec-azure-resource-manager@0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4)': + '@azure-tools/typespec-azure-resource-manager@0.67.0(f6c56f1404e3e5f37008eebf829701b8)': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) change-case: 5.4.4 pluralize: 8.0.0 - '@azure-tools/typespec-azure-rulesets@0.67.0(@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))))(@azure-tools/typespec-azure-resource-manager@0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4))(@azure-tools/typespec-client-generator-core@0.68.4(iddsji6x7kkq6q74egd42apaja))(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@azure-tools/typespec-azure-rulesets@0.67.0(e0cf5ccc6837ab58d03df92798efbe68)': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) - '@azure-tools/typespec-azure-resource-manager': 0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4) - '@azure-tools/typespec-client-generator-core': 0.68.4(iddsji6x7kkq6q74egd42apaja) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) + '@azure-tools/typespec-azure-resource-manager': 0.67.0(f6c56f1404e3e5f37008eebf829701b8) + '@azure-tools/typespec-client-generator-core': 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@azure-tools/typespec-client-generator-core@0.68.4(iddsji6x7kkq6q74egd42apaja)': + '@azure-tools/typespec-client-generator-core@0.68.4(d52a96869aeec1cdda9cd7e81cd231b5)': dependencies: - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) change-case: 5.4.4 pluralize: 8.0.0 yaml: 2.9.0 @@ -3454,11 +3464,11 @@ snapshots: dependencies: '@types/node': 25.9.2 - '@typespec/asset-emitter@0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@typespec/asset-emitter@0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)': + '@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)': dependencies: '@babel/code-frame': 7.29.0 '@inquirer/prompts': 8.4.2(@types/node@25.9.2) @@ -3480,32 +3490,32 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@typespec/emitter-framework@0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@typespec/emitter-framework@0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': dependencies: '@alloy-js/core': 0.23.1 '@alloy-js/csharp': 0.22.0 '@alloy-js/python': 0.3.0 '@alloy-js/typescript': 0.23.0 - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) optional: true - '@typespec/http-client-python@0.28.3(patch_hash=junrvxocxwjwjp5jztgergifke)(6inkwr6cbtxv6flr665yduyu2y)': - dependencies: - '@azure-tools/typespec-autorest': 0.67.0(byhmcmjm5wvcyili6vfnju77si) - '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))) - '@azure-tools/typespec-azure-resource-manager': 0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4) - '@azure-tools/typespec-azure-rulesets': 0.67.0(@azure-tools/typespec-azure-core@0.67.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))))(@azure-tools/typespec-azure-resource-manager@0.67.0(qxjcmodvgxkwch2k7ku5jgfxf4))(@azure-tools/typespec-client-generator-core@0.68.4(iddsji6x7kkq6q74egd42apaja))(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@azure-tools/typespec-client-generator-core': 0.68.4(iddsji6x7kkq6q74egd42apaja) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/http-client-python@0.28.3(patch_hash=9dcf4fca858e9dbe3b04ba9a577a6a45b074b6ebbab97ab781834c3b609a22b5)(ab42fd472452fee622155669e574e69a)': + dependencies: + '@azure-tools/typespec-autorest': 0.67.0(f8fb9bd330bad0373fc685cd461b75e9) + '@azure-tools/typespec-azure-core': 0.67.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))) + '@azure-tools/typespec-azure-resource-manager': 0.67.0(f6c56f1404e3e5f37008eebf829701b8) + '@azure-tools/typespec-azure-rulesets': 0.67.0(e0cf5ccc6837ab58d03df92798efbe68) + '@azure-tools/typespec-client-generator-core': 0.68.4(d52a96869aeec1cdda9cd7e81cd231b5) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/rest': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) js-yaml: 4.1.1 marked: 15.0.12 pyodide: 0.26.2 @@ -3515,92 +3525,92 @@ snapshots: - bufferutil - utf-8-validate - '@typespec/http-client@0.15.1(patch_hash=hzsn74ah7pvsbuy64whbvwa4my)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))': + '@typespec/http-client@0.15.1(patch_hash=868c8bb8ad6e321347846c4a7fee764bae6a04300db4dab27521e11dcfec61af)(@alloy-js/core@0.23.1)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/emitter-framework@0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))': dependencies: '@alloy-js/core': 0.23.1 '@alloy-js/typescript': 0.23.0 - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/emitter-framework': 0.17.0(patch_hash=jqz7bjrpz73vbtzb63j5jq5p4q)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/emitter-framework': 0.17.0(patch_hash=1924b26e8238a02e15c23759ab0a5c66ee037cfe4efba57add286ffe232b81cd)(@alloy-js/core@0.23.1)(@alloy-js/csharp@0.22.0)(@alloy-js/python@0.3.0)(@alloy-js/typescript@0.23.0)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': + '@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) optionalDependencies: - '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': dependencies: - '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) yaml: 2.8.3 - '@typespec/openapi3@1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/json-schema@1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/openapi@1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': + '@typespec/openapi3@1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(972722f3b2beb4f4aa97c5f6210f12c6)': dependencies: '@scalar/json-magic': 0.11.7 '@scalar/openapi-parser': 0.24.17 '@scalar/openapi-types': 0.5.4 - '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) yaml: 2.8.3 optionalDependencies: - '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/sse': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/openapi3@1.11.0(patch_hash=pyfaeqet7wh7fkbeodck2dgrki)(m3dgrauyqqqdsmhf7bdsf4jfoi)': + '@typespec/openapi3@1.11.0(patch_hash=cef18a91d29848030707929c70aa810dbb5d532466155d211d4f49e1ac7a7d95)(f243169b9b94d9a7b11d6d0ca80911b9)': dependencies: '@scalar/json-magic': 0.11.7 '@scalar/openapi-parser': 0.24.17 '@scalar/openapi-types': 0.5.4 - '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/openapi': 1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/asset-emitter': 0.79.1(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/openapi': 1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) yaml: 2.8.3 optionalDependencies: - '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/sse': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/json-schema': 1.11.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/versioning': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/xml': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) - '@typespec/openapi@1.11.0(patch_hash=26lua3u5ftfsgp7iobehpb3hn4)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': + '@typespec/openapi@1.11.0(patch_hash=f3c538331cfd9a1478f99a7a039f3b067fc2e47edd1870495dc30459c37ec827)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) '@typespec/prettier-plugin-typespec@1.12.0': dependencies: prettier: 3.8.3 - '@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': + '@typespec/rest@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) - '@typespec/sse@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)))': + '@typespec/sse@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/events@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))(@typespec/http@1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) - '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) - '@typespec/http': 1.11.0(patch_hash=7net4uhpgzyfqswqb76vhoaleu)(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))) - '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2)) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) + '@typespec/events': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) + '@typespec/http': 1.11.0(patch_hash=e5eb7d65296bdaff8a23f7b13f46c819ab5f5f0f14d1efd4e4a5694b13be1410)(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))(@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))) + '@typespec/streams': 0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2)) optional: true - '@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@typespec/streams@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) optional: true - '@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@typespec/versioning@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) - '@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2))': + '@typespec/xml@0.81.0(@typespec/compiler@1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2))': dependencies: - '@typespec/compiler': 1.11.0(patch_hash=riwjrattawzpgwt74lnbqx3sdy)(@types/node@25.9.2) + '@typespec/compiler': 1.11.0(patch_hash=3c2b359a3f53dc45d1cb921d164fdd02b562613bbd72527208ac5c1f1631b6a7)(@types/node@25.9.2) '@vitest/coverage-v8@4.1.8(vitest@4.1.8)': dependencies: From 3dc5afdf4842232b39f0ab2317a6e2f573cea325 Mon Sep 17 00:00:00 2001 From: Andras Toth <4157749+tothandras@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:16:56 +0200 Subject: [PATCH 3/4] chore: review comments --- .../aip-client-javascript/src/funcs/addons.ts | 35 ++++- .../aip-client-javascript/src/funcs/apps.ts | 7 +- .../src/funcs/billing.ts | 21 ++- .../src/funcs/currencies.ts | 14 +- .../src/funcs/customers.ts | 129 +++++++++++++++--- .../src/funcs/entitlements.ts | 7 +- .../src/funcs/features.ts | 28 +++- .../src/funcs/invoices.ts | 7 +- .../src/funcs/llmCost.ts | 14 +- .../aip-client-javascript/src/funcs/meters.ts | 35 ++++- .../src/funcs/planAddons.ts | 50 ++++++- .../aip-client-javascript/src/funcs/plans.ts | 35 ++++- .../src/funcs/subscriptions.ts | 54 +++++++- .../aip-client-javascript/src/funcs/tax.ts | 21 ++- .../aip-client-javascript/src/index.ts | 2 +- .../aip-client-javascript/src/lib/wire.ts | 52 +++++-- .../src/models/schemas.ts | 2 +- .../aip-client-javascript/tests/wire.spec.ts | 39 +++++- .../typespec-typescript/src/ZodOperations.tsx | 12 +- .../typespec-typescript/src/casing-gate.ts | 47 +++++-- .../typespec-typescript/src/runtime/wire.ts | 52 +++++-- .../typespec-typescript/src/sdk-files.ts | 15 +- 22 files changed, 584 insertions(+), 94 deletions(-) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/addons.ts b/api/spec/packages/aip-client-javascript/src/funcs/addons.ts index bf93c2659c..4f3435963a 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/addons.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/addons.ts @@ -76,7 +76,12 @@ export function updateAddon( req: UpdateAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}` + const path = `openmeter/addons/${(() => { + if (req.addonId === undefined) { + throw new Error('missing path parameter: addonId') + } + return encodeURIComponent(String(req.addonId)) + })()}` return request(() => { const body = toWire(req.body, schemas.updateAddonBody) if (client._options.validate) { @@ -99,7 +104,12 @@ export function getAddon( req: GetAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}` + const path = `openmeter/addons/${(() => { + if (req.addonId === undefined) { + throw new Error('missing path parameter: addonId') + } + return encodeURIComponent(String(req.addonId)) + })()}` return request(() => http(client) .get(path, options) @@ -118,7 +128,12 @@ export function deleteAddon( req: DeleteAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}` + const path = `openmeter/addons/${(() => { + if (req.addonId === undefined) { + throw new Error('missing path parameter: addonId') + } + return encodeURIComponent(String(req.addonId)) + })()}` return request(async () => { await http(client).delete(path, options) }) @@ -129,7 +144,12 @@ export function archiveAddon( req: ArchiveAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}/archive` + const path = `openmeter/addons/${(() => { + if (req.addonId === undefined) { + throw new Error('missing path parameter: addonId') + } + return encodeURIComponent(String(req.addonId)) + })()}/archive` return request(() => http(client) .post(path, options) @@ -148,7 +168,12 @@ export function publishAddon( req: PublishAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/addons/${encodeURIComponent(String(req.addonId))}/publish` + const path = `openmeter/addons/${(() => { + if (req.addonId === undefined) { + throw new Error('missing path parameter: addonId') + } + return encodeURIComponent(String(req.addonId)) + })()}/publish` return request(() => http(client) .post(path, options) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/apps.ts b/api/spec/packages/aip-client-javascript/src/funcs/apps.ts index 5178805ba8..beef2610e4 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/apps.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/apps.ts @@ -42,7 +42,12 @@ export function getApp( req: GetAppRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/apps/${encodeURIComponent(String(req.appId))}` + const path = `openmeter/apps/${(() => { + if (req.appId === undefined) { + throw new Error('missing path parameter: appId') + } + return encodeURIComponent(String(req.appId)) + })()}` return request(() => http(client) .get(path, options) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/billing.ts b/api/spec/packages/aip-client-javascript/src/funcs/billing.ts index f64f3c91e8..dc7caf189f 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/billing.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/billing.ts @@ -70,7 +70,12 @@ export function getBillingProfile( req: GetBillingProfileRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/profiles/${encodeURIComponent(String(req.id))}` + const path = `openmeter/profiles/${(() => { + if (req.id === undefined) { + throw new Error('missing path parameter: id') + } + return encodeURIComponent(String(req.id)) + })()}` return request(() => http(client) .get(path, options) @@ -89,7 +94,12 @@ export function updateBillingProfile( req: UpdateBillingProfileRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/profiles/${encodeURIComponent(String(req.id))}` + const path = `openmeter/profiles/${(() => { + if (req.id === undefined) { + throw new Error('missing path parameter: id') + } + return encodeURIComponent(String(req.id)) + })()}` return request(() => { const body = toWire(req.body, schemas.updateBillingProfileBody) if (client._options.validate) { @@ -112,7 +122,12 @@ export function deleteBillingProfile( req: DeleteBillingProfileRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/profiles/${encodeURIComponent(String(req.id))}` + const path = `openmeter/profiles/${(() => { + if (req.id === undefined) { + throw new Error('missing path parameter: id') + } + return encodeURIComponent(String(req.id)) + })()}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts b/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts index 6411d7b571..bc036fd1e5 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts @@ -79,7 +79,12 @@ export function listCostBases( schemas.listCostBasesQueryParams, ), ) - const path = `openmeter/currencies/custom/${encodeURIComponent(String(req.currencyId))}/cost-bases` + const path = `openmeter/currencies/custom/${(() => { + if (req.currencyId === undefined) { + throw new Error('missing path parameter: currencyId') + } + return encodeURIComponent(String(req.currencyId)) + })()}/cost-bases` return request(() => http(client) .get(path, { ...options, searchParams }) @@ -98,7 +103,12 @@ export function createCostBasis( req: CreateCostBasisRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/currencies/custom/${encodeURIComponent(String(req.currencyId))}/cost-bases` + const path = `openmeter/currencies/custom/${(() => { + if (req.currencyId === undefined) { + throw new Error('missing path parameter: currencyId') + } + return encodeURIComponent(String(req.currencyId)) + })()}/cost-bases` return request(() => { const body = toWire(req.body, schemas.createCostBasisBody) if (client._options.validate) { diff --git a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts index e7c4db71ec..c0dd48d6ac 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts @@ -72,7 +72,12 @@ export function getCustomer( req: GetCustomerRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}` return request(() => http(client) .get(path, options) @@ -119,7 +124,12 @@ export function upsertCustomer( req: UpsertCustomerRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}` return request(() => { const body = toWire(req.body, schemas.upsertCustomerBody) if (client._options.validate) { @@ -142,7 +152,12 @@ export function deleteCustomer( req: DeleteCustomerRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}` return request(async () => { await http(client).delete(path, options) }) @@ -153,7 +168,12 @@ export function getCustomerBilling( req: GetCustomerBillingRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/billing` return request(() => http(client) .get(path, options) @@ -172,7 +192,12 @@ export function updateCustomerBilling( req: UpdateCustomerBillingRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/billing` return request(() => { const body = toWire(req.body, schemas.updateCustomerBillingBody) if (client._options.validate) { @@ -195,7 +220,12 @@ export function updateCustomerBillingAppData( req: UpdateCustomerBillingAppDataRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing/app-data` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/billing/app-data` return request(() => { const body = toWire(req.body, schemas.updateCustomerBillingAppDataBody) if (client._options.validate) { @@ -218,7 +248,12 @@ export function createCustomerStripeCheckoutSession( req: CreateCustomerStripeCheckoutSessionRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing/stripe/checkout-sessions` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/billing/stripe/checkout-sessions` return request(() => { const body = toWire( req.body, @@ -250,7 +285,12 @@ export function createCustomerStripePortalSession( req: CreateCustomerStripePortalSessionRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/billing/stripe/portal-sessions` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/billing/stripe/portal-sessions` return request(() => { const body = toWire(req.body, schemas.createCustomerStripePortalSessionBody) if (client._options.validate) { @@ -276,7 +316,12 @@ export function createCreditGrant( req: CreateCreditGrantRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/credits/grants` return request(() => { const body = toWire(req.body, schemas.createCreditGrantBody) if (client._options.validate) { @@ -299,7 +344,17 @@ export function getCreditGrant( req: GetCreditGrantRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants/${encodeURIComponent(String(req.creditGrantId))}` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/credits/grants/${(() => { + if (req.creditGrantId === undefined) { + throw new Error('missing path parameter: creditGrantId') + } + return encodeURIComponent(String(req.creditGrantId)) + })()}` return request(() => http(client) .get(path, options) @@ -327,7 +382,12 @@ export function listCreditGrants( schemas.listCreditGrantsQueryParams, ), ) - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/credits/grants` return request(() => http(client) .get(path, { ...options, searchParams }) @@ -355,7 +415,12 @@ export function getCustomerCreditBalance( schemas.getCustomerCreditBalanceQueryParams, ), ) - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/balance` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/credits/balance` return request(() => http(client) .get(path, { ...options, searchParams }) @@ -374,7 +439,12 @@ export function createCreditAdjustment( req: CreateCreditAdjustmentRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/adjustments` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/credits/adjustments` return request(() => { const body = toWire(req.body, schemas.createCreditAdjustmentBody) if (client._options.validate) { @@ -397,7 +467,17 @@ export function updateCreditGrantExternalSettlement( req: UpdateCreditGrantExternalSettlementRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/grants/${encodeURIComponent(String(req.creditGrantId))}/settlement/external` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/credits/grants/${(() => { + if (req.creditGrantId === undefined) { + throw new Error('missing path parameter: creditGrantId') + } + return encodeURIComponent(String(req.creditGrantId)) + })()}/settlement/external` return request(() => { const body = toWire( req.body, @@ -438,7 +518,12 @@ export function listCreditTransactions( schemas.listCreditTransactionsQueryParams, ), ) - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/credits/transactions` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/credits/transactions` return request(() => http(client) .get(path, { ...options, searchParams }) @@ -468,7 +553,12 @@ export function listCustomerCharges( schemas.listCustomerChargesQueryParams, ), ) - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/charges` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/charges` return request(() => http(client) .get(path, { ...options, searchParams }) @@ -487,7 +577,12 @@ export function createCustomerCharges( req: CreateCustomerChargesRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/charges` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/charges` return request(() => { const body = toWire(req.body, schemas.createCustomerChargesBody) if (client._options.validate) { diff --git a/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts b/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts index e67f53e92f..38e599f945 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/entitlements.ts @@ -13,7 +13,12 @@ export function listCustomerEntitlementAccess( req: ListCustomerEntitlementAccessRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/customers/${encodeURIComponent(String(req.customerId))}/entitlement-access` + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/entitlement-access` return request(() => http(client) .get(path, options) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/features.ts b/api/spec/packages/aip-client-javascript/src/funcs/features.ts index cdeea7cec0..88a7f7000f 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/features.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/features.ts @@ -74,7 +74,12 @@ export function getFeature( req: GetFeatureRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}` + const path = `openmeter/features/${(() => { + if (req.featureId === undefined) { + throw new Error('missing path parameter: featureId') + } + return encodeURIComponent(String(req.featureId)) + })()}` return request(() => http(client) .get(path, options) @@ -93,7 +98,12 @@ export function updateFeature( req: UpdateFeatureRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}` + const path = `openmeter/features/${(() => { + if (req.featureId === undefined) { + throw new Error('missing path parameter: featureId') + } + return encodeURIComponent(String(req.featureId)) + })()}` return request(() => { const body = toWire(req.body, schemas.updateFeatureBody) if (client._options.validate) { @@ -116,7 +126,12 @@ export function deleteFeature( req: DeleteFeatureRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}` + const path = `openmeter/features/${(() => { + if (req.featureId === undefined) { + throw new Error('missing path parameter: featureId') + } + return encodeURIComponent(String(req.featureId)) + })()}` return request(async () => { await http(client).delete(path, options) }) @@ -127,7 +142,12 @@ export function queryFeatureCost( req: QueryFeatureCostRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/features/${encodeURIComponent(String(req.featureId))}/cost/query` + const path = `openmeter/features/${(() => { + if (req.featureId === undefined) { + throw new Error('missing path parameter: featureId') + } + return encodeURIComponent(String(req.featureId)) + })()}/cost/query` return request(() => { const body = toWire(req.body, schemas.queryFeatureCostBody) if (client._options.validate) { diff --git a/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts b/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts index ffd21a0244..621e4ff576 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/invoices.ts @@ -13,7 +13,12 @@ export function getInvoice( req: GetInvoiceRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/billing/invoices/${encodeURIComponent(String(req.invoiceId))}` + const path = `openmeter/billing/invoices/${(() => { + if (req.invoiceId === undefined) { + throw new Error('missing path parameter: invoiceId') + } + return encodeURIComponent(String(req.invoiceId)) + })()}` return request(() => http(client) .get(path, options) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts b/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts index cef8d8ba02..e784f4cdc2 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts @@ -50,7 +50,12 @@ export function getLlmCostPrice( req: GetLlmCostPriceRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/llm-cost/prices/${encodeURIComponent(String(req.priceId))}` + const path = `openmeter/llm-cost/prices/${(() => { + if (req.priceId === undefined) { + throw new Error('missing path parameter: priceId') + } + return encodeURIComponent(String(req.priceId)) + })()}` return request(() => http(client) .get(path, options) @@ -118,7 +123,12 @@ export function deleteLlmCostOverride( req: DeleteLlmCostOverrideRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/llm-cost/overrides/${encodeURIComponent(String(req.priceId))}` + const path = `openmeter/llm-cost/overrides/${(() => { + if (req.priceId === undefined) { + throw new Error('missing path parameter: priceId') + } + return encodeURIComponent(String(req.priceId)) + })()}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/meters.ts b/api/spec/packages/aip-client-javascript/src/funcs/meters.ts index 3a42187132..09a380459c 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/meters.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/meters.ts @@ -48,7 +48,12 @@ export function getMeter( req: GetMeterRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}` + const path = `openmeter/meters/${(() => { + if (req.meterId === undefined) { + throw new Error('missing path parameter: meterId') + } + return encodeURIComponent(String(req.meterId)) + })()}` return request(() => http(client) .get(path, options) @@ -95,7 +100,12 @@ export function updateMeter( req: UpdateMeterRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}` + const path = `openmeter/meters/${(() => { + if (req.meterId === undefined) { + throw new Error('missing path parameter: meterId') + } + return encodeURIComponent(String(req.meterId)) + })()}` return request(() => { const body = toWire(req.body, schemas.updateMeterBody) if (client._options.validate) { @@ -118,7 +128,12 @@ export function deleteMeter( req: DeleteMeterRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}` + const path = `openmeter/meters/${(() => { + if (req.meterId === undefined) { + throw new Error('missing path parameter: meterId') + } + return encodeURIComponent(String(req.meterId)) + })()}` return request(async () => { await http(client).delete(path, options) }) @@ -129,7 +144,12 @@ export function queryMeter( req: QueryMeterRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}/query` + const path = `openmeter/meters/${(() => { + if (req.meterId === undefined) { + throw new Error('missing path parameter: meterId') + } + return encodeURIComponent(String(req.meterId)) + })()}/query` return request(() => { const body = toWire(req.body, schemas.queryMeterBody) if (client._options.validate) { @@ -154,7 +174,12 @@ export function queryMeterCsv( ): Promise> { const headers = new Headers(options?.headers as HeadersInit | undefined) headers.set('accept', 'text/csv') - const path = `openmeter/meters/${encodeURIComponent(String(req.meterId))}/query` + const path = `openmeter/meters/${(() => { + if (req.meterId === undefined) { + throw new Error('missing path parameter: meterId') + } + return encodeURIComponent(String(req.meterId)) + })()}/query` return request(() => { const body = toWire(req.body, schemas.queryMeterCsvBody) if (client._options.validate) { diff --git a/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts b/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts index d2bff19ead..62a8769d19 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts @@ -30,7 +30,12 @@ export function listPlanAddons( schemas.listPlanAddonsQueryParams, ), ) - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}/addons` return request(() => http(client) .get(path, { ...options, searchParams }) @@ -49,7 +54,12 @@ export function createPlanAddon( req: CreatePlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}/addons` return request(() => { const body = toWire(req.body, schemas.createPlanAddonBody) if (client._options.validate) { @@ -72,7 +82,17 @@ export function getPlanAddon( req: GetPlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons/${encodeURIComponent(String(req.planAddonId))}` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}/addons/${(() => { + if (req.planAddonId === undefined) { + throw new Error('missing path parameter: planAddonId') + } + return encodeURIComponent(String(req.planAddonId)) + })()}` return request(() => http(client) .get(path, options) @@ -91,7 +111,17 @@ export function updatePlanAddon( req: UpdatePlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons/${encodeURIComponent(String(req.planAddonId))}` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}/addons/${(() => { + if (req.planAddonId === undefined) { + throw new Error('missing path parameter: planAddonId') + } + return encodeURIComponent(String(req.planAddonId)) + })()}` return request(() => { const body = toWire(req.body, schemas.updatePlanAddonBody) if (client._options.validate) { @@ -114,7 +144,17 @@ export function deletePlanAddon( req: DeletePlanAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/addons/${encodeURIComponent(String(req.planAddonId))}` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}/addons/${(() => { + if (req.planAddonId === undefined) { + throw new Error('missing path parameter: planAddonId') + } + return encodeURIComponent(String(req.planAddonId)) + })()}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/plans.ts b/api/spec/packages/aip-client-javascript/src/funcs/plans.ts index 7bd75f381d..8618e438c8 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/plans.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/plans.ts @@ -76,7 +76,12 @@ export function updatePlan( req: UpdatePlanRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}` return request(() => { const body = toWire(req.body, schemas.updatePlanBody) if (client._options.validate) { @@ -99,7 +104,12 @@ export function getPlan( req: GetPlanRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}` return request(() => http(client) .get(path, options) @@ -118,7 +128,12 @@ export function deletePlan( req: DeletePlanRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}` return request(async () => { await http(client).delete(path, options) }) @@ -129,7 +144,12 @@ export function archivePlan( req: ArchivePlanRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/archive` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}/archive` return request(() => http(client) .post(path, options) @@ -148,7 +168,12 @@ export function publishPlan( req: PublishPlanRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/plans/${encodeURIComponent(String(req.planId))}/publish` + const path = `openmeter/plans/${(() => { + if (req.planId === undefined) { + throw new Error('missing path parameter: planId') + } + return encodeURIComponent(String(req.planId)) + })()}/publish` return request(() => http(client) .post(path, options) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts b/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts index 119ad380c4..41c585bf7a 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts @@ -80,7 +80,12 @@ export function getSubscription( req: GetSubscriptionRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}` + const path = `openmeter/subscriptions/${(() => { + if (req.subscriptionId === undefined) { + throw new Error('missing path parameter: subscriptionId') + } + return encodeURIComponent(String(req.subscriptionId)) + })()}` return request(() => http(client) .get(path, options) @@ -99,7 +104,12 @@ export function cancelSubscription( req: CancelSubscriptionRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/cancel` + const path = `openmeter/subscriptions/${(() => { + if (req.subscriptionId === undefined) { + throw new Error('missing path parameter: subscriptionId') + } + return encodeURIComponent(String(req.subscriptionId)) + })()}/cancel` return request(() => { const body = toWire(req.body, schemas.cancelSubscriptionBody) if (client._options.validate) { @@ -122,7 +132,12 @@ export function unscheduleCancelation( req: UnscheduleCancelationRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/unschedule-cancelation` + const path = `openmeter/subscriptions/${(() => { + if (req.subscriptionId === undefined) { + throw new Error('missing path parameter: subscriptionId') + } + return encodeURIComponent(String(req.subscriptionId)) + })()}/unschedule-cancelation` return request(() => http(client) .post(path, options) @@ -141,7 +156,12 @@ export function changeSubscription( req: ChangeSubscriptionRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/change` + const path = `openmeter/subscriptions/${(() => { + if (req.subscriptionId === undefined) { + throw new Error('missing path parameter: subscriptionId') + } + return encodeURIComponent(String(req.subscriptionId)) + })()}/change` return request(() => { const body = toWire(req.body, schemas.changeSubscriptionBody) if (client._options.validate) { @@ -164,7 +184,12 @@ export function createSubscriptionAddon( req: CreateSubscriptionAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/addons` + const path = `openmeter/subscriptions/${(() => { + if (req.subscriptionId === undefined) { + throw new Error('missing path parameter: subscriptionId') + } + return encodeURIComponent(String(req.subscriptionId)) + })()}/addons` return request(() => { const body = toWire(req.body, schemas.createSubscriptionAddonBody) if (client._options.validate) { @@ -196,7 +221,12 @@ export function listSubscriptionAddons( schemas.listSubscriptionAddonsQueryParams, ), ) - const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/addons` + const path = `openmeter/subscriptions/${(() => { + if (req.subscriptionId === undefined) { + throw new Error('missing path parameter: subscriptionId') + } + return encodeURIComponent(String(req.subscriptionId)) + })()}/addons` return request(() => http(client) .get(path, { ...options, searchParams }) @@ -215,7 +245,17 @@ export function getSubscriptionAddon( req: GetSubscriptionAddonRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/subscriptions/${encodeURIComponent(String(req.subscriptionId))}/addons/${encodeURIComponent(String(req.subscriptionAddonId))}` + const path = `openmeter/subscriptions/${(() => { + if (req.subscriptionId === undefined) { + throw new Error('missing path parameter: subscriptionId') + } + return encodeURIComponent(String(req.subscriptionId)) + })()}/addons/${(() => { + if (req.subscriptionAddonId === undefined) { + throw new Error('missing path parameter: subscriptionAddonId') + } + return encodeURIComponent(String(req.subscriptionAddonId)) + })()}` return request(() => http(client) .get(path, options) diff --git a/api/spec/packages/aip-client-javascript/src/funcs/tax.ts b/api/spec/packages/aip-client-javascript/src/funcs/tax.ts index 382b4a1a53..fb654f2b8b 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/tax.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/tax.ts @@ -44,7 +44,12 @@ export function getTaxCode( req: GetTaxCodeRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/tax-codes/${encodeURIComponent(String(req.taxCodeId))}` + const path = `openmeter/tax-codes/${(() => { + if (req.taxCodeId === undefined) { + throw new Error('missing path parameter: taxCodeId') + } + return encodeURIComponent(String(req.taxCodeId)) + })()}` return request(() => http(client) .get(path, options) @@ -90,7 +95,12 @@ export function upsertTaxCode( req: UpsertTaxCodeRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/tax-codes/${encodeURIComponent(String(req.taxCodeId))}` + const path = `openmeter/tax-codes/${(() => { + if (req.taxCodeId === undefined) { + throw new Error('missing path parameter: taxCodeId') + } + return encodeURIComponent(String(req.taxCodeId)) + })()}` return request(() => { const body = toWire(req.body, schemas.upsertTaxCodeBody) if (client._options.validate) { @@ -113,7 +123,12 @@ export function deleteTaxCode( req: DeleteTaxCodeRequest, options?: RequestOptions, ): Promise> { - const path = `openmeter/tax-codes/${encodeURIComponent(String(req.taxCodeId))}` + const path = `openmeter/tax-codes/${(() => { + if (req.taxCodeId === undefined) { + throw new Error('missing path parameter: taxCodeId') + } + return encodeURIComponent(String(req.taxCodeId)) + })()}` return request(async () => { await http(client).delete(path, options) }) diff --git a/api/spec/packages/aip-client-javascript/src/index.ts b/api/spec/packages/aip-client-javascript/src/index.ts index 9f0313c375..a13b247b6f 100644 --- a/api/spec/packages/aip-client-javascript/src/index.ts +++ b/api/spec/packages/aip-client-javascript/src/index.ts @@ -18,7 +18,7 @@ export { Defaults } from './sdk/defaults.js' export { Governance } from './sdk/governance.js' export { Client } from './core.js' export { HTTPError } from './models/errors.js' -export { ValidationError } from './lib/wire.js' +export { ValidationError, DepthLimitExceededError } from './lib/wire.js' export { ServerList, Regions } from './lib/config.js' export type { SDKOptions, Region, ServerVariables } from './lib/config.js' diff --git a/api/spec/packages/aip-client-javascript/src/lib/wire.ts b/api/spec/packages/aip-client-javascript/src/lib/wire.ts index 3e24a49869..b7d803ec35 100644 --- a/api/spec/packages/aip-client-javascript/src/lib/wire.ts +++ b/api/spec/packages/aip-client-javascript/src/lib/wire.ts @@ -99,14 +99,35 @@ type Direction = { discriminatorKey: (camelKey: string) => string } +// A handful of schemas are genuinely self-referential (e.g. the `and`/`or` +// legs of a filter tree), so nesting depth is bounded only by the DATA the +// server sends, not by the schema. Without a limit, a crafted or +// accidentally-deep response recurses until the JS engine throws a raw +// `RangeError: Maximum call stack size exceeded` — still caught by request() +// and surfaced as Result.error, but as an opaque native error instead of a +// typed one. 500 levels is far beyond any real filter/record/array nesting +// in the API today; it exists to fail predictably, not to constrain valid data. +const MAX_WALK_DEPTH = 500 + +export class DepthLimitExceededError extends Error { + constructor() { + super(`wire mapping exceeded maximum nesting depth (${MAX_WALK_DEPTH})`) + this.name = 'DepthLimitExceededError' + } +} + function walk( data: unknown, schema: ZodType | undefined, dir: Direction, + depth = 0, ): unknown { if (data === null || data === undefined) { return data } + if (depth > MAX_WALK_DEPTH) { + throw new DepthLimitExceededError() + } const s = unwrap(schema) const d = def(s) if (Array.isArray(data)) { @@ -114,7 +135,7 @@ function walk( // (e.g. a single-or-batch body `T | T[]`); resolve the element schema from // whichever applies so array items are still walked with their shape. const element = arrayElement(s) - return data.map((item) => walk(item, element, dir)) + return data.map((item) => walk(item, element, dir, depth + 1)) } if (typeof data !== 'object') { return data @@ -123,11 +144,14 @@ function walk( if (d?.type === 'record') { // Record keys are user data (label/dimension names) — preserved verbatim. - // Only the value is walked, and only when it is model-shaped. + // Only the value is walked, and only when it is model-shaped. A null + // prototype avoids the `__proto__` key silently reassigning `out`'s own + // prototype instead of becoming a visible entry (user data may contain + // any key, including reserved object-literal property names). const valueSchema = hasRenamableShape(d.valueType) ? d.valueType : undefined - const out: Record = {} + const out: Record = Object.create(null) for (const [key, value] of Object.entries(record)) { - out[key] = valueSchema ? walk(value, valueSchema, dir) : value + out[key] = valueSchema ? walk(value, valueSchema, dir, depth + 1) : value } return out } @@ -138,12 +162,17 @@ function walk( // No confident match: leave keys untransformed rather than guess. return data } - return walk(data, variant, dir) + return walk(data, variant, dir, depth + 1) } if (d?.type === 'object') { const shape = shapeOf(s) ?? {} - const out: Record = {} + // A null prototype avoids two failure modes from data-controlled keys + // like `__proto__`/`constructor`: (1) `fieldFor` below reading an + // inherited Object.prototype member instead of correctly treating the + // key as schema-undeclared, and (2) the assignment at the end of this + // loop reassigning `out`'s own prototype instead of adding a visible key. + const out: Record = Object.create(null) for (const [key, value] of Object.entries(record)) { const fieldSchema = fieldFor(shape, key) // Keys the schema does not declare are dropped, so the result matches the @@ -151,7 +180,7 @@ function walk( if (fieldSchema === undefined) { continue } - out[dir.rename(key)] = walk(value, fieldSchema, dir) + out[dir.rename(key)] = walk(value, fieldSchema, dir, depth + 1) } return out } @@ -162,11 +191,18 @@ function walk( // Resolve a data key to its field schema. The schema is camelCase-keyed; a // wire→public data key is snake, so it is camelized to index the shape. +// Own-property checks (not `shape[key]`) so a data-controlled key like +// `__proto__` or `constructor` cannot resolve to an inherited +// Object.prototype member and be mistaken for a declared schema field. function fieldFor( shape: Record, dataKey: string, ): ZodType | undefined { - return shape[dataKey] ?? shape[toCamelCase(dataKey)] + if (Object.hasOwn(shape, dataKey)) { + return shape[dataKey] + } + const camelKey = toCamelCase(dataKey) + return Object.hasOwn(shape, camelKey) ? shape[camelKey] : undefined } function selectVariant( diff --git a/api/spec/packages/aip-client-javascript/src/models/schemas.ts b/api/spec/packages/aip-client-javascript/src/models/schemas.ts index 211371adc3..beeaf37418 100644 --- a/api/spec/packages/aip-client-javascript/src/models/schemas.ts +++ b/api/spec/packages/aip-client-javascript/src/models/schemas.ts @@ -11788,7 +11788,7 @@ export const listTaxCodesQueryParamsWire = z.object({ }) .optional() .describe('Determines which page of the collection to retrieve.'), - includeDeleted: z.coerce + include_deleted: z.coerce .boolean() .optional() .describe('Include deleted tax codes in the response.'), diff --git a/api/spec/packages/aip-client-javascript/tests/wire.spec.ts b/api/spec/packages/aip-client-javascript/tests/wire.spec.ts index 47d8cadc6f..291c3eea2b 100644 --- a/api/spec/packages/aip-client-javascript/tests/wire.spec.ts +++ b/api/spec/packages/aip-client-javascript/tests/wire.spec.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it } from 'vitest' import { z } from 'zod' import { Client, funcs, ValidationError } from '../src/index.js' import * as schemas from '../src/models/schemas.js' -import { fromWire, toWire } from '../src/lib/wire.js' +import { DepthLimitExceededError, fromWire, toWire } from '../src/lib/wire.js' beforeEach(() => { fetchMock.mockReset() @@ -32,6 +32,43 @@ describe('wire mapper (toWire/fromWire over real schemas)', () => { expect(back.features.my_user_feature.has_access).toBe(true) }) + it('preserves a record entry keyed "__proto__" as a visible own property', () => { + // A literal object expression treats `__proto__` as a prototype setter, + // not a data key — build it the way a real HTTP response body arrives + // (JSON.parse always yields `__proto__` as an own enumerable property). + const wire = JSON.parse('{"features":{"__proto__":{"has_access":true}}}') + const camel = fromWire(wire, schemas.governanceQueryResult) as any + expect(Object.keys(camel.features)).toEqual(['__proto__']) + expect(camel.features.__proto__.hasAccess).toBe(true) + expect(Object.getPrototypeOf({})).toBe(Object.prototype) + }) + + it('treats a data key of "constructor" as an unknown field, not a shape hit', () => { + const wire = JSON.parse('{"id":"x","constructor":"evil"}') + const camel = fromWire(wire, schemas.taxCode) as any + expect(camel.constructor).toBeUndefined() + expect(Object.keys(camel)).not.toContain('constructor') + }) + + it('throws a typed DepthLimitExceededError instead of a raw stack overflow on a deeply nested self-referential filter', () => { + let deep: unknown = { eq: 'leaf' } + for (let i = 0; i < 1000; i++) { + deep = { and: [deep] } + } + expect(() => fromWire(deep, schemas.queryFilterString)).toThrow( + DepthLimitExceededError, + ) + }) + + it('walks a moderately nested filter without hitting the depth limit', () => { + let nested: unknown = { eq: 'leaf' } + for (let i = 0; i < 20; i++) { + nested = { and: [nested] } + } + const wire = { and: [nested] } + expect(() => fromWire(wire, schemas.queryFilterString)).not.toThrow() + }) + it('handles a multi-word discriminator (collection_method)', () => { const wire = { collection_method: 'charge_automatically' } const camel = fromWire(wire, schemas.workflowPaymentSettings) as any diff --git a/api/spec/packages/typespec-typescript/src/ZodOperations.tsx b/api/spec/packages/typespec-typescript/src/ZodOperations.tsx index e33434d703..b743ac71e1 100644 --- a/api/spec/packages/typespec-typescript/src/ZodOperations.tsx +++ b/api/spec/packages/typespec-typescript/src/ZodOperations.tsx @@ -19,6 +19,7 @@ import { callPart, CoerceContext, toCamelCase, + useWireMode, zodMemberExpr, } from './utils.jsx' @@ -108,7 +109,16 @@ interface ParamLeaf { prop: ModelProperty } -function paramObject(params: ParamLeaf[], camelize: boolean): Children { +function paramObject( + params: ParamLeaf[], + camelizeInCamelPass: boolean, +): Children { + // `p.name` is the HTTP-binding wire name. Camelize it only for the public + // (camel) pass; the wire pass (emitted under WireModeContext, see + // emitter.tsx's `…Wire` re-render) must keep the raw wire name so a + // `*QueryParamsWire` schema actually matches the querystring sent on the + // wire — otherwise it silently describes the wrong (camelCase) shape. + const camelize = camelizeInCamelPass && !useWireMode() return ( {zodMemberExpr( diff --git a/api/spec/packages/typespec-typescript/src/casing-gate.ts b/api/spec/packages/typespec-typescript/src/casing-gate.ts index 407a34ac0a..4dd2708f41 100644 --- a/api/spec/packages/typespec-typescript/src/casing-gate.ts +++ b/api/spec/packages/typespec-typescript/src/casing-gate.ts @@ -10,7 +10,7 @@ import { import { $ } from '@typespec/compiler/typekit' import '@typespec/http/experimental/typekit' import { isCasingDerivable } from './casing.js' -import { bodyProperties } from './utils.jsx' +import { bodyProperties, emitsAsIntersection } from './utils.jsx' /** * The JSON wire name of a body property: its `@encodedName("application/json", …)` @@ -65,7 +65,36 @@ export function assertCasingDerivable( // Unions the boundary mapper actually walks: those reachable from a request body // or a success response. Error-envelope unions (e.g. `InvalidParameter` via // `badRequest`) are excluded — they are consumed by `to-error.ts`, never mapped. - const mappedUnions = mappedReachableUnions(program, operations) + const { unions: mappedUnions, models: mappedModels } = mappedReachableTypes( + program, + operations, + ) + + // Models the mapper would walk that emit as `z.intersection(...)` (a record + // spread combined with named fields): the walker's object/record branches + // dispatch on `def.type`, and zod has no `"intersection"` case in that + // dispatch, so such a schema would silently pass through untransformed — + // the record side keeps its wire casing, the named-field side keeps its + // wire casing too, and nothing gets camelized. Every intersection model + // today (`baseError`/`badRequest`) is error-envelope-only and bypasses the + // mapper entirely, so this fails the build the moment that stops being + // true rather than letting a future model silently ship the wrong casing. + const intersectionModels: string[] = [] + for (const model of mappedModels) { + if (emitsAsIntersection(program, model)) { + intersectionModels.push(model.name || '') + } + } + if (intersectionModels.length > 0) { + throw new Error( + `camelCase SDK: ${intersectionModels.length} model(s) reachable from a ` + + `request body or success response emit as z.intersection(...), which the ` + + `wire mapper cannot walk (no case for it). Restructure the model to avoid ` + + `combining a record indexer with named properties, or extend the mapper's ` + + `walk() with an intersection branch.\n ${intersectionModels.join('\n ')}`, + ) + } + const ambiguousUnions: string[] = [] for (const union of userUnions(program)) { const discriminated = tk.union.getDiscriminatedUnion(union) @@ -122,16 +151,17 @@ function objectVariantCount(program: Program, union: Union): number { } /** - * The unions the boundary mapper walks: those in the transitive closure of every - * operation's request body and success-response body. Error responses are excluded - * (their bodies are read by the error path, not mapped). + * The unions and models the boundary mapper walks: those in the transitive closure + * of every operation's request body and success-response body. Error responses are + * excluded (their bodies are read by the error path, not mapped). */ -function mappedReachableUnions( +function mappedReachableTypes( program: Program, operations: Operation[], -): Set { +): { unions: Set; models: Set } { const tk = $(program) const unions = new Set() + const models = new Set() const seen = new Set() const visit = (type: Type | undefined): void => { if (!type || seen.has(type)) { @@ -146,6 +176,7 @@ function mappedReachableUnions( } break case 'Model': + models.add(type) if (type.indexer) { visit(type.indexer.value) } @@ -178,7 +209,7 @@ function mappedReachableUnions( } } } - return unions + return { unions, models } } function isSuccessStatus( diff --git a/api/spec/packages/typespec-typescript/src/runtime/wire.ts b/api/spec/packages/typespec-typescript/src/runtime/wire.ts index 3e24a49869..b7d803ec35 100644 --- a/api/spec/packages/typespec-typescript/src/runtime/wire.ts +++ b/api/spec/packages/typespec-typescript/src/runtime/wire.ts @@ -99,14 +99,35 @@ type Direction = { discriminatorKey: (camelKey: string) => string } +// A handful of schemas are genuinely self-referential (e.g. the `and`/`or` +// legs of a filter tree), so nesting depth is bounded only by the DATA the +// server sends, not by the schema. Without a limit, a crafted or +// accidentally-deep response recurses until the JS engine throws a raw +// `RangeError: Maximum call stack size exceeded` — still caught by request() +// and surfaced as Result.error, but as an opaque native error instead of a +// typed one. 500 levels is far beyond any real filter/record/array nesting +// in the API today; it exists to fail predictably, not to constrain valid data. +const MAX_WALK_DEPTH = 500 + +export class DepthLimitExceededError extends Error { + constructor() { + super(`wire mapping exceeded maximum nesting depth (${MAX_WALK_DEPTH})`) + this.name = 'DepthLimitExceededError' + } +} + function walk( data: unknown, schema: ZodType | undefined, dir: Direction, + depth = 0, ): unknown { if (data === null || data === undefined) { return data } + if (depth > MAX_WALK_DEPTH) { + throw new DepthLimitExceededError() + } const s = unwrap(schema) const d = def(s) if (Array.isArray(data)) { @@ -114,7 +135,7 @@ function walk( // (e.g. a single-or-batch body `T | T[]`); resolve the element schema from // whichever applies so array items are still walked with their shape. const element = arrayElement(s) - return data.map((item) => walk(item, element, dir)) + return data.map((item) => walk(item, element, dir, depth + 1)) } if (typeof data !== 'object') { return data @@ -123,11 +144,14 @@ function walk( if (d?.type === 'record') { // Record keys are user data (label/dimension names) — preserved verbatim. - // Only the value is walked, and only when it is model-shaped. + // Only the value is walked, and only when it is model-shaped. A null + // prototype avoids the `__proto__` key silently reassigning `out`'s own + // prototype instead of becoming a visible entry (user data may contain + // any key, including reserved object-literal property names). const valueSchema = hasRenamableShape(d.valueType) ? d.valueType : undefined - const out: Record = {} + const out: Record = Object.create(null) for (const [key, value] of Object.entries(record)) { - out[key] = valueSchema ? walk(value, valueSchema, dir) : value + out[key] = valueSchema ? walk(value, valueSchema, dir, depth + 1) : value } return out } @@ -138,12 +162,17 @@ function walk( // No confident match: leave keys untransformed rather than guess. return data } - return walk(data, variant, dir) + return walk(data, variant, dir, depth + 1) } if (d?.type === 'object') { const shape = shapeOf(s) ?? {} - const out: Record = {} + // A null prototype avoids two failure modes from data-controlled keys + // like `__proto__`/`constructor`: (1) `fieldFor` below reading an + // inherited Object.prototype member instead of correctly treating the + // key as schema-undeclared, and (2) the assignment at the end of this + // loop reassigning `out`'s own prototype instead of adding a visible key. + const out: Record = Object.create(null) for (const [key, value] of Object.entries(record)) { const fieldSchema = fieldFor(shape, key) // Keys the schema does not declare are dropped, so the result matches the @@ -151,7 +180,7 @@ function walk( if (fieldSchema === undefined) { continue } - out[dir.rename(key)] = walk(value, fieldSchema, dir) + out[dir.rename(key)] = walk(value, fieldSchema, dir, depth + 1) } return out } @@ -162,11 +191,18 @@ function walk( // Resolve a data key to its field schema. The schema is camelCase-keyed; a // wire→public data key is snake, so it is camelized to index the shape. +// Own-property checks (not `shape[key]`) so a data-controlled key like +// `__proto__` or `constructor` cannot resolve to an inherited +// Object.prototype member and be mistaken for a declared schema field. function fieldFor( shape: Record, dataKey: string, ): ZodType | undefined { - return shape[dataKey] ?? shape[toCamelCase(dataKey)] + if (Object.hasOwn(shape, dataKey)) { + return shape[dataKey] + } + const camelKey = toCamelCase(dataKey) + return Object.hasOwn(shape, camelKey) ? shape[camelKey] : undefined } function selectVariant( diff --git a/api/spec/packages/typespec-typescript/src/sdk-files.ts b/api/spec/packages/typespec-typescript/src/sdk-files.ts index 5cf1971831..187341a492 100644 --- a/api/spec/packages/typespec-typescript/src/sdk-files.ts +++ b/api/spec/packages/typespec-typescript/src/sdk-files.ts @@ -5,14 +5,19 @@ import { toCamelCase } from './casing.js' function pathExpr(op: SdkOperation): string { // Inline the path as a template literal: each `{param}` becomes the - // URL-encoded request field. Path params are required strings, so no - // missing-value guard is needed (unlike the generic encodePath used for the - // server-variable baseUrl). + // URL-encoded request field. Path params are typed as required strings, but + // that only holds under the TS compiler — a caller on `any`/plain JS, or one + // that builds the request object dynamically, can still omit one. Without a + // runtime check, `String(undefined)` renders the literal segment "undefined" + // into the URL, turning a client-side mistake into a confusing server request + // instead of an immediate, clear error (matching the guard `encodePath` has + // always thrown for the server-variable baseUrl). const path = op.path .replace(/^\//, '') .replace( /\{(\w+)\}/g, - (_, p: string) => `\${encodeURIComponent(String(req.${p}))}`, + (_, p: string) => + `\${(() => { if (req.${p} === undefined) { throw new Error('missing path parameter: ${p}') } return encodeURIComponent(String(req.${p})) })()}`, ) return `\`${path}\`` } @@ -402,7 +407,7 @@ export function indexFile(tags: string[], modelTypeNames: string[]): string { namespaceExports, `export { Client } from './core.js'`, `export { HTTPError } from './models/errors.js'`, - `export { ValidationError } from './lib/wire.js'`, + `export { ValidationError, DepthLimitExceededError } from './lib/wire.js'`, ``, `export { ServerList, Regions } from './lib/config.js'`, `export type { SDKOptions, Region, ServerVariables } from './lib/config.js'`, From 2067210132ca6b5d046ea236fd99d7c6a810ecde Mon Sep 17 00:00:00 2001 From: Andras Toth <4157749+tothandras@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:38:44 +0200 Subject: [PATCH 4/4] chore: review comments --- api/spec/AGENTS.md | 4 +- .../aip-client-javascript/src/funcs/addons.ts | 18 +-- .../aip-client-javascript/src/funcs/apps.ts | 18 +-- .../src/funcs/billing.ts | 18 +-- .../src/funcs/currencies.ts | 46 ++++--- .../src/funcs/customers.ts | 130 ++++++++++-------- .../aip-client-javascript/src/funcs/events.ts | 18 +-- .../src/funcs/features.ts | 18 +-- .../src/funcs/governance.ts | 16 ++- .../src/funcs/llmCost.ts | 36 ++--- .../aip-client-javascript/src/funcs/meters.ts | 18 +-- .../src/funcs/planAddons.ts | 26 ++-- .../aip-client-javascript/src/funcs/plans.ts | 18 +-- .../src/funcs/subscriptions.ts | 46 ++++--- .../aip-client-javascript/src/funcs/tax.ts | 18 +-- .../aip-client-javascript/src/lib/config.ts | 4 +- .../tests/wire-helpers.ts | 7 +- .../aip-client-javascript/tests/wire.spec.ts | 32 +++++ .../src/runtime-templates.ts | 2 +- .../typespec-typescript/src/sdk-files.ts | 88 ++++++------ 20 files changed, 336 insertions(+), 245 deletions(-) diff --git a/api/spec/AGENTS.md b/api/spec/AGENTS.md index b23ddab3ad..d73fea235c 100644 --- a/api/spec/AGENTS.md +++ b/api/spec/AGENTS.md @@ -198,10 +198,10 @@ hatch). ### Optional wire-payload validation (`validate` option) `SDKOptions.validate` (default **off**) turns on schema validation of the actual -snake*case wire payload: the request body after `toWire` (before sending) and the +`snake_case` wire payload: the request body after `toWire` (before sending) and the raw response body before `fromWire`. Validation uses the generated **`…Wire` schemas** in `models/schemas.ts` — every model and per-op body/response is emitted a -second time in a snake_case "wire" pass (`WireModeContext` in the emitter), keyed by +second time in a snake*case "wire" pass (`WireModeContext` in the emitter), keyed by the raw JSON wire name and made `z.strictObject`, so a wrong-shaped or leaked-camelCase wire field is **rejected, not silently stripped**. Open models (record spread, `emitsAsIntersection`, e.g. `baseError`) stay non-strict — strict diff --git a/api/spec/packages/aip-client-javascript/src/funcs/addons.ts b/api/spec/packages/aip-client-javascript/src/funcs/addons.ts index 4f3435963a..cb22344748 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/addons.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/addons.ts @@ -26,18 +26,20 @@ export function listAddons( req: ListAddonsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), filter: req.filter, }, schemas.listAddonsQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listAddonsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/addons', { ...options, searchParams }) .json() .then((data) => { @@ -45,8 +47,8 @@ export function listAddons( assertValid(schemas.listAddonsResponseWire, data) } return fromWire(data, schemas.listAddonsResponse) - }), - ) + }) + }) } export function createAddon( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/apps.ts b/api/spec/packages/aip-client-javascript/src/funcs/apps.ts index beef2610e4..5c28dce726 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/apps.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/apps.ts @@ -16,16 +16,18 @@ export function listApps( req: ListAppsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, }, schemas.listAppsQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listAppsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/apps', { ...options, searchParams }) .json() .then((data) => { @@ -33,8 +35,8 @@ export function listApps( assertValid(schemas.listAppsResponseWire, data) } return fromWire(data, schemas.listAppsResponse) - }), - ) + }) + }) } export function getApp( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/billing.ts b/api/spec/packages/aip-client-javascript/src/funcs/billing.ts index dc7caf189f..32b3530494 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/billing.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/billing.ts @@ -22,16 +22,18 @@ export function listBillingProfiles( req: ListBillingProfilesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, }, schemas.listBillingProfilesQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listBillingProfilesQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/profiles', { ...options, searchParams }) .json() .then((data) => { @@ -39,8 +41,8 @@ export function listBillingProfiles( assertValid(schemas.listBillingProfilesResponseWire, data) } return fromWire(data, schemas.listBillingProfilesResponse) - }), - ) + }) + }) } export function createBillingProfile( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts b/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts index bc036fd1e5..7ab676f66f 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/currencies.ts @@ -20,18 +20,20 @@ export function listCurrencies( req: ListCurrenciesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), filter: req.filter, }, schemas.listCurrenciesQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listCurrenciesQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/currencies', { ...options, searchParams }) .json() .then((data) => { @@ -39,8 +41,8 @@ export function listCurrencies( assertValid(schemas.listCurrenciesResponseWire, data) } return fromWire(data, schemas.listCurrenciesResponse) - }), - ) + }) + }) } export function createCustomCurrency( @@ -70,23 +72,25 @@ export function listCostBases( req: ListCostBasesRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( - { - filter: req.filter, - page: req.page, - }, - schemas.listCostBasesQueryParams, - ), - ) const path = `openmeter/currencies/custom/${(() => { if (req.currencyId === undefined) { throw new Error('missing path parameter: currencyId') } return encodeURIComponent(String(req.currencyId)) })()}/cost-bases` - return request(() => - http(client) + return request(() => { + const query = toWire( + { + filter: req.filter, + page: req.page, + }, + schemas.listCostBasesQueryParams, + ) + if (client._options.validate) { + assertValid(schemas.listCostBasesQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get(path, { ...options, searchParams }) .json() .then((data) => { @@ -94,8 +98,8 @@ export function listCostBases( assertValid(schemas.listCostBasesResponseWire, data) } return fromWire(data, schemas.listCostBasesResponse) - }), - ) + }) + }) } export function createCostBasis( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts index c0dd48d6ac..a736837bd0 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/customers.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/customers.ts @@ -96,18 +96,20 @@ export function listCustomers( req: ListCustomersRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), filter: req.filter, }, schemas.listCustomersQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listCustomersQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/customers', { ...options, searchParams }) .json() .then((data) => { @@ -115,8 +117,8 @@ export function listCustomers( assertValid(schemas.listCustomersResponseWire, data) } return fromWire(data, schemas.listCustomersResponse) - }), - ) + }) + }) } export function upsertCustomer( @@ -373,23 +375,25 @@ export function listCreditGrants( req: ListCreditGrantsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( - { - page: req.page, - filter: req.filter, - }, - schemas.listCreditGrantsQueryParams, - ), - ) const path = `openmeter/customers/${(() => { if (req.customerId === undefined) { throw new Error('missing path parameter: customerId') } return encodeURIComponent(String(req.customerId)) })()}/credits/grants` - return request(() => - http(client) + return request(() => { + const query = toWire( + { + page: req.page, + filter: req.filter, + }, + schemas.listCreditGrantsQueryParams, + ) + if (client._options.validate) { + assertValid(schemas.listCreditGrantsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get(path, { ...options, searchParams }) .json() .then((data) => { @@ -397,8 +401,8 @@ export function listCreditGrants( assertValid(schemas.listCreditGrantsResponseWire, data) } return fromWire(data, schemas.listCreditGrantsResponse) - }), - ) + }) + }) } export function getCustomerCreditBalance( @@ -406,23 +410,25 @@ export function getCustomerCreditBalance( req: GetCustomerCreditBalanceRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( - { - timestamp: req.timestamp, - filter: req.filter, - }, - schemas.getCustomerCreditBalanceQueryParams, - ), - ) const path = `openmeter/customers/${(() => { if (req.customerId === undefined) { throw new Error('missing path parameter: customerId') } return encodeURIComponent(String(req.customerId)) })()}/credits/balance` - return request(() => - http(client) + return request(() => { + const query = toWire( + { + timestamp: req.timestamp, + filter: req.filter, + }, + schemas.getCustomerCreditBalanceQueryParams, + ) + if (client._options.validate) { + assertValid(schemas.getCustomerCreditBalanceQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get(path, { ...options, searchParams }) .json() .then((data) => { @@ -430,8 +436,8 @@ export function getCustomerCreditBalance( assertValid(schemas.getCustomerCreditBalanceResponseWire, data) } return fromWire(data, schemas.getCustomerCreditBalanceResponse) - }), - ) + }) + }) } export function createCreditAdjustment( @@ -509,23 +515,25 @@ export function listCreditTransactions( req: ListCreditTransactionsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( - { - page: req.page, - filter: req.filter, - }, - schemas.listCreditTransactionsQueryParams, - ), - ) const path = `openmeter/customers/${(() => { if (req.customerId === undefined) { throw new Error('missing path parameter: customerId') } return encodeURIComponent(String(req.customerId)) })()}/credits/transactions` - return request(() => - http(client) + return request(() => { + const query = toWire( + { + page: req.page, + filter: req.filter, + }, + schemas.listCreditTransactionsQueryParams, + ) + if (client._options.validate) { + assertValid(schemas.listCreditTransactionsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get(path, { ...options, searchParams }) .json() .then((data) => { @@ -533,8 +541,8 @@ export function listCreditTransactions( assertValid(schemas.listCreditTransactionsResponseWire, data) } return fromWire(data, schemas.listCreditTransactionsResponse) - }), - ) + }) + }) } export function listCustomerCharges( @@ -542,8 +550,14 @@ export function listCustomerCharges( req: ListCustomerChargesRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + const path = `openmeter/customers/${(() => { + if (req.customerId === undefined) { + throw new Error('missing path parameter: customerId') + } + return encodeURIComponent(String(req.customerId)) + })()}/charges` + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), @@ -551,16 +565,12 @@ export function listCustomerCharges( expand: req.expand, }, schemas.listCustomerChargesQueryParams, - ), - ) - const path = `openmeter/customers/${(() => { - if (req.customerId === undefined) { - throw new Error('missing path parameter: customerId') + ) + if (client._options.validate) { + assertValid(schemas.listCustomerChargesQueryParamsWire, query) } - return encodeURIComponent(String(req.customerId)) - })()}/charges` - return request(() => - http(client) + const searchParams = toURLSearchParams(query) + return http(client) .get(path, { ...options, searchParams }) .json() .then((data) => { @@ -568,8 +578,8 @@ export function listCustomerCharges( assertValid(schemas.listCustomerChargesResponseWire, data) } return fromWire(data, schemas.listCustomerChargesResponse) - }), - ) + }) + }) } export function createCustomerCharges( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/events.ts b/api/spec/packages/aip-client-javascript/src/funcs/events.ts index ffde9dc695..f4734d4e63 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/events.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/events.ts @@ -16,18 +16,20 @@ export function listMeteringEvents( req: ListMeteringEventsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, filter: req.filter, sort: encodeSort(req.sort, toSnakeCase), }, schemas.listMeteringEventsQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listMeteringEventsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/events', { ...options, searchParams }) .json() .then((data) => { @@ -35,8 +37,8 @@ export function listMeteringEvents( assertValid(schemas.listMeteringEventsResponseWire, data) } return fromWire(data, schemas.listMeteringEventsResponse) - }), - ) + }) + }) } export function ingestMeteringEvents( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/features.ts b/api/spec/packages/aip-client-javascript/src/funcs/features.ts index 88a7f7000f..e1813357c2 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/features.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/features.ts @@ -24,18 +24,20 @@ export function listFeatures( req: ListFeaturesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), filter: req.filter, }, schemas.listFeaturesQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listFeaturesQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/features', { ...options, searchParams }) .json() .then((data) => { @@ -43,8 +45,8 @@ export function listFeatures( assertValid(schemas.listFeaturesResponseWire, data) } return fromWire(data, schemas.listFeaturesResponse) - }), - ) + }) + }) } export function createFeature( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/governance.ts b/api/spec/packages/aip-client-javascript/src/funcs/governance.ts index 42b2354a7c..5749d143f1 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/governance.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/governance.ts @@ -14,19 +14,21 @@ export function queryGovernanceAccess( req: QueryGovernanceAccessRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const body = toWire(req.body, schemas.queryGovernanceAccessBody) + if (client._options.validate) { + assertValid(schemas.queryGovernanceAccessBodyWire, body) + } + const query = toWire( { page: req.page, }, schemas.queryGovernanceAccessQueryParams, - ), - ) - return request(() => { - const body = toWire(req.body, schemas.queryGovernanceAccessBody) + ) if (client._options.validate) { - assertValid(schemas.queryGovernanceAccessBodyWire, body) + assertValid(schemas.queryGovernanceAccessQueryParamsWire, query) } + const searchParams = toURLSearchParams(query) return http(client) .post('openmeter/governance/query', { ...options, diff --git a/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts b/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts index e784f4cdc2..c19cfeaaf8 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/llmCost.ts @@ -22,18 +22,20 @@ export function listLlmCostPrices( req: ListLlmCostPricesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { filter: req.filter, sort: encodeSort(req.sort, toSnakeCase), page: req.page, }, schemas.listLlmCostPricesQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listLlmCostPricesQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/llm-cost/prices', { ...options, searchParams }) .json() .then((data) => { @@ -41,8 +43,8 @@ export function listLlmCostPrices( assertValid(schemas.listLlmCostPricesResponseWire, data) } return fromWire(data, schemas.listLlmCostPricesResponse) - }), - ) + }) + }) } export function getLlmCostPrice( @@ -74,17 +76,19 @@ export function listLlmCostOverrides( req: ListLlmCostOverridesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { filter: req.filter, page: req.page, }, schemas.listLlmCostOverridesQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listLlmCostOverridesQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/llm-cost/overrides', { ...options, searchParams }) .json() .then((data) => { @@ -92,8 +96,8 @@ export function listLlmCostOverrides( assertValid(schemas.listLlmCostOverridesResponseWire, data) } return fromWire(data, schemas.listLlmCostOverridesResponse) - }), - ) + }) + }) } export function createLlmCostOverride( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/meters.ts b/api/spec/packages/aip-client-javascript/src/funcs/meters.ts index 09a380459c..183455b6d1 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/meters.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/meters.ts @@ -72,18 +72,20 @@ export function listMeters( req: ListMetersRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), filter: req.filter, }, schemas.listMetersQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listMetersQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/meters', { ...options, searchParams }) .json() .then((data) => { @@ -91,8 +93,8 @@ export function listMeters( assertValid(schemas.listMetersResponseWire, data) } return fromWire(data, schemas.listMetersResponse) - }), - ) + }) + }) } export function updateMeter( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts b/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts index 62a8769d19..01e944e1ec 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/planAddons.ts @@ -22,22 +22,24 @@ export function listPlanAddons( req: ListPlanAddonsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( - { - page: req.page, - }, - schemas.listPlanAddonsQueryParams, - ), - ) const path = `openmeter/plans/${(() => { if (req.planId === undefined) { throw new Error('missing path parameter: planId') } return encodeURIComponent(String(req.planId)) })()}/addons` - return request(() => - http(client) + return request(() => { + const query = toWire( + { + page: req.page, + }, + schemas.listPlanAddonsQueryParams, + ) + if (client._options.validate) { + assertValid(schemas.listPlanAddonsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get(path, { ...options, searchParams }) .json() .then((data) => { @@ -45,8 +47,8 @@ export function listPlanAddons( assertValid(schemas.listPlanAddonsResponseWire, data) } return fromWire(data, schemas.listPlanAddonsResponse) - }), - ) + }) + }) } export function createPlanAddon( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/plans.ts b/api/spec/packages/aip-client-javascript/src/funcs/plans.ts index 8618e438c8..251cd67611 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/plans.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/plans.ts @@ -26,18 +26,20 @@ export function listPlans( req: ListPlansRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), filter: req.filter, }, schemas.listPlansQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listPlansQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/plans', { ...options, searchParams }) .json() .then((data) => { @@ -45,8 +47,8 @@ export function listPlans( assertValid(schemas.listPlansResponseWire, data) } return fromWire(data, schemas.listPlansResponse) - }), - ) + }) + }) } export function createPlan( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts b/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts index 41c585bf7a..617c81bd04 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/subscriptions.ts @@ -52,18 +52,20 @@ export function listSubscriptions( req: ListSubscriptionsRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, sort: encodeSort(req.sort, toSnakeCase), filter: req.filter, }, schemas.listSubscriptionsQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listSubscriptionsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/subscriptions', { ...options, searchParams }) .json() .then((data) => { @@ -71,8 +73,8 @@ export function listSubscriptions( assertValid(schemas.listSubscriptionsResponseWire, data) } return fromWire(data, schemas.listSubscriptionsResponse) - }), - ) + }) + }) } export function getSubscription( @@ -212,23 +214,25 @@ export function listSubscriptionAddons( req: ListSubscriptionAddonsRequest, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( - { - page: req.page, - sort: encodeSort(req.sort, toSnakeCase), - }, - schemas.listSubscriptionAddonsQueryParams, - ), - ) const path = `openmeter/subscriptions/${(() => { if (req.subscriptionId === undefined) { throw new Error('missing path parameter: subscriptionId') } return encodeURIComponent(String(req.subscriptionId)) })()}/addons` - return request(() => - http(client) + return request(() => { + const query = toWire( + { + page: req.page, + sort: encodeSort(req.sort, toSnakeCase), + }, + schemas.listSubscriptionAddonsQueryParams, + ) + if (client._options.validate) { + assertValid(schemas.listSubscriptionAddonsQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get(path, { ...options, searchParams }) .json() .then((data) => { @@ -236,8 +240,8 @@ export function listSubscriptionAddons( assertValid(schemas.listSubscriptionAddonsResponseWire, data) } return fromWire(data, schemas.listSubscriptionAddonsResponse) - }), - ) + }) + }) } export function getSubscriptionAddon( diff --git a/api/spec/packages/aip-client-javascript/src/funcs/tax.ts b/api/spec/packages/aip-client-javascript/src/funcs/tax.ts index fb654f2b8b..c9324bae26 100644 --- a/api/spec/packages/aip-client-javascript/src/funcs/tax.ts +++ b/api/spec/packages/aip-client-javascript/src/funcs/tax.ts @@ -68,17 +68,19 @@ export function listTaxCodes( req: ListTaxCodesRequest = {}, options?: RequestOptions, ): Promise> { - const searchParams = toURLSearchParams( - toWire( + return request(() => { + const query = toWire( { page: req.page, includeDeleted: req.includeDeleted, }, schemas.listTaxCodesQueryParams, - ), - ) - return request(() => - http(client) + ) + if (client._options.validate) { + assertValid(schemas.listTaxCodesQueryParamsWire, query) + } + const searchParams = toURLSearchParams(query) + return http(client) .get('openmeter/tax-codes', { ...options, searchParams }) .json() .then((data) => { @@ -86,8 +88,8 @@ export function listTaxCodes( assertValid(schemas.listTaxCodesResponseWire, data) } return fromWire(data, schemas.listTaxCodesResponse) - }), - ) + }) + }) } export function upsertTaxCode( diff --git a/api/spec/packages/aip-client-javascript/src/lib/config.ts b/api/spec/packages/aip-client-javascript/src/lib/config.ts index 02987e2eab..84d3e13b40 100644 --- a/api/spec/packages/aip-client-javascript/src/lib/config.ts +++ b/api/spec/packages/aip-client-javascript/src/lib/config.ts @@ -23,7 +23,9 @@ export interface SDKOptions extends Omit { * Validate request bodies and response payloads against their schemas. Off by * default: the SDK maps casing but does not validate, so additive server fields * never break clients. When on, a request body or response that fails its schema - * (missing/wrong-typed field, unknown enum value) rejects with a ValidationError. + * (missing/wrong-typed field, unknown enum value) returns a failed Result whose + * `error` is a ValidationError (validation runs inside the SDK's request + * handling, so it never rejects/throws). */ validate?: boolean } diff --git a/api/spec/packages/aip-client-javascript/tests/wire-helpers.ts b/api/spec/packages/aip-client-javascript/tests/wire-helpers.ts index f281eaf8a1..85f155ba49 100644 --- a/api/spec/packages/aip-client-javascript/tests/wire-helpers.ts +++ b/api/spec/packages/aip-client-javascript/tests/wire-helpers.ts @@ -114,7 +114,12 @@ export function collectFieldKeys( if (value && typeof value === 'object' && !(value instanceof Date)) { for (const [k, v] of Object.entries(value)) { if (k.startsWith('user_key_')) { - // Preserved user record key — not a schema field, skip it and its subtree. + // Preserved user record key — not itself a schema field, so it's excluded + // from the leak check. Its value can still be model-shaped (e.g. a + // governance feature-access record value), so keep walking into it — + // skipping the value too would blind the leak check to casing bugs in + // any schema field nested under a record. + collectFieldKeys(v, keys) continue } keys.push(k) diff --git a/api/spec/packages/aip-client-javascript/tests/wire.spec.ts b/api/spec/packages/aip-client-javascript/tests/wire.spec.ts index 291c3eea2b..9051eb7dec 100644 --- a/api/spec/packages/aip-client-javascript/tests/wire.spec.ts +++ b/api/spec/packages/aip-client-javascript/tests/wire.spec.ts @@ -346,6 +346,38 @@ describe('optional schema validation (validate option)', () => { expect(result.ok).toBe(false) expect(result.error).toBeInstanceOf(ValidationError) }) + + it('rejects a bad query object before the request is sent, same as a bad body', async () => { + // listMeters has no request body, only query params; the wire query object + // (built by toWire) must still be checked against its …QueryParamsWire schema + // when validate is on, the same guarantee bodies already had. + fetchMock.route('*', { + body: { + data: [goodMeter], + meta: { page: { number: 1, size: 10, total: 1 } }, + }, + headers: { 'Content-Type': 'application/json' }, + }) + const result = await funcs.listMeters(validatingClient(), { + page: { size: Number.NaN }, + }) + expect(result.ok).toBe(false) + expect(result.error).toBeInstanceOf(ValidationError) + }) + + it('passes a valid query object when validate is on', async () => { + fetchMock.route('*', { + body: { + data: [goodMeter], + meta: { page: { number: 1, size: 10, total: 1 } }, + }, + headers: { 'Content-Type': 'application/json' }, + }) + const result = await funcs.listMeters(validatingClient(), { + page: { size: 10, number: 1 }, + }) + expect(result.ok).toBe(true) + }) }) describe('generated wire schemas (snake_case, strict)', () => { diff --git a/api/spec/packages/typespec-typescript/src/runtime-templates.ts b/api/spec/packages/typespec-typescript/src/runtime-templates.ts index 7d5d6afb42..a8b44ff182 100644 --- a/api/spec/packages/typespec-typescript/src/runtime-templates.ts +++ b/api/spec/packages/typespec-typescript/src/runtime-templates.ts @@ -8,7 +8,7 @@ const ENCODED: Record = { 'src/lib/types.ts': 'aW1wb3J0IHR5cGUgeyBPcHRpb25zIH0gZnJvbSAna3knCgpleHBvcnQgdHlwZSBSZXF1ZXN0T3B0aW9ucyA9IFBpY2s8CiAgT3B0aW9ucywKICAnc2lnbmFsJyB8ICdoZWFkZXJzJyB8ICd0aW1lb3V0JyB8ICdyZXRyeScKPgoKZXhwb3J0IHR5cGUgUmVzdWx0PFQsIEUgPSBFcnJvcj4gPQogIHwgeyBvazogdHJ1ZTsgdmFsdWU6IFQ7IGVycm9yPzogbmV2ZXIgfQogIHwgeyBvazogZmFsc2U7IHZhbHVlPzogbmV2ZXI7IGVycm9yOiBFIH0KCmV4cG9ydCBmdW5jdGlvbiBvazxUPih2YWx1ZTogVCk6IFJlc3VsdDxULCBuZXZlcj4gewogIHJldHVybiB7IG9rOiB0cnVlLCB2YWx1ZSB9Cn0KCmV4cG9ydCBmdW5jdGlvbiBlcnI8RT4oZXJyb3I6IEUpOiBSZXN1bHQ8bmV2ZXIsIEU+IHsKICByZXR1cm4geyBvazogZmFsc2UsIGVycm9yIH0KfQoKZXhwb3J0IGZ1bmN0aW9uIHVud3JhcDxULCBFPihyZXN1bHQ6IFJlc3VsdDxULCBFPik6IFQgewogIGlmIChyZXN1bHQub2spIHsKICAgIHJldHVybiByZXN1bHQudmFsdWUKICB9CiAgdGhyb3cgcmVzdWx0LmVycm9yCn0K', 'src/lib/config.ts': - 'aW1wb3J0IHsgdHlwZSBPcHRpb25zIH0gZnJvbSAna3knCgpleHBvcnQgY29uc3QgU2VydmVyTGlzdCA9IFsKICAnaHR0cHM6Ly97cmVnaW9ufS5hcGkua29uZ2hxLmNvbS92MycsCiAgJ2h0dHA6Ly9sb2NhbGhvc3Q6e3BvcnR9L2FwaS92MycsCiAgJ2h0dHBzOi8vb3Blbm1ldGVyLmNsb3VkL2FwaS92MycsCl0gYXMgY29uc3QKCmV4cG9ydCBjb25zdCBSZWdpb25zID0gWwogICdpbicsCiAgJ21lJywKICAnYXUnLAogICdldScsCiAgJ3VzJywKXSBhcyBjb25zdAoKZXhwb3J0IHR5cGUgUmVnaW9uID0gKHR5cGVvZiBSZWdpb25zKVtudW1iZXJdCgpleHBvcnQgdHlwZSBTZXJ2ZXJWYXJpYWJsZXMgPSB7CiAgcmVnaW9uPzogUmVnaW9uCiAgcG9ydD86IHN0cmluZyB8IG51bWJlcgp9CgpleHBvcnQgaW50ZXJmYWNlIFNES09wdGlvbnMgZXh0ZW5kcyBPbWl0PE9wdGlvbnMsICdtZXRob2QnPiB7CiAgYmFzZVVybDogKHR5cGVvZiBTZXJ2ZXJMaXN0KVtudW1iZXJdIHwgVVJMIHwgc3RyaW5nCiAgc2VydmVyVmFyaWFibGVzPzogU2VydmVyVmFyaWFibGVzCiAgYXBpS2V5Pzogc3RyaW5nIHwgKCgpID0+IHN0cmluZyB8IFByb21pc2U8c3RyaW5nPikKICAvKioKICAgKiBWYWxpZGF0ZSByZXF1ZXN0IGJvZGllcyBhbmQgcmVzcG9uc2UgcGF5bG9hZHMgYWdhaW5zdCB0aGVpciBzY2hlbWFzLiBPZmYgYnkKICAgKiBkZWZhdWx0OiB0aGUgU0RLIG1hcHMgY2FzaW5nIGJ1dCBkb2VzIG5vdCB2YWxpZGF0ZSwgc28gYWRkaXRpdmUgc2VydmVyIGZpZWxkcwogICAqIG5ldmVyIGJyZWFrIGNsaWVudHMuIFdoZW4gb24sIGEgcmVxdWVzdCBib2R5IG9yIHJlc3BvbnNlIHRoYXQgZmFpbHMgaXRzIHNjaGVtYQogICAqIChtaXNzaW5nL3dyb25nLXR5cGVkIGZpZWxkLCB1bmtub3duIGVudW0gdmFsdWUpIHJlamVjdHMgd2l0aCBhIFZhbGlkYXRpb25FcnJvci4KICAgKi8KICB2YWxpZGF0ZT86IGJvb2xlYW4KfQo=', + 'aW1wb3J0IHsgdHlwZSBPcHRpb25zIH0gZnJvbSAna3knCgpleHBvcnQgY29uc3QgU2VydmVyTGlzdCA9IFsKICAnaHR0cHM6Ly97cmVnaW9ufS5hcGkua29uZ2hxLmNvbS92MycsCiAgJ2h0dHA6Ly9sb2NhbGhvc3Q6e3BvcnR9L2FwaS92MycsCiAgJ2h0dHBzOi8vb3Blbm1ldGVyLmNsb3VkL2FwaS92MycsCl0gYXMgY29uc3QKCmV4cG9ydCBjb25zdCBSZWdpb25zID0gWwogICdpbicsCiAgJ21lJywKICAnYXUnLAogICdldScsCiAgJ3VzJywKXSBhcyBjb25zdAoKZXhwb3J0IHR5cGUgUmVnaW9uID0gKHR5cGVvZiBSZWdpb25zKVtudW1iZXJdCgpleHBvcnQgdHlwZSBTZXJ2ZXJWYXJpYWJsZXMgPSB7CiAgcmVnaW9uPzogUmVnaW9uCiAgcG9ydD86IHN0cmluZyB8IG51bWJlcgp9CgpleHBvcnQgaW50ZXJmYWNlIFNES09wdGlvbnMgZXh0ZW5kcyBPbWl0PE9wdGlvbnMsICdtZXRob2QnPiB7CiAgYmFzZVVybDogKHR5cGVvZiBTZXJ2ZXJMaXN0KVtudW1iZXJdIHwgVVJMIHwgc3RyaW5nCiAgc2VydmVyVmFyaWFibGVzPzogU2VydmVyVmFyaWFibGVzCiAgYXBpS2V5Pzogc3RyaW5nIHwgKCgpID0+IHN0cmluZyB8IFByb21pc2U8c3RyaW5nPikKICAvKioKICAgKiBWYWxpZGF0ZSByZXF1ZXN0IGJvZGllcyBhbmQgcmVzcG9uc2UgcGF5bG9hZHMgYWdhaW5zdCB0aGVpciBzY2hlbWFzLiBPZmYgYnkKICAgKiBkZWZhdWx0OiB0aGUgU0RLIG1hcHMgY2FzaW5nIGJ1dCBkb2VzIG5vdCB2YWxpZGF0ZSwgc28gYWRkaXRpdmUgc2VydmVyIGZpZWxkcwogICAqIG5ldmVyIGJyZWFrIGNsaWVudHMuIFdoZW4gb24sIGEgcmVxdWVzdCBib2R5IG9yIHJlc3BvbnNlIHRoYXQgZmFpbHMgaXRzIHNjaGVtYQogICAqIChtaXNzaW5nL3dyb25nLXR5cGVkIGZpZWxkLCB1bmtub3duIGVudW0gdmFsdWUpIHJldHVybnMgYSBmYWlsZWQgUmVzdWx0IHdob3NlCiAgICogYGVycm9yYCBpcyBhIFZhbGlkYXRpb25FcnJvciAodmFsaWRhdGlvbiBydW5zIGluc2lkZSB0aGUgU0RLJ3MgcmVxdWVzdAogICAqIGhhbmRsaW5nLCBzbyBpdCBuZXZlciByZWplY3RzL3Rocm93cykuCiAgICovCiAgdmFsaWRhdGU/OiBib29sZWFuCn0K', 'src/lib/encodings.ts': 'ZXhwb3J0IGZ1bmN0aW9uIGVuY29kZVBhdGgoCiAgdGVtcGxhdGU6IHN0cmluZywKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlcj4sCik6IHN0cmluZyB7CiAgcmV0dXJuIHRlbXBsYXRlLnJlcGxhY2UoL1x7KFx3KylcfS9nLCAoXywga2V5OiBzdHJpbmcpID0+IHsKICAgIGNvbnN0IHZhbHVlID0gcGFyYW1zW2tleV0KICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihgbWlzc2luZyBwYXRoIHBhcmFtZXRlcjogJHtrZXl9YCkKICAgIH0KICAgIHJldHVybiBlbmNvZGVVUklDb21wb25lbnQoU3RyaW5nKHZhbHVlKSkKICB9KQp9CgpmdW5jdGlvbiBzZXJpYWxpemVEZWVwT2JqZWN0KAogIHByZWZpeDogc3RyaW5nLAogIHZhbHVlOiB1bmtub3duLAogIHBhcnRzOiBBcnJheTxbc3RyaW5nLCBzdHJpbmddPiwKKTogdm9pZCB7CiAgaWYgKHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSB1bmRlZmluZWQpIHsKICAgIHJldHVybgogIH0KICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHsKICAgIHBhcnRzLnB1c2goW3ByZWZpeCwgdmFsdWUubWFwKFN0cmluZykuam9pbignLCcpXSkKICB9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHsKICAgIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKHZhbHVlKSkgewogICAgICBzZXJpYWxpemVEZWVwT2JqZWN0KGAke3ByZWZpeH1bJHtrfV1gLCB2LCBwYXJ0cykKICAgIH0KICB9IGVsc2UgewogICAgcGFydHMucHVzaChbcHJlZml4LCBTdHJpbmcodmFsdWUpXSkKICB9Cn0KCmZ1bmN0aW9uIHNlcmlhbGl6ZVBhcmFtcygKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LAopOiBBcnJheTxbc3RyaW5nLCBzdHJpbmddPiB7CiAgY29uc3QgcGFydHM6IEFycmF5PFtzdHJpbmcsIHN0cmluZ10+ID0gW10KICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwYXJhbXMpKSB7CiAgICBzZXJpYWxpemVEZWVwT2JqZWN0KGtleSwgdmFsdWUsIHBhcnRzKQogIH0KICByZXR1cm4gcGFydHMKfQoKZXhwb3J0IGZ1bmN0aW9uIHF1ZXJ5U2VyaWFsaXplcihwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogc3RyaW5nIHsKICBjb25zdCBwYXJ0cyA9IHNlcmlhbGl6ZVBhcmFtcyhwYXJhbXMpLm1hcCgKICAgIChbaywgdl0pID0+IGAke2VuY29kZVVSSUNvbXBvbmVudChrKX09JHtlbmNvZGVVUklDb21wb25lbnQodil9YCwKICApCiAgcmV0dXJuIHBhcnRzLmxlbmd0aCA/IGA/JHtwYXJ0cy5qb2luKCcmJyl9YCA6ICcnCn0KCmV4cG9ydCBmdW5jdGlvbiB0b1VSTFNlYXJjaFBhcmFtcygKICBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LAopOiBVUkxTZWFyY2hQYXJhbXMgewogIGNvbnN0IHNlYXJjaCA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKQogIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIHNlcmlhbGl6ZVBhcmFtcyhwYXJhbXMpKSB7CiAgICBzZWFyY2guYXBwZW5kKGtleSwgdmFsdWUpCiAgfQogIHJldHVybiBzZWFyY2gKfQoKZXhwb3J0IGZ1bmN0aW9uIGVuY29kZVNvcnQoCiAgc29ydDogeyBieT86IHN0cmluZzsgb3JkZXI/OiAnYXNjJyB8ICdkZXNjJyB9IHwgdW5kZWZpbmVkLAogIGVuY29kZUZpZWxkOiAoZmllbGQ6IHN0cmluZykgPT4gc3RyaW5nID0gKGZpZWxkKSA9PiBmaWVsZCwKKTogc3RyaW5nIHwgdW5kZWZpbmVkIHsKICBpZiAoIXNvcnQ/LmJ5KSB7CiAgICByZXR1cm4gdW5kZWZpbmVkCiAgfQogIGNvbnN0IGJ5ID0gZW5jb2RlRmllbGQoc29ydC5ieSkKICBpZiAoc29ydC5vcmRlciA9PT0gJ2Rlc2MnKSB7CiAgICByZXR1cm4gYCR7Ynl9IGRlc2NgCiAgfQogIGlmIChzb3J0Lm9yZGVyID09PSAnYXNjJykgewogICAgcmV0dXJuIGAke2J5fSBhc2NgCiAgfQogIHJldHVybiBieQp9Cg==', 'src/lib/to-error.ts': diff --git a/api/spec/packages/typespec-typescript/src/sdk-files.ts b/api/spec/packages/typespec-typescript/src/sdk-files.ts index 187341a492..aa88d802df 100644 --- a/api/spec/packages/typespec-typescript/src/sdk-files.ts +++ b/api/spec/packages/typespec-typescript/src/sdk-files.ts @@ -58,26 +58,6 @@ function funcBody(op: SdkOperation): string { ` options?: RequestOptions,`, `): Promise> {`, ) - if (hasQuery) { - // The query object is camelCase (sort pre-encoded to its wire string); toWire - // snake-ifies the keys, including typed filter field names, against the query - // schema. Record keys (label/dimension names) are preserved by the walker. - const entries = op.queryParams.map((p) => { - const key = toCamelCase(p) - // sort.by names a field in the public (camelCase) surface; the server - // expects the snake_case field name, so translate the value here. - return p === 'sort' - ? ` sort: encodeSort(req.sort, toSnakeCase),` - : ` ${key}: req.${key},` - }) - lines.push( - ` const searchParams = toURLSearchParams(`, - ` toWire({`, - ...entries, - ` }, schemas.${op.funcName}QueryParams),`, - ` )`, - ) - } if (op.textResponseContentType) { // The server negotiates this variant on the exact Accept media type; user // headers are carried over so only `accept` is forced. @@ -91,9 +71,10 @@ function funcBody(op: SdkOperation): string { } const target = hasPath ? 'path' : url const optsArg = optsObj === 'options' ? ', options' : `, ${optsObj}` - // Maps the request body to the wire and, when validation is on, checks the actual - // snake_case payload against the strict wire schema before sending. Runs inside the - // request() closure so a failure becomes Result.error, not a synchronous throw. + // Maps the request body/query object to the wire and, when validation is on, + // checks the actual snake_case payload against the strict wire schema before + // sending. Runs inside the request() closure so a failure becomes Result.error, + // not a synchronous throw — query params get the same guarantee as bodies do. const bodyValue = hasPath || hasQuery ? 'req.body' : 'req' const prepareBody = op.hasBody ? [ @@ -103,21 +84,46 @@ function funcBody(op: SdkOperation): string { ` }`, ] : [] - // A request-body op runs its validation inside a block-arrow request() closure; - // bodyless ops keep the terser expression form (no behavior change). - const open = op.hasBody - ? ` return request(() => {` - : ` return request(() =>` - const ret = op.hasBody ? ` return http(client)` : ` http(client)` + const prepareQuery = hasQuery + ? [ + // The query object is camelCase (sort pre-encoded to its wire string); + // toWire snake-ifies the keys, including typed filter field names, against + // the query schema. Record keys (label/dimension names) are preserved by + // the walker. + ` const query = toWire({`, + ...op.queryParams.map((p) => { + const key = toCamelCase(p) + // sort.by names a field in the public (camelCase) surface; the server + // expects the snake_case field name, so translate the value here. + return p === 'sort' + ? ` sort: encodeSort(req.sort, toSnakeCase),` + : ` ${key}: req.${key},` + }), + ` }, schemas.${op.funcName}QueryParams)`, + ` if (client._options.validate) {`, + ` assertValid(schemas.${op.funcName}QueryParamsWire, query)`, + ` }`, + ` const searchParams = toURLSearchParams(query)`, + ] + : [] + const prepare = [...prepareBody, ...prepareQuery] + // An op that prepares a body and/or query object runs its validation inside a + // block-arrow request() closure; a plain op keeps the terser expression form + // (no behavior change). + const open = + prepare.length > 0 ? ` return request(() => {` : ` return request(() =>` + const ret = + prepare.length > 0 ? ` return http(client)` : ` http(client)` + const closeBlock = prepare.length > 0 if (op.hasResponse) { if (op.textResponseContentType) { lines.push( open, - ...prepareBody, + ...prepare, ret, ` .${op.verb}(${target}${optsArg})`, - op.hasBody ? ` .text()` : ` .text(),`, - op.hasBody ? ` })` : ` )`, + closeBlock ? ` .text()` : ` .text(),`, + closeBlock ? ` })` : ` )`, `}`, ) } else { @@ -125,7 +131,7 @@ function funcBody(op: SdkOperation): string { // the strict wire schema before fromWire maps it to the camelCase public shape. lines.push( open, - ...prepareBody, + ...prepare, ret, ` .${op.verb}(${target}${optsArg})`, ` .json()`, @@ -134,16 +140,17 @@ function funcBody(op: SdkOperation): string { ` assertValid(schemas.${op.funcName}ResponseWire, data)`, ` }`, ` return fromWire(data, schemas.${op.funcName}Response)`, - op.hasBody ? ` })` : ` }),`, - op.hasBody ? ` })` : ` )`, + closeBlock ? ` })` : ` }),`, + closeBlock ? ` })` : ` )`, `}`, ) } } else { - // Bodyless void ops already used an async block; body void ops add validation. + // Bodyless/query-less void ops already used an async block; ops that prepare + // a body or query add validation into the same block. lines.push( ` return request(async () => {`, - ...prepareBody, + ...prepare, ` await http(client).${op.verb}(${target}${optsArg})`, ` })`, `}`, @@ -215,9 +222,12 @@ export function funcsFile(tag: string, ops: SdkOperation[]): string { ) const usesSnake = ops.some((op) => op.hasSort) // assertValid runs (when the validate option is on) for any op with a request - // body or a JSON response. + // body, query params, or a JSON response. const usesValidate = ops.some( - (op) => op.hasBody || (op.hasResponse && !op.textResponseContentType), + (op) => + op.hasBody || + op.queryParams.length > 0 || + (op.hasResponse && !op.textResponseContentType), ) const mapperNames = [ ...(usesToWire ? ['toWire'] : []),