@xemahq/org-database-pool-internal-api-client 0.1.0

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/LICENSE ADDED
@@ -0,0 +1 @@
1
+ Copyright (c) 2026 Xema. All rights reserved.
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Configurable fetch wrapper for Orval-generated clients.
3
+ *
4
+ * Consumers must call `configureClient()` before using any endpoint function.
5
+ * The baseUrl is prepended to the relative paths generated by Orval.
6
+ *
7
+ * Features:
8
+ * - Automatic bearer-token injection via `getAuthToken` callback
9
+ * - Per-request header injection via `getHeaders` callback
10
+ * - Automatic 401 handling via `onUnauthorized` callback (single retry)
11
+ * - Exponential-backoff retry for transient failures (429, 502, 503, 504)
12
+ * - Typed `ClientError` for non-2xx responses
13
+ * - Per-call header overrides (take precedence over global headers)
14
+ *
15
+ * @example
16
+ * configureClient({
17
+ * baseUrl: 'http://org-database-pool-api:3000',
18
+ * getAuthToken: () => identityBootstrapService.getAccessToken(),
19
+ * getHeaders: () => ({ 'X-Org-Id': orgId, 'X-Correlation-Id': crypto.randomUUID() }),
20
+ * });
21
+ */
22
+ export interface ClientConfig {
23
+ /** Base URL for the API (e.g. 'http://localhost:3284') — no trailing slash. */
24
+ baseUrl?: string;
25
+ /**
26
+ * Per-request base-URL resolver. When set, the peer URL is resolved from
27
+ * the service registry on EVERY request (boot-order-safe). Wired by
28
+ * `configureOrvalClientResolved` from `@xemahq/platform-common`.
29
+ */
30
+ baseUrlResolver?: () => string | Promise<string>;
31
+ /** Optional async callback to get an auth token. Auto-sets Authorization header on every request. */
32
+ getAuthToken?: () => Promise<string>;
33
+ /** Optional callback returning headers to inject on every request. Per-call headers take precedence. */
34
+ getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
35
+ /** Optional callback invoked on 401 before a single retry (e.g. force-refresh auth token). */
36
+ onUnauthorized?: () => Promise<void>;
37
+ /** Maximum retry attempts for transient failures (default: 3). Set to 0 to disable retries. */
38
+ maxRetries?: number;
39
+ }
40
+ export declare class ClientError extends Error {
41
+ readonly status: number;
42
+ readonly url: string;
43
+ readonly body: unknown;
44
+ constructor(status: number, url: string, body: unknown);
45
+ }
46
+ export declare function configureClient(config: ClientConfig): void;
47
+ export declare function getClientConfig(): ClientConfig;
48
+ export declare const customFetch: <T>(url: string, options: RequestInit) => Promise<T>;
49
+ export default customFetch;
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ /**
3
+ * Configurable fetch wrapper for Orval-generated clients.
4
+ *
5
+ * Consumers must call `configureClient()` before using any endpoint function.
6
+ * The baseUrl is prepended to the relative paths generated by Orval.
7
+ *
8
+ * Features:
9
+ * - Automatic bearer-token injection via `getAuthToken` callback
10
+ * - Per-request header injection via `getHeaders` callback
11
+ * - Automatic 401 handling via `onUnauthorized` callback (single retry)
12
+ * - Exponential-backoff retry for transient failures (429, 502, 503, 504)
13
+ * - Typed `ClientError` for non-2xx responses
14
+ * - Per-call header overrides (take precedence over global headers)
15
+ *
16
+ * @example
17
+ * configureClient({
18
+ * baseUrl: 'http://org-database-pool-api:3000',
19
+ * getAuthToken: () => identityBootstrapService.getAccessToken(),
20
+ * getHeaders: () => ({ 'X-Org-Id': orgId, 'X-Correlation-Id': crypto.randomUUID() }),
21
+ * });
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.customFetch = exports.ClientError = void 0;
25
+ exports.configureClient = configureClient;
26
+ exports.getClientConfig = getClientConfig;
27
+ class ClientError extends Error {
28
+ status;
29
+ url;
30
+ body;
31
+ constructor(status, url, body) {
32
+ super(`HTTP ${status} from ${url}`);
33
+ this.status = status;
34
+ this.url = url;
35
+ this.body = body;
36
+ this.name = 'ClientError';
37
+ }
38
+ }
39
+ exports.ClientError = ClientError;
40
+ let clientConfig = null;
41
+ function configureClient(config) {
42
+ clientConfig = config;
43
+ }
44
+ function getClientConfig() {
45
+ if (!clientConfig) {
46
+ throw new Error('Client not configured. Call configureClient({ baseUrl }) before using endpoint functions.');
47
+ }
48
+ return clientConfig;
49
+ }
50
+ const RETRYABLE_STATUSES = [429, 502, 503, 504];
51
+ async function buildHeaders(config, callerHeaders) {
52
+ const headers = new Headers(callerHeaders);
53
+ if (config.getHeaders) {
54
+ const globalHeaders = await Promise.resolve(config.getHeaders());
55
+ for (const [key, value] of Object.entries(globalHeaders)) {
56
+ if (!headers.has(key)) {
57
+ headers.set(key, value);
58
+ }
59
+ }
60
+ }
61
+ if (config.getAuthToken && !headers.has('Authorization')) {
62
+ const token = await config.getAuthToken();
63
+ headers.set('Authorization', `Bearer ${token}`);
64
+ }
65
+ return headers;
66
+ }
67
+ const customFetch = async (url, options) => {
68
+ const config = getClientConfig();
69
+ const base = config.baseUrlResolver
70
+ ? await config.baseUrlResolver()
71
+ : config.baseUrl;
72
+ if (base === undefined) {
73
+ throw new Error('Client not configured: set baseUrl or baseUrlResolver via configureClient().');
74
+ }
75
+ const fullUrl = `${base}${url}`;
76
+ const maxRetries = config.maxRetries ?? 3;
77
+ const headers = await buildHeaders(config, options.headers);
78
+ const requestInit = { ...options, headers };
79
+ let delay = 1000;
80
+ let lastError;
81
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
82
+ try {
83
+ const response = await fetch(fullUrl, requestInit);
84
+ if (response.status === 401 && config.onUnauthorized && attempt === 0) {
85
+ await config.onUnauthorized();
86
+ const refreshedHeaders = await buildHeaders(config, options.headers);
87
+ const retryResponse = await fetch(fullUrl, { ...options, headers: refreshedHeaders });
88
+ const retryBody = await parseBody(retryResponse);
89
+ if (retryResponse.status >= 400) {
90
+ throw new ClientError(retryResponse.status, fullUrl, retryBody);
91
+ }
92
+ return retryBody;
93
+ }
94
+ if (!RETRYABLE_STATUSES.includes(response.status) || attempt >= maxRetries) {
95
+ const body = await parseBody(response);
96
+ if (response.status >= 400) {
97
+ throw new ClientError(response.status, fullUrl, body);
98
+ }
99
+ return body;
100
+ }
101
+ const retryAfter = parseRetryAfter(response.headers.get('Retry-After'));
102
+ const waitMs = retryAfter ?? addJitter(delay);
103
+ await sleep(waitMs);
104
+ delay = Math.min(delay * 2, 30_000);
105
+ }
106
+ catch (error) {
107
+ if (error instanceof ClientError)
108
+ throw error;
109
+ lastError = error instanceof Error ? error : new Error(String(error));
110
+ if (attempt >= maxRetries)
111
+ throw lastError;
112
+ const waitMs = addJitter(delay);
113
+ await sleep(waitMs);
114
+ delay = Math.min(delay * 2, 30_000);
115
+ }
116
+ }
117
+ throw lastError ?? new Error(`All retries exhausted for ${fullUrl}`);
118
+ };
119
+ exports.customFetch = customFetch;
120
+ async function parseBody(response) {
121
+ const contentType = response.headers.get('content-type');
122
+ if (contentType?.includes('application/json')) {
123
+ return response.json();
124
+ }
125
+ if (response.status === 204) {
126
+ return undefined;
127
+ }
128
+ return response.text();
129
+ }
130
+ function parseRetryAfter(value) {
131
+ if (!value)
132
+ return undefined;
133
+ const seconds = Number(value);
134
+ if (!isNaN(seconds) && seconds >= 0)
135
+ return seconds * 1000;
136
+ const date = new Date(value);
137
+ if (!isNaN(date.getTime()))
138
+ return Math.max(0, date.getTime() - Date.now());
139
+ return undefined;
140
+ }
141
+ function addJitter(delay) {
142
+ return delay + (Math.random() * 2 - 1) * delay * 0.25;
143
+ }
144
+ function sleep(ms) {
145
+ return new Promise(resolve => setTimeout(resolve, ms));
146
+ }
147
+ exports.default = exports.customFetch;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Generated by orval v8.6.2 🍺
3
+ * Do not edit manually.
4
+ * Org Database Pool API
5
+ * Org Database Pool API extract (generated for client codegen).
6
+ * OpenAPI spec version: 0.1.0
7
+ */
8
+ import type { MintConnectionDto } from '../../models';
9
+ /**
10
+ * @summary Mint a short-lived connection grant for an org database
11
+ */
12
+ export declare const getConnectionControllerMintUrl: () => string;
13
+ export declare const connectionControllerMint: (mintConnectionDto: MintConnectionDto, options?: RequestInit) => Promise<void>;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.connectionControllerMint = exports.getConnectionControllerMintUrl = void 0;
4
+ const custom_fetch_1 = require("../../custom-fetch");
5
+ /**
6
+ * @summary Mint a short-lived connection grant for an org database
7
+ */
8
+ const getConnectionControllerMintUrl = () => {
9
+ return `/connections/mint`;
10
+ };
11
+ exports.getConnectionControllerMintUrl = getConnectionControllerMintUrl;
12
+ const connectionControllerMint = async (mintConnectionDto, options) => {
13
+ return (0, custom_fetch_1.customFetch)((0, exports.getConnectionControllerMintUrl)(), {
14
+ ...options,
15
+ method: 'POST',
16
+ headers: { 'Content-Type': 'application/json', ...options?.headers },
17
+ body: JSON.stringify(mintConnectionDto)
18
+ });
19
+ };
20
+ exports.connectionControllerMint = connectionControllerMint;
@@ -0,0 +1,3 @@
1
+ export { configureClient, getClientConfig, ClientError, customFetch, type ClientConfig } from './custom-fetch';
2
+ export * from './models';
3
+ export * from './endpoints/connections/connections';
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.customFetch = exports.ClientError = exports.getClientConfig = exports.configureClient = void 0;
18
+ // Auto-generated by @xemahq/api-client-generator — do not edit manually.
19
+ var custom_fetch_1 = require("./custom-fetch");
20
+ Object.defineProperty(exports, "configureClient", { enumerable: true, get: function () { return custom_fetch_1.configureClient; } });
21
+ Object.defineProperty(exports, "getClientConfig", { enumerable: true, get: function () { return custom_fetch_1.getClientConfig; } });
22
+ Object.defineProperty(exports, "ClientError", { enumerable: true, get: function () { return custom_fetch_1.ClientError; } });
23
+ Object.defineProperty(exports, "customFetch", { enumerable: true, get: function () { return custom_fetch_1.customFetch; } });
24
+ __exportStar(require("./models"), exports);
25
+ __exportStar(require("./endpoints/connections/connections"), exports);
@@ -0,0 +1 @@
1
+ export * from './mintConnectionDto';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Auto-generated by tooling/codegen/regenerate-models-barrel.js — do not edit manually.
18
+ __exportStar(require("./mintConnectionDto"), exports);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Generated by orval v8.6.2 🍺
3
+ * Do not edit manually.
4
+ * Org Database Pool API
5
+ * Org Database Pool API extract (generated for client codegen).
6
+ * OpenAPI spec version: 0.1.0
7
+ */
8
+ export interface MintConnectionDto {
9
+ [key: string]: unknown;
10
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * Generated by orval v8.6.2 🍺
4
+ * Do not edit manually.
5
+ * Org Database Pool API
6
+ * Org Database Pool API extract (generated for client codegen).
7
+ * OpenAPI spec version: 0.1.0
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@xemahq/org-database-pool-internal-api-client",
3
+ "version": "0.1.0",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "publishConfig": {
10
+ "registry": "https://npm.pkg.github.com"
11
+ },
12
+ "devDependencies": {
13
+ "typescript": "5.9.3"
14
+ },
15
+ "xema": {
16
+ "kind": "api-client",
17
+ "surface": "internal",
18
+ "service": "org-database-pool-api",
19
+ "biome": "org-database-pool",
20
+ "target": "server",
21
+ "generator": "@xemahq/api-client-generator@0.1.0",
22
+ "source": "openapi.internal.json"
23
+ },
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json"
26
+ }
27
+ }