@xemahq/api-client-generator 0.10.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.source-stamp.json +5 -0
- package/dist/lib/client-package.d.ts +1 -1
- package/dist/lib/client-package.d.ts.map +1 -1
- package/dist/lib/client-package.js +3 -13
- package/dist/lib/client-package.js.map +1 -1
- package/dist/lib/custom-fetch-template.d.ts +1 -1
- package/dist/lib/custom-fetch-template.d.ts.map +1 -1
- package/dist/lib/custom-fetch-template.js +198 -74
- package/dist/lib/custom-fetch-template.js.map +1 -1
- package/dist/lib/extract-openapi.d.ts.map +1 -1
- package/dist/lib/extract-openapi.js +1 -1
- package/dist/lib/extract-openapi.js.map +1 -1
- package/dist/lib/repo-post-process.js +1 -1
- package/dist/lib/repo-post-process.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-package.d.ts","sourceRoot":"","sources":["../../src/lib/client-package.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client-package.d.ts","sourceRoot":"","sources":["../../src/lib/client-package.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1D,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAyCD,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,sBAAsB,EAC3B,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,MAAM,GACd;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAqF1C"}
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ensureClientPackage = ensureClientPackage;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
5
|
const node_path_1 = require("node:path");
|
|
6
|
-
const xema_decorators_1 = require("@xemahq/xema-decorators");
|
|
7
6
|
const custom_fetch_template_1 = require("./custom-fetch-template");
|
|
8
7
|
const service_config_1 = require("./service-config");
|
|
9
8
|
const CLIENT_TSCONFIG = {
|
|
@@ -79,19 +78,10 @@ function ensureClientPackage(ctx, surface, version) {
|
|
|
79
78
|
if (!(0, node_fs_1.existsSync)(tsconfigPath)) {
|
|
80
79
|
(0, node_fs_1.writeFileSync)(tsconfigPath, `${JSON.stringify(CLIENT_TSCONFIG, null, 2)}\n`, 'utf8');
|
|
81
80
|
}
|
|
82
|
-
|
|
81
|
+
writeCustomFetch(clientSrc);
|
|
83
82
|
return { clientDir, clientSrc };
|
|
84
83
|
}
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
if ((0, node_fs_1.existsSync)(target)) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const publicSibling = (0, node_path_1.join)((0, service_config_1.surfaceClientDir)(ctx, xema_decorators_1.ApiSurface.Public), 'src', 'custom-fetch.ts');
|
|
91
|
-
if ((0, node_fs_1.existsSync)(publicSibling)) {
|
|
92
|
-
(0, node_fs_1.cpSync)(publicSibling, target);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
(0, node_fs_1.writeFileSync)(target, custom_fetch_template_1.CANONICAL_CUSTOM_FETCH, 'utf8');
|
|
84
|
+
function writeCustomFetch(clientSrc) {
|
|
85
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(clientSrc, 'custom-fetch.ts'), custom_fetch_template_1.CANONICAL_CUSTOM_FETCH, 'utf8');
|
|
96
86
|
}
|
|
97
87
|
//# sourceMappingURL=client-package.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-package.js","sourceRoot":"","sources":["../../src/lib/client-package.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"client-package.js","sourceRoot":"","sources":["../../src/lib/client-package.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CANONICAL_CUSTOM_FETCH = "/**\n * Configurable fetch wrapper for Orval-generated clients.\n *\n * Consumers must call `configureClient()` before using any endpoint function.\n * The baseUrl is prepended to the relative paths generated by Orval.\n */\n\nexport interface ClientConfig {\n /** Static base URL (e.g. 'http://localhost:3140') \u2014 no trailing slash. */\n baseUrl?: string;\n /** Per-request base-URL resolver (service-registry-safe). */\n baseUrlResolver?: () => string | Promise<string>;\n /** Optional async callback to get an auth token. */\n getAuthToken?: () => Promise<string>;\n /** Optional callback returning headers to inject on every request. */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Optional callback invoked on 401 before a single retry. */\n onUnauthorized?: () => Promise<void>;\n /** Maximum retry attempts for transient failures (default: 3). */\n maxRetries?: number;\n}\n\nexport class ClientError extends Error {\n constructor(\n public readonly status: number,\n public readonly url: string,\n public readonly body: unknown,\n ) {\n super(`HTTP ${status} from ${url}`);\n this.name = 'ClientError';\n }\n}\n\nlet clientConfig: ClientConfig | null = null;\n\nexport function configureClient(config: ClientConfig): void {\n clientConfig = config;\n}\n\nexport function getClientConfig(): ClientConfig {\n if (!clientConfig) {\n throw new Error(\n 'Client not configured. Call configureClient({ baseUrl }) before using endpoint functions.',\n );\n }\n return clientConfig;\n}\n\nconst RETRYABLE_STATUSES = [429, 502, 503, 504];\n\nasync function buildHeaders(\n config: ClientConfig,\n callerHeaders: RequestInit['headers'],\n): Promise<Headers> {\n const headers = new Headers(callerHeaders);\n if (config.getHeaders) {\n const globalHeaders = await Promise.resolve(config.getHeaders());\n for (const [key, value] of Object.entries(globalHeaders)) {\n if (!headers.has(key)) {\n headers.set(key, value);\n }\n }\n }\n if (config.getAuthToken && !headers.has('Authorization')) {\n const token = await config.getAuthToken();\n headers.set('Authorization', `Bearer ${token}`);\n }\n return headers;\n}\n\nexport const customFetch = async <T>(\n url: string,\n options: RequestInit,\n): Promise<T> => {\n const config = getClientConfig();\n const base = config.baseUrlResolver\n ? await config.baseUrlResolver()\n : config.baseUrl;\n if (base === undefined) {\n throw new Error(\n 'Client not configured: set baseUrl or baseUrlResolver via configureClient().',\n );\n }\n const fullUrl = `${base}${url}`;\n const maxRetries = config.maxRetries ?? 3;\n\n const headers = await buildHeaders(config, options.headers);\n const requestInit: RequestInit = { ...options, headers };\n\n let delay = 1000;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(fullUrl, requestInit);\n\n if (response.status === 401 && config.onUnauthorized && attempt === 0) {\n await config.onUnauthorized();\n const refreshedHeaders = await buildHeaders(config, options.headers);\n const retryResponse = await fetch(fullUrl, { ...options, headers: refreshedHeaders });\n const retryBody = await parseBody(retryResponse);\n if (retryResponse.status >= 400) {\n throw new ClientError(retryResponse.status, fullUrl, retryBody);\n }\n return retryBody as T;\n }\n\n if (!RETRYABLE_STATUSES.includes(response.status) || attempt >= maxRetries) {\n const body = await parseBody(response);\n if (response.status >= 400) {\n throw new ClientError(response.status, fullUrl, body);\n }\n return body as T;\n }\n\n const retryAfter = parseRetryAfter(response.headers.get('Retry-After'));\n const waitMs = retryAfter ?? addJitter(delay);\n await sleep(waitMs);\n delay = Math.min(delay * 2, 30_000);\n } catch (error) {\n if (error instanceof ClientError) throw error;\n lastError = error instanceof Error ? error : new Error(String(error));\n if (attempt >= maxRetries) throw lastError;\n const waitMs = addJitter(delay);\n await sleep(waitMs);\n delay = Math.min(delay * 2, 30_000);\n }\n }\n\n throw lastError ?? new Error(`All retries exhausted for ${fullUrl}`);\n};\n\n/*\n * THE TRANSPORT RETURNS THE BODY UNCHANGED. Do not reintroduce an unwrap here.\n *\n * Xema services wrap every 2xx payload in a { data: T } envelope via the global\n * ResponseEnvelopeInterceptor (platform-common), and the generator now emits\n * types that describe THAT ENVELOPE \u2014 every endpoint returns\n * Promise<XDataEnvelope> or Promise<XPaginatedEnvelope>, never a bare inner\n * T. Consumers read the data property themselves.\n *\n * This template used to peel data, justified by a comment claiming generated\n * types describe the inner T. That stopped being true when the generator moved\n * to envelope-typed returns, and the comment outlived the fact \u2014 so the peel\n * then contradicted every type in every package it seeded. Four clients shipped\n * that way (resource-governance x3, workload-runtime-api): declared\n * *DataEnvelope, returned the already-peeled inner object, so .data read\n * undefined at runtime on every non-paginated endpoint. Paginated calls hid\n * it, because the old peel deliberately preserved an envelope carrying\n * pagination.\n *\n * ensureCustomFetch seeds this template into any client that lacks a\n * transport, so a peel here is not one bug \u2014 it is re-imported into every new\n * client generated from now on, which is exactly how those four were born after\n * the hand-fix of their siblings.\n *\n * Enforced fleet-wide by check-client-transport-envelope in xema-base, which\n * reads the RETURN SITES rather than banning a name \u2014 a rename, an inline\n * .data, or ?? body.data all walk past a string ban.\n */\n\nasync function parseBody(response: Response): Promise<unknown> {\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n return response.json();\n }\n if (response.status === 204) {\n return undefined;\n }\n return response.text();\n}\n\nfunction parseRetryAfter(value: string | null): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n if (!isNaN(seconds) && seconds >= 0) return seconds * 1000;\n const date = new Date(value);\n if (!isNaN(date.getTime())) return Math.max(0, date.getTime() - Date.now());\n return undefined;\n}\n\nfunction addJitter(delay: number): number {\n return delay + (Math.random() * 2 - 1) * delay * 0.25;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nexport default customFetch;\n";
|
|
1
|
+
export declare const CANONICAL_CUSTOM_FETCH = "/**\n * Configurable fetch wrapper for Orval-generated clients.\n *\n * GENERATED by `@xemahq/api-client-generator` \u2014 do not edit. Every client in\n * the fleet ships this file byte-for-byte; a local edit is erased by the next\n * `pnpm refresh` and, until then, makes this one client speak to the platform\n * differently from all of its siblings. Change the template instead:\n * `api-client-generator/src/lib/custom-fetch-template.ts`.\n *\n * Consumers must call `configureClient()` before using any endpoint function.\n * The baseUrl is prepended to the relative paths generated by Orval.\n *\n * By default this transport issues exactly ONE request and adds no delay of its\n * own, so a slow call is a slow server. Retrying is opt-in, and opting in\n * requires supplying an observer \u2014 see `maxRetries` / `onRetry` below.\n */\n\n/** What {@link ClientConfig.onRetry} is told before each re-attempt. */\nexport interface RetryNotice {\n /** 1-based index of the re-attempt about to be made. */\n attempt: number;\n /** The configured budget this re-attempt is spending from. */\n maxRetries: number;\n /** The response status that made the previous attempt retryable. */\n status: number;\n /** Delay before the re-attempt, in ms \u2014 from `Retry-After` when the server sent one. */\n waitMs: number;\n /** Whether {@link waitMs} came from the server's `Retry-After` header. */\n retryAfterHonoured: boolean;\n /** Absolute URL being re-requested. */\n url: string;\n /** HTTP method being re-requested. */\n method: string;\n}\n\nexport interface ClientConfig {\n /**\n * Static base URL (e.g. 'http://localhost:3140') \u2014 no trailing slash.\n * Mutually exclusive with `baseUrlResolver`; exactly one MUST be set.\n */\n baseUrl?: string;\n /**\n * Per-request base-URL resolver. When set, the peer URL is resolved from\n * the service registry on EVERY request (boot-order-safe). Wired by\n * `configureOrvalClientResolved` from `@xemahq/platform-common`.\n */\n baseUrlResolver?: () => string | Promise<string>;\n /** Optional async callback to get an auth token. Auto-sets Authorization header on every request. */\n getAuthToken?: () => Promise<string>;\n /** Optional callback returning headers to inject on every request. Per-call headers take precedence. */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /**\n * Optional callback invoked on a 401 before ONE re-attempt. Supplying it is\n * what opts this client into that re-attempt; without it a 401 comes back to\n * the caller as a `ClientError` like any other 4xx.\n */\n onUnauthorized?: () => Promise<void>;\n /**\n * Re-attempts for a retryable status. **Defaults to 0 \u2014 no retry.**\n *\n * Setting it above 0 REQUIRES `onRetry`; `configureClient` throws otherwise.\n * See the block above `customFetch` for why the budget is opt-in and why an\n * observer is mandatory rather than advisory.\n */\n maxRetries?: number;\n /**\n * Called immediately before every re-attempt, with the reason and the delay.\n *\n * Mandatory whenever `maxRetries` is above 0. It is the difference between a\n * retry and a hidden degradation path: without it a caller cannot tell a slow\n * server from this transport sleeping between attempts.\n */\n onRetry?: (notice: RetryNotice) => void;\n}\n\nexport class ClientError extends Error {\n constructor(\n public readonly status: number,\n public readonly url: string,\n public readonly body: unknown,\n ) {\n super(`HTTP ${status} from ${url}`);\n this.name = 'ClientError';\n }\n}\n\nlet clientConfig: ClientConfig | null = null;\n\nexport function configureClient(config: ClientConfig): void {\n // Fail at WIRING time, not on the request that happens to be retried. A\n // client configured to retry without an observer is the exact defect this\n // transport was rebuilt to remove, so it must not be constructible.\n if ((config.maxRetries ?? 0) > 0 && !config.onRetry) {\n throw new Error(\n 'configureClient: maxRetries above 0 requires onRetry. A retry nothing ' +\n 'reports is indistinguishable from a slow server, which is how two ' +\n 'silent re-attempts were read as a 3,312ms call.',\n );\n }\n clientConfig = config;\n}\n\nexport function getClientConfig(): ClientConfig {\n if (!clientConfig) {\n throw new Error(\n 'Client not configured. Call configureClient({ baseUrl }) before using endpoint functions.',\n );\n }\n return clientConfig;\n}\n\n/*\n * RETRYING IS OPT-IN, OBSERVED, AND NARROW. Do not widen any of the three.\n *\n * This transport used to retry up to `maxRetries` (defaulting to THREE) over\n * 429/502/503/504 AND over any thrown network error, backing off from 1000ms,\n * with no log line anywhere. Three properties were wrong:\n *\n * 1. The DEFAULT was on. ~99% of the fleet's client configurations never\n * mention `maxRetries`, so two re-attempts could add ~3s to any call with\n * nothing to distinguish that from a slow peer. Every caller that DID set\n * it set it LOWER \u2014 canopy's control plane to 1, \"so a hard outage\n * surfaces inside the turn's latency budget rather than after three\n * backoffs\"; two llm-registry callers to 1; a test to 0. Nobody ever\n * raised it. A default three separate call sites work around is not a\n * default, and `configureOrvalClientResolved` cannot express the field at\n * all, so most callers could not have opted out if they had wanted to.\n * 2. It retried AMBIGUOUS failures. 502 and 504 mean a gateway did not get a\n * timely answer from upstream \u2014 the upstream may well have APPLIED the\n * request. So did a thrown network error mid-flight. Re-sending a POST in\n * either case is a duplicate write, and no amount of logging makes that\n * safe. Only 429 and 503 state positively that the request was NOT\n * processed, so only those are retried; everything else surfaces at once.\n * 3. Nothing reported it. Now `onRetry` is mandatory whenever the budget is\n * above 0, enforced in `configureClient`.\n *\n * Also note `baseUrlResolver` is awaited ONCE, above the loop: a re-attempt\n * returns to the same resolved instance. In a registry-resolved fleet the cure\n * for an unhealthy peer is re-resolution, which lives above this file \u2014 which\n * is a further reason not to lean on retrying here.\n *\n * Retry policy a caller genuinely wants belongs in `@xemahq/managed-fetch`,\n * which has backoff, a circuit breaker, a token bucket and health reporting,\n * and reports what it did.\n *\n * Enforced fleet-wide by `check-client-transport-envelope`, which compares\n * every client's transport to this template.\n */\n\n/** The only statuses that state the request was NOT processed. See above. */\nconst RETRYABLE_STATUSES = [429, 503];\n\n/** Backoff floor, doubling per attempt up to {@link MAX_BACKOFF_MS}. */\nconst BASE_BACKOFF_MS = 1000;\n\n/** Ceiling on a single backoff, however many attempts have elapsed. */\nconst MAX_BACKOFF_MS = 30_000;\n\nasync function buildHeaders(\n config: ClientConfig,\n callerHeaders: RequestInit['headers'],\n): Promise<Headers> {\n const headers = new Headers(callerHeaders);\n\n // Global headers from config (caller-provided headers take precedence)\n if (config.getHeaders) {\n const globalHeaders = await Promise.resolve(config.getHeaders());\n for (const [key, value] of Object.entries(globalHeaders)) {\n if (!headers.has(key)) {\n headers.set(key, value);\n }\n }\n }\n\n // Auth token (caller or global headers take precedence)\n if (config.getAuthToken && !headers.has('Authorization')) {\n const token = await config.getAuthToken();\n headers.set('Authorization', `Bearer ${token}`);\n }\n\n return headers;\n}\n\n/*\n * THE TRANSPORT RETURNS THE BODY UNCHANGED. Do not reintroduce an unwrap here.\n *\n * Xema services wrap every 2xx payload in a { data: T } envelope via the global\n * ResponseEnvelopeInterceptor (platform-common), and the generator emits types\n * that describe THAT ENVELOPE \u2014 every endpoint returns Promise<XDataEnvelope>\n * or Promise<XPaginatedEnvelope>, never a bare inner T. Consumers read the data\n * property themselves.\n *\n * This template used to peel data, justified by a comment claiming generated\n * types describe the inner T. That stopped being true when the generator moved\n * to envelope-typed returns, and the comment outlived the fact \u2014 so the peel\n * then contradicted every type in every package it seeded. Four clients shipped\n * that way (resource-governance x3, workload-runtime-api): declared\n * *DataEnvelope, returned the already-peeled inner object, so .data read\n * undefined at runtime on every non-paginated endpoint. Paginated calls hid it,\n * because the old peel deliberately preserved an envelope carrying pagination.\n *\n * Four MORE shipped it in repositories the gate could not see \u2014 license-api and\n * license-internal-api in xema-operator, plus their host-web mirrors \u2014 because\n * the gate ran in xema-base only. That is why it now runs in every repository\n * that ships a client, and why this file is generator-owned rather than seeded.\n */\n\nexport const customFetch = async <T>(\n url: string,\n options: RequestInit,\n): Promise<T> => {\n const config = getClientConfig();\n const base = config.baseUrlResolver\n ? await config.baseUrlResolver()\n : config.baseUrl;\n if (base === undefined) {\n throw new Error(\n 'Client not configured: set baseUrl or baseUrlResolver via configureClient().',\n );\n }\n const fullUrl = `${base}${url}`;\n const maxRetries = config.maxRetries ?? 0;\n\n // A caller that supplied its own Authorization header owns that credential.\n // `buildHeaders` lets it win, so refreshing the CLIENT-WIDE token and\n // re-sending would replay the identical failing request with the identical\n // credential: one wasted round trip, plus a global refresh nobody asked for.\n const callerSuppliedAuth = new Headers(options.headers).has('Authorization');\n\n const headers = await buildHeaders(config, options.headers);\n const requestInit: RequestInit = { ...options, headers };\n\n let delay = BASE_BACKOFF_MS;\n\n for (let attempt = 0; ; attempt++) {\n const response = await fetch(fullUrl, requestInit);\n\n if (\n response.status === 401 &&\n config.onUnauthorized &&\n !callerSuppliedAuth &&\n attempt === 0\n ) {\n await config.onUnauthorized();\n const refreshedHeaders = await buildHeaders(config, options.headers);\n const retryResponse = await fetch(fullUrl, {\n ...options,\n headers: refreshedHeaders,\n });\n const retryBody = await parseBody(retryResponse);\n if (retryResponse.status >= 400) {\n throw new ClientError(retryResponse.status, fullUrl, retryBody);\n }\n return retryBody as T;\n }\n\n if (\n !RETRYABLE_STATUSES.includes(response.status) ||\n attempt >= maxRetries\n ) {\n const body = await parseBody(response);\n if (response.status >= 400) {\n throw new ClientError(response.status, fullUrl, body);\n }\n return body as T;\n }\n\n const retryAfter = parseRetryAfter(response.headers.get('Retry-After'));\n const waitMs = retryAfter ?? addJitter(delay);\n // Non-null: `configureClient` refuses a budget above 0 without an observer,\n // and this line is unreachable unless `maxRetries` is above 0.\n (config.onRetry as (notice: RetryNotice) => void)({\n attempt: attempt + 1,\n maxRetries,\n status: response.status,\n waitMs,\n retryAfterHonoured: retryAfter !== undefined,\n url: fullUrl,\n method: options.method ?? 'GET',\n });\n await sleep(waitMs);\n delay = Math.min(delay * 2, MAX_BACKOFF_MS);\n }\n};\n\nasync function parseBody(response: Response): Promise<unknown> {\n const contentType = response.headers.get('content-type');\n if (contentType?.includes('application/json')) {\n return response.json();\n }\n if (response.status === 204) {\n return undefined;\n }\n return response.text();\n}\n\nfunction parseRetryAfter(value: string | null): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n if (!isNaN(seconds) && seconds >= 0) return seconds * 1000;\n const date = new Date(value);\n if (!isNaN(date.getTime())) return Math.max(0, date.getTime() - Date.now());\n return undefined;\n}\n\nfunction addJitter(delay: number): number {\n return delay + (Math.random() * 2 - 1) * delay * 0.25;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport default customFetch;\n";
|
|
2
2
|
//# sourceMappingURL=custom-fetch-template.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-fetch-template.d.ts","sourceRoot":"","sources":["../../src/lib/custom-fetch-template.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"custom-fetch-template.d.ts","sourceRoot":"","sources":["../../src/lib/custom-fetch-template.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,sBAAsB,olZA0TlC,CAAC"}
|
|
@@ -4,23 +4,76 @@ exports.CANONICAL_CUSTOM_FETCH = void 0;
|
|
|
4
4
|
exports.CANONICAL_CUSTOM_FETCH = `/**
|
|
5
5
|
* Configurable fetch wrapper for Orval-generated clients.
|
|
6
6
|
*
|
|
7
|
+
* GENERATED by \`@xemahq/api-client-generator\` — do not edit. Every client in
|
|
8
|
+
* the fleet ships this file byte-for-byte; a local edit is erased by the next
|
|
9
|
+
* \`pnpm refresh\` and, until then, makes this one client speak to the platform
|
|
10
|
+
* differently from all of its siblings. Change the template instead:
|
|
11
|
+
* \`api-client-generator/src/lib/custom-fetch-template.ts\`.
|
|
12
|
+
*
|
|
7
13
|
* Consumers must call \`configureClient()\` before using any endpoint function.
|
|
8
14
|
* The baseUrl is prepended to the relative paths generated by Orval.
|
|
15
|
+
*
|
|
16
|
+
* By default this transport issues exactly ONE request and adds no delay of its
|
|
17
|
+
* own, so a slow call is a slow server. Retrying is opt-in, and opting in
|
|
18
|
+
* requires supplying an observer — see \`maxRetries\` / \`onRetry\` below.
|
|
9
19
|
*/
|
|
10
20
|
|
|
21
|
+
/** What {@link ClientConfig.onRetry} is told before each re-attempt. */
|
|
22
|
+
export interface RetryNotice {
|
|
23
|
+
/** 1-based index of the re-attempt about to be made. */
|
|
24
|
+
attempt: number;
|
|
25
|
+
/** The configured budget this re-attempt is spending from. */
|
|
26
|
+
maxRetries: number;
|
|
27
|
+
/** The response status that made the previous attempt retryable. */
|
|
28
|
+
status: number;
|
|
29
|
+
/** Delay before the re-attempt, in ms — from \`Retry-After\` when the server sent one. */
|
|
30
|
+
waitMs: number;
|
|
31
|
+
/** Whether {@link waitMs} came from the server's \`Retry-After\` header. */
|
|
32
|
+
retryAfterHonoured: boolean;
|
|
33
|
+
/** Absolute URL being re-requested. */
|
|
34
|
+
url: string;
|
|
35
|
+
/** HTTP method being re-requested. */
|
|
36
|
+
method: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
11
39
|
export interface ClientConfig {
|
|
12
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Static base URL (e.g. 'http://localhost:3140') — no trailing slash.
|
|
42
|
+
* Mutually exclusive with \`baseUrlResolver\`; exactly one MUST be set.
|
|
43
|
+
*/
|
|
13
44
|
baseUrl?: string;
|
|
14
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Per-request base-URL resolver. When set, the peer URL is resolved from
|
|
47
|
+
* the service registry on EVERY request (boot-order-safe). Wired by
|
|
48
|
+
* \`configureOrvalClientResolved\` from \`@xemahq/platform-common\`.
|
|
49
|
+
*/
|
|
15
50
|
baseUrlResolver?: () => string | Promise<string>;
|
|
16
|
-
/** Optional async callback to get an auth token. */
|
|
51
|
+
/** Optional async callback to get an auth token. Auto-sets Authorization header on every request. */
|
|
17
52
|
getAuthToken?: () => Promise<string>;
|
|
18
|
-
/** Optional callback returning headers to inject on every request. */
|
|
53
|
+
/** Optional callback returning headers to inject on every request. Per-call headers take precedence. */
|
|
19
54
|
getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
20
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Optional callback invoked on a 401 before ONE re-attempt. Supplying it is
|
|
57
|
+
* what opts this client into that re-attempt; without it a 401 comes back to
|
|
58
|
+
* the caller as a \`ClientError\` like any other 4xx.
|
|
59
|
+
*/
|
|
21
60
|
onUnauthorized?: () => Promise<void>;
|
|
22
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Re-attempts for a retryable status. **Defaults to 0 — no retry.**
|
|
63
|
+
*
|
|
64
|
+
* Setting it above 0 REQUIRES \`onRetry\`; \`configureClient\` throws otherwise.
|
|
65
|
+
* See the block above \`customFetch\` for why the budget is opt-in and why an
|
|
66
|
+
* observer is mandatory rather than advisory.
|
|
67
|
+
*/
|
|
23
68
|
maxRetries?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Called immediately before every re-attempt, with the reason and the delay.
|
|
71
|
+
*
|
|
72
|
+
* Mandatory whenever \`maxRetries\` is above 0. It is the difference between a
|
|
73
|
+
* retry and a hidden degradation path: without it a caller cannot tell a slow
|
|
74
|
+
* server from this transport sleeping between attempts.
|
|
75
|
+
*/
|
|
76
|
+
onRetry?: (notice: RetryNotice) => void;
|
|
24
77
|
}
|
|
25
78
|
|
|
26
79
|
export class ClientError extends Error {
|
|
@@ -37,6 +90,16 @@ export class ClientError extends Error {
|
|
|
37
90
|
let clientConfig: ClientConfig | null = null;
|
|
38
91
|
|
|
39
92
|
export function configureClient(config: ClientConfig): void {
|
|
93
|
+
// Fail at WIRING time, not on the request that happens to be retried. A
|
|
94
|
+
// client configured to retry without an observer is the exact defect this
|
|
95
|
+
// transport was rebuilt to remove, so it must not be constructible.
|
|
96
|
+
if ((config.maxRetries ?? 0) > 0 && !config.onRetry) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
'configureClient: maxRetries above 0 requires onRetry. A retry nothing ' +
|
|
99
|
+
'reports is indistinguishable from a slow server, which is how two ' +
|
|
100
|
+
'silent re-attempts were read as a 3,312ms call.',
|
|
101
|
+
);
|
|
102
|
+
}
|
|
40
103
|
clientConfig = config;
|
|
41
104
|
}
|
|
42
105
|
|
|
@@ -49,13 +112,60 @@ export function getClientConfig(): ClientConfig {
|
|
|
49
112
|
return clientConfig;
|
|
50
113
|
}
|
|
51
114
|
|
|
52
|
-
|
|
115
|
+
/*
|
|
116
|
+
* RETRYING IS OPT-IN, OBSERVED, AND NARROW. Do not widen any of the three.
|
|
117
|
+
*
|
|
118
|
+
* This transport used to retry up to \`maxRetries\` (defaulting to THREE) over
|
|
119
|
+
* 429/502/503/504 AND over any thrown network error, backing off from 1000ms,
|
|
120
|
+
* with no log line anywhere. Three properties were wrong:
|
|
121
|
+
*
|
|
122
|
+
* 1. The DEFAULT was on. ~99% of the fleet's client configurations never
|
|
123
|
+
* mention \`maxRetries\`, so two re-attempts could add ~3s to any call with
|
|
124
|
+
* nothing to distinguish that from a slow peer. Every caller that DID set
|
|
125
|
+
* it set it LOWER — canopy's control plane to 1, "so a hard outage
|
|
126
|
+
* surfaces inside the turn's latency budget rather than after three
|
|
127
|
+
* backoffs"; two llm-registry callers to 1; a test to 0. Nobody ever
|
|
128
|
+
* raised it. A default three separate call sites work around is not a
|
|
129
|
+
* default, and \`configureOrvalClientResolved\` cannot express the field at
|
|
130
|
+
* all, so most callers could not have opted out if they had wanted to.
|
|
131
|
+
* 2. It retried AMBIGUOUS failures. 502 and 504 mean a gateway did not get a
|
|
132
|
+
* timely answer from upstream — the upstream may well have APPLIED the
|
|
133
|
+
* request. So did a thrown network error mid-flight. Re-sending a POST in
|
|
134
|
+
* either case is a duplicate write, and no amount of logging makes that
|
|
135
|
+
* safe. Only 429 and 503 state positively that the request was NOT
|
|
136
|
+
* processed, so only those are retried; everything else surfaces at once.
|
|
137
|
+
* 3. Nothing reported it. Now \`onRetry\` is mandatory whenever the budget is
|
|
138
|
+
* above 0, enforced in \`configureClient\`.
|
|
139
|
+
*
|
|
140
|
+
* Also note \`baseUrlResolver\` is awaited ONCE, above the loop: a re-attempt
|
|
141
|
+
* returns to the same resolved instance. In a registry-resolved fleet the cure
|
|
142
|
+
* for an unhealthy peer is re-resolution, which lives above this file — which
|
|
143
|
+
* is a further reason not to lean on retrying here.
|
|
144
|
+
*
|
|
145
|
+
* Retry policy a caller genuinely wants belongs in \`@xemahq/managed-fetch\`,
|
|
146
|
+
* which has backoff, a circuit breaker, a token bucket and health reporting,
|
|
147
|
+
* and reports what it did.
|
|
148
|
+
*
|
|
149
|
+
* Enforced fleet-wide by \`check-client-transport-envelope\`, which compares
|
|
150
|
+
* every client's transport to this template.
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/** The only statuses that state the request was NOT processed. See above. */
|
|
154
|
+
const RETRYABLE_STATUSES = [429, 503];
|
|
155
|
+
|
|
156
|
+
/** Backoff floor, doubling per attempt up to {@link MAX_BACKOFF_MS}. */
|
|
157
|
+
const BASE_BACKOFF_MS = 1000;
|
|
158
|
+
|
|
159
|
+
/** Ceiling on a single backoff, however many attempts have elapsed. */
|
|
160
|
+
const MAX_BACKOFF_MS = 30_000;
|
|
53
161
|
|
|
54
162
|
async function buildHeaders(
|
|
55
163
|
config: ClientConfig,
|
|
56
164
|
callerHeaders: RequestInit['headers'],
|
|
57
165
|
): Promise<Headers> {
|
|
58
166
|
const headers = new Headers(callerHeaders);
|
|
167
|
+
|
|
168
|
+
// Global headers from config (caller-provided headers take precedence)
|
|
59
169
|
if (config.getHeaders) {
|
|
60
170
|
const globalHeaders = await Promise.resolve(config.getHeaders());
|
|
61
171
|
for (const [key, value] of Object.entries(globalHeaders)) {
|
|
@@ -64,13 +174,40 @@ async function buildHeaders(
|
|
|
64
174
|
}
|
|
65
175
|
}
|
|
66
176
|
}
|
|
177
|
+
|
|
178
|
+
// Auth token (caller or global headers take precedence)
|
|
67
179
|
if (config.getAuthToken && !headers.has('Authorization')) {
|
|
68
180
|
const token = await config.getAuthToken();
|
|
69
181
|
headers.set('Authorization', \`Bearer \${token}\`);
|
|
70
182
|
}
|
|
183
|
+
|
|
71
184
|
return headers;
|
|
72
185
|
}
|
|
73
186
|
|
|
187
|
+
/*
|
|
188
|
+
* THE TRANSPORT RETURNS THE BODY UNCHANGED. Do not reintroduce an unwrap here.
|
|
189
|
+
*
|
|
190
|
+
* Xema services wrap every 2xx payload in a { data: T } envelope via the global
|
|
191
|
+
* ResponseEnvelopeInterceptor (platform-common), and the generator emits types
|
|
192
|
+
* that describe THAT ENVELOPE — every endpoint returns Promise<XDataEnvelope>
|
|
193
|
+
* or Promise<XPaginatedEnvelope>, never a bare inner T. Consumers read the data
|
|
194
|
+
* property themselves.
|
|
195
|
+
*
|
|
196
|
+
* This template used to peel data, justified by a comment claiming generated
|
|
197
|
+
* types describe the inner T. That stopped being true when the generator moved
|
|
198
|
+
* to envelope-typed returns, and the comment outlived the fact — so the peel
|
|
199
|
+
* then contradicted every type in every package it seeded. Four clients shipped
|
|
200
|
+
* that way (resource-governance x3, workload-runtime-api): declared
|
|
201
|
+
* *DataEnvelope, returned the already-peeled inner object, so .data read
|
|
202
|
+
* undefined at runtime on every non-paginated endpoint. Paginated calls hid it,
|
|
203
|
+
* because the old peel deliberately preserved an envelope carrying pagination.
|
|
204
|
+
*
|
|
205
|
+
* Four MORE shipped it in repositories the gate could not see — license-api and
|
|
206
|
+
* license-internal-api in xema-operator, plus their host-web mirrors — because
|
|
207
|
+
* the gate ran in xema-base only. That is why it now runs in every repository
|
|
208
|
+
* that ships a client, and why this file is generator-owned rather than seeded.
|
|
209
|
+
*/
|
|
210
|
+
|
|
74
211
|
export const customFetch = async <T>(
|
|
75
212
|
url: string,
|
|
76
213
|
options: RequestInit,
|
|
@@ -85,83 +222,70 @@ export const customFetch = async <T>(
|
|
|
85
222
|
);
|
|
86
223
|
}
|
|
87
224
|
const fullUrl = \`\${base}\${url}\`;
|
|
88
|
-
const maxRetries = config.maxRetries ??
|
|
225
|
+
const maxRetries = config.maxRetries ?? 0;
|
|
226
|
+
|
|
227
|
+
// A caller that supplied its own Authorization header owns that credential.
|
|
228
|
+
// \`buildHeaders\` lets it win, so refreshing the CLIENT-WIDE token and
|
|
229
|
+
// re-sending would replay the identical failing request with the identical
|
|
230
|
+
// credential: one wasted round trip, plus a global refresh nobody asked for.
|
|
231
|
+
const callerSuppliedAuth = new Headers(options.headers).has('Authorization');
|
|
89
232
|
|
|
90
233
|
const headers = await buildHeaders(config, options.headers);
|
|
91
234
|
const requestInit: RequestInit = { ...options, headers };
|
|
92
235
|
|
|
93
|
-
let delay =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
const response = await fetch(fullUrl, requestInit);
|
|
99
|
-
|
|
100
|
-
if (response.status === 401 && config.onUnauthorized && attempt === 0) {
|
|
101
|
-
await config.onUnauthorized();
|
|
102
|
-
const refreshedHeaders = await buildHeaders(config, options.headers);
|
|
103
|
-
const retryResponse = await fetch(fullUrl, { ...options, headers: refreshedHeaders });
|
|
104
|
-
const retryBody = await parseBody(retryResponse);
|
|
105
|
-
if (retryResponse.status >= 400) {
|
|
106
|
-
throw new ClientError(retryResponse.status, fullUrl, retryBody);
|
|
107
|
-
}
|
|
108
|
-
return retryBody as T;
|
|
109
|
-
}
|
|
236
|
+
let delay = BASE_BACKOFF_MS;
|
|
237
|
+
|
|
238
|
+
for (let attempt = 0; ; attempt++) {
|
|
239
|
+
const response = await fetch(fullUrl, requestInit);
|
|
110
240
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
241
|
+
if (
|
|
242
|
+
response.status === 401 &&
|
|
243
|
+
config.onUnauthorized &&
|
|
244
|
+
!callerSuppliedAuth &&
|
|
245
|
+
attempt === 0
|
|
246
|
+
) {
|
|
247
|
+
await config.onUnauthorized();
|
|
248
|
+
const refreshedHeaders = await buildHeaders(config, options.headers);
|
|
249
|
+
const retryResponse = await fetch(fullUrl, {
|
|
250
|
+
...options,
|
|
251
|
+
headers: refreshedHeaders,
|
|
252
|
+
});
|
|
253
|
+
const retryBody = await parseBody(retryResponse);
|
|
254
|
+
if (retryResponse.status >= 400) {
|
|
255
|
+
throw new ClientError(retryResponse.status, fullUrl, retryBody);
|
|
117
256
|
}
|
|
257
|
+
return retryBody as T;
|
|
258
|
+
}
|
|
118
259
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
await sleep(waitMs);
|
|
129
|
-
delay = Math.min(delay * 2, 30_000);
|
|
260
|
+
if (
|
|
261
|
+
!RETRYABLE_STATUSES.includes(response.status) ||
|
|
262
|
+
attempt >= maxRetries
|
|
263
|
+
) {
|
|
264
|
+
const body = await parseBody(response);
|
|
265
|
+
if (response.status >= 400) {
|
|
266
|
+
throw new ClientError(response.status, fullUrl, body);
|
|
267
|
+
}
|
|
268
|
+
return body as T;
|
|
130
269
|
}
|
|
131
|
-
}
|
|
132
270
|
|
|
133
|
-
|
|
271
|
+
const retryAfter = parseRetryAfter(response.headers.get('Retry-After'));
|
|
272
|
+
const waitMs = retryAfter ?? addJitter(delay);
|
|
273
|
+
// Non-null: \`configureClient\` refuses a budget above 0 without an observer,
|
|
274
|
+
// and this line is unreachable unless \`maxRetries\` is above 0.
|
|
275
|
+
(config.onRetry as (notice: RetryNotice) => void)({
|
|
276
|
+
attempt: attempt + 1,
|
|
277
|
+
maxRetries,
|
|
278
|
+
status: response.status,
|
|
279
|
+
waitMs,
|
|
280
|
+
retryAfterHonoured: retryAfter !== undefined,
|
|
281
|
+
url: fullUrl,
|
|
282
|
+
method: options.method ?? 'GET',
|
|
283
|
+
});
|
|
284
|
+
await sleep(waitMs);
|
|
285
|
+
delay = Math.min(delay * 2, MAX_BACKOFF_MS);
|
|
286
|
+
}
|
|
134
287
|
};
|
|
135
288
|
|
|
136
|
-
/*
|
|
137
|
-
* THE TRANSPORT RETURNS THE BODY UNCHANGED. Do not reintroduce an unwrap here.
|
|
138
|
-
*
|
|
139
|
-
* Xema services wrap every 2xx payload in a { data: T } envelope via the global
|
|
140
|
-
* ResponseEnvelopeInterceptor (platform-common), and the generator now emits
|
|
141
|
-
* types that describe THAT ENVELOPE — every endpoint returns
|
|
142
|
-
* Promise<XDataEnvelope> or Promise<XPaginatedEnvelope>, never a bare inner
|
|
143
|
-
* T. Consumers read the data property themselves.
|
|
144
|
-
*
|
|
145
|
-
* This template used to peel data, justified by a comment claiming generated
|
|
146
|
-
* types describe the inner T. That stopped being true when the generator moved
|
|
147
|
-
* to envelope-typed returns, and the comment outlived the fact — so the peel
|
|
148
|
-
* then contradicted every type in every package it seeded. Four clients shipped
|
|
149
|
-
* that way (resource-governance x3, workload-runtime-api): declared
|
|
150
|
-
* *DataEnvelope, returned the already-peeled inner object, so .data read
|
|
151
|
-
* undefined at runtime on every non-paginated endpoint. Paginated calls hid
|
|
152
|
-
* it, because the old peel deliberately preserved an envelope carrying
|
|
153
|
-
* pagination.
|
|
154
|
-
*
|
|
155
|
-
* ensureCustomFetch seeds this template into any client that lacks a
|
|
156
|
-
* transport, so a peel here is not one bug — it is re-imported into every new
|
|
157
|
-
* client generated from now on, which is exactly how those four were born after
|
|
158
|
-
* the hand-fix of their siblings.
|
|
159
|
-
*
|
|
160
|
-
* Enforced fleet-wide by check-client-transport-envelope in xema-base, which
|
|
161
|
-
* reads the RETURN SITES rather than banning a name — a rename, an inline
|
|
162
|
-
* .data, or ?? body.data all walk past a string ban.
|
|
163
|
-
*/
|
|
164
|
-
|
|
165
289
|
async function parseBody(response: Response): Promise<unknown> {
|
|
166
290
|
const contentType = response.headers.get('content-type');
|
|
167
291
|
if (contentType?.includes('application/json')) {
|
|
@@ -187,7 +311,7 @@ function addJitter(delay: number): number {
|
|
|
187
311
|
}
|
|
188
312
|
|
|
189
313
|
function sleep(ms: number): Promise<void> {
|
|
190
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
314
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
191
315
|
}
|
|
192
316
|
|
|
193
317
|
export default customFetch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-fetch-template.js","sourceRoot":"","sources":["../../src/lib/custom-fetch-template.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"custom-fetch-template.js","sourceRoot":"","sources":["../../src/lib/custom-fetch-template.ts"],"names":[],"mappings":";;;AAca,QAAA,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0TrC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-openapi.d.ts","sourceRoot":"","sources":["../../src/lib/extract-openapi.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"extract-openapi.d.ts","sourceRoot":"","sources":["../../src/lib/extract-openapi.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,UAAU,EAShB,MAAM,yBAAyB,CAAC;AAwDjC,wBAAsB,wBAAwB,CAC5C,UAAU,GAAE,MAAsB,EAOlC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,EAAE,CAAC,CAkJvB;AAGD,wBAAgB,aAAa,IAAI,IAAI,CAiBpC"}
|
|
@@ -55,8 +55,8 @@ async function extractOpenApiForService(serviceDir = process.cwd(), outDir) {
|
|
|
55
55
|
const discovery = new nest.DiscoveryService(modulesContainer);
|
|
56
56
|
const metadataScanner = new nest.MetadataScanner();
|
|
57
57
|
const scan = (0, xema_decorators_1.scanApiSurfaces)(discovery, metadataScanner);
|
|
58
|
+
const unclassifiedInDoc = (0, xema_decorators_1.unclassifiedSurfacesInDocument)(scan, fullDoc);
|
|
58
59
|
const docOps = (0, xema_decorators_1.collectOperationIds)(fullDoc);
|
|
59
|
-
const unclassifiedInDoc = scan.unclassified.filter((u) => docOps.has(u.operationId));
|
|
60
60
|
if (unclassifiedInDoc.length > 0) {
|
|
61
61
|
(0, xema_decorators_1.assertNoUnclassifiedSurfaces)(ctx.api.serviceId, {
|
|
62
62
|
unclassified: unclassifiedInDoc,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-openapi.js","sourceRoot":"","sources":["../../src/lib/extract-openapi.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"extract-openapi.js","sourceRoot":"","sources":["../../src/lib/extract-openapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ function loadRepoPostProcessors(repoRoot) {
|
|
|
21
21
|
function rebuildIndexBarrel(clientSrc) {
|
|
22
22
|
const lines = [
|
|
23
23
|
'// Auto-generated by @xemahq/api-client-generator — do not edit manually.',
|
|
24
|
-
"export { configureClient, getClientConfig, ClientError, customFetch, type ClientConfig } from './custom-fetch';",
|
|
24
|
+
"export { configureClient, getClientConfig, ClientError, customFetch, type ClientConfig, type RetryNotice } from './custom-fetch';",
|
|
25
25
|
];
|
|
26
26
|
const modelsIndex = (0, node_path_1.join)(clientSrc, 'models', 'index.ts');
|
|
27
27
|
if ((0, node_fs_1.existsSync)(modelsIndex)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo-post-process.js","sourceRoot":"","sources":["../../src/lib/repo-post-process.ts"],"names":[],"mappings":";;AA4BA,wDAcC;AAqDD,8CAUC;AAzGD,qCAA+E;AAC/E,yCAAiC;AAejC,SAAS,eAAe,CAAC,QAAgB,EAAE,IAAY;IACrD,MAAM,IAAI,GAAG,IAAA,gBAAI,EAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,IAAA,oBAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,iDAAiD,IAAI,kBAAkB;YACrE,kCAAkC,IAAI,GAAG,CAC5C,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC;AAED,SAAgB,sBAAsB,CAAC,QAAgB;IACrD,MAAM,EAAE,sBAAsB,EAAE,GAAG,eAAe,CAChD,QAAQ,EACR,6BAA6B,CACuB,CAAC;IACvD,MAAM,EAAE,qBAAqB,EAAE,GAAG,eAAe,CAC/C,QAAQ,EACR,6BAA6B,CACsB,CAAC;IACtD,MAAM,EAAE,mBAAmB,EAAE,GAAG,eAAe,CAC7C,QAAQ,EACR,2BAA2B,CACsB,CAAC;IACpD,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,CAAC;AAChF,CAAC;AAOD,SAAS,kBAAkB,CAAC,SAAiB;IAC3C,MAAM,KAAK,GAAG;QACZ,2EAA2E;QAC3E,
|
|
1
|
+
{"version":3,"file":"repo-post-process.js","sourceRoot":"","sources":["../../src/lib/repo-post-process.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/api-client-generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Layer 1 — the single, central OpenAPI + ORVAL client generator for every Xema NestJS service. Replaces the per-service orval.config.ts / scripts/extract-openapi.ts / scripts/build-client-index.js trio with two bins (xema-openapi, xema-client-generate). Extracts one OpenAPI document per ApiSurface (public/internal/operator) using the @XemaApiSurface decorator metadata, generates one ORVAL client package per non-empty surface (every surface of a service landing on ONE derived version — the highest its clients already carry, advanced a patch when the generated output changes), and stamps deterministic `xema` provenance metadata into each generated client package.json. No heuristics: a route ships in a surface only if it (or its controller) is explicitly classified, and no client version is ever typed by hand.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"xema",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"prettier": "3.6.2",
|
|
62
62
|
"reflect-metadata": "0.2.2",
|
|
63
63
|
"typescript": "5.9.3",
|
|
64
|
-
"@xemahq/xema-decorators": "0.7.
|
|
64
|
+
"@xemahq/xema-decorators": "0.7.3"
|
|
65
65
|
},
|
|
66
66
|
"exports": {
|
|
67
67
|
".": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"clean": "rm -rf dist",
|
|
75
|
-
"build": "tsc -p tsconfig.build.json",
|
|
75
|
+
"build": "pnpm run clean && tsc -p tsconfig.build.json && node scripts/write-dist-source-stamp.mjs",
|
|
76
76
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
77
77
|
"lint": "eslint .",
|
|
78
78
|
"test": "node --test \"tests/**/*.test.cjs\"",
|