@fabricorg/experiments-db-pool 0.1.0 → 0.1.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/index.cjs +75 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -1
- package/dist/index.d.ts +95 -1
- package/dist/index.js +73 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -79,61 +79,85 @@ function servicePrincipalProvider(principal) {
|
|
|
79
79
|
cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };
|
|
80
80
|
return token;
|
|
81
81
|
};
|
|
82
|
-
|
|
82
|
+
const getToken = async () => {
|
|
83
83
|
if (cached && Date.now() < cached.refreshAtMs) return cached.token;
|
|
84
84
|
refreshing ??= refresh().finally(() => {
|
|
85
85
|
refreshing = void 0;
|
|
86
86
|
});
|
|
87
87
|
return refreshing;
|
|
88
88
|
};
|
|
89
|
+
getToken.refresh = async () => {
|
|
90
|
+
cached = void 0;
|
|
91
|
+
return getToken();
|
|
92
|
+
};
|
|
93
|
+
return getToken;
|
|
89
94
|
}
|
|
90
|
-
function
|
|
91
|
-
const workspaceHost = options.workspaceHost.replace(/\/$/, "");
|
|
95
|
+
async function exchangeLakebaseCredential(options) {
|
|
92
96
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
97
|
+
const response = await fetchImpl(
|
|
98
|
+
`${options.workspaceHost.replace(/\/$/, "")}/api/2.0/postgres/credentials`,
|
|
99
|
+
{
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: {
|
|
102
|
+
authorization: `Bearer ${options.workspaceToken}`,
|
|
103
|
+
"content-type": "application/json"
|
|
104
|
+
},
|
|
105
|
+
body: JSON.stringify({ endpoint: options.endpoint })
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
if (!response.ok) {
|
|
109
|
+
const text = await response.text();
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const value = await response.json();
|
|
115
|
+
if (typeof value.token !== "string" || !value.token) {
|
|
116
|
+
throw new Error("Lakebase credential exchange returned no token.");
|
|
117
|
+
}
|
|
118
|
+
const expiresAt = parseExpireTime(value.expire_time);
|
|
119
|
+
if (!Number.isFinite(expiresAt)) {
|
|
120
|
+
throw new Error("Lakebase credential exchange returned an invalid expire_time.");
|
|
121
|
+
}
|
|
122
|
+
return { token: value.token, expiresAt };
|
|
123
|
+
}
|
|
124
|
+
function lakebaseCredentialProvider(options) {
|
|
93
125
|
const now = options.now ?? Date.now;
|
|
94
126
|
const random = options.random ?? Math.random;
|
|
95
127
|
const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 6e4;
|
|
96
128
|
const refreshJitterMs = options.refreshJitterMs ?? 3e4;
|
|
97
129
|
let cached;
|
|
98
130
|
let refreshing;
|
|
99
|
-
const refresh = async () => {
|
|
100
|
-
const workspaceToken = typeof options.token === "function" ? await options.token() : options.token;
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
body: JSON.stringify({ endpoint: options.endpoint })
|
|
131
|
+
const refresh = async (forceWorkspaceToken = false) => {
|
|
132
|
+
const workspaceToken = typeof options.token === "function" ? await (forceWorkspaceToken && options.token.refresh ? options.token.refresh() : options.token()) : options.token;
|
|
133
|
+
const value = await exchangeLakebaseCredential({
|
|
134
|
+
workspaceHost: options.workspaceHost,
|
|
135
|
+
endpoint: options.endpoint,
|
|
136
|
+
workspaceToken,
|
|
137
|
+
fetchImpl: options.fetchImpl
|
|
108
138
|
});
|
|
109
|
-
if (!response.ok) {
|
|
110
|
-
const text = await response.text();
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
const value = await response.json();
|
|
116
|
-
if (typeof value.token !== "string" || !value.token) {
|
|
117
|
-
throw new Error("Lakebase credential exchange returned no token.");
|
|
118
|
-
}
|
|
119
|
-
const expiresAt = parseExpireTime(value.expire_time);
|
|
120
|
-
if (!Number.isFinite(expiresAt)) {
|
|
121
|
-
throw new Error("Lakebase credential exchange returned an invalid expire_time.");
|
|
122
|
-
}
|
|
123
139
|
const jitter = Math.max(0, refreshJitterMs) * random();
|
|
124
140
|
cached = {
|
|
125
141
|
token: value.token,
|
|
126
|
-
refreshAt: Math.max(now(), expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
142
|
+
refreshAt: Math.max(now(), value.expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
127
143
|
};
|
|
128
144
|
return value.token;
|
|
129
145
|
};
|
|
130
|
-
|
|
146
|
+
const getCredential = async () => {
|
|
131
147
|
if (cached && now() < cached.refreshAt) return cached.token;
|
|
132
|
-
refreshing ??= refresh().finally(() => {
|
|
148
|
+
refreshing ??= refresh(false).finally(() => {
|
|
149
|
+
refreshing = void 0;
|
|
150
|
+
});
|
|
151
|
+
return refreshing;
|
|
152
|
+
};
|
|
153
|
+
getCredential.refresh = async () => {
|
|
154
|
+
cached = void 0;
|
|
155
|
+
refreshing ??= refresh(true).finally(() => {
|
|
133
156
|
refreshing = void 0;
|
|
134
157
|
});
|
|
135
158
|
return refreshing;
|
|
136
159
|
};
|
|
160
|
+
return getCredential;
|
|
137
161
|
}
|
|
138
162
|
function parseExpireTime(value) {
|
|
139
163
|
if (typeof value === "number") {
|
|
@@ -201,6 +225,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
201
225
|
options;
|
|
202
226
|
poolFactory;
|
|
203
227
|
getPassword;
|
|
228
|
+
refreshPassword;
|
|
204
229
|
delegatingPool;
|
|
205
230
|
refreshTimer;
|
|
206
231
|
constructor(options) {
|
|
@@ -209,6 +234,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
209
234
|
this.delegatingPool = new DelegatingPool();
|
|
210
235
|
if (options.password !== void 0) {
|
|
211
236
|
this.getPassword = async () => options.password;
|
|
237
|
+
this.refreshPassword = this.getPassword;
|
|
212
238
|
this.delegatingPool.swap(this.createPool(options.password));
|
|
213
239
|
} else {
|
|
214
240
|
if (!options.token || !options.workspaceHost || !options.endpoint) {
|
|
@@ -216,16 +242,23 @@ var LakebaseDatabaseProvider = class {
|
|
|
216
242
|
"Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password."
|
|
217
243
|
);
|
|
218
244
|
}
|
|
219
|
-
|
|
245
|
+
const credential = lakebaseCredentialProvider({
|
|
220
246
|
workspaceHost: options.workspaceHost,
|
|
221
247
|
endpoint: options.endpoint,
|
|
222
248
|
token: options.token,
|
|
223
249
|
fetchImpl: options.fetchImpl
|
|
224
250
|
});
|
|
251
|
+
this.getPassword = credential;
|
|
252
|
+
this.refreshPassword = credential.refresh;
|
|
225
253
|
this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);
|
|
226
|
-
const intervalMs = options.refreshIntervalMs ??
|
|
254
|
+
const intervalMs = options.refreshIntervalMs ?? 45 * 6e4;
|
|
227
255
|
this.refreshTimer = setInterval(() => {
|
|
228
|
-
void this.
|
|
256
|
+
void this.refreshCredentials().catch((error) => {
|
|
257
|
+
this.delegatingPool.emit(
|
|
258
|
+
"error",
|
|
259
|
+
error instanceof Error ? error : new Error(String(error))
|
|
260
|
+
);
|
|
261
|
+
});
|
|
229
262
|
}, intervalMs);
|
|
230
263
|
}
|
|
231
264
|
}
|
|
@@ -239,17 +272,15 @@ var LakebaseDatabaseProvider = class {
|
|
|
239
272
|
ssl: this.options.ssl ?? { rejectUnauthorized: true },
|
|
240
273
|
max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? "5", 10),
|
|
241
274
|
idleTimeoutMillis: this.options.idleTimeoutMillis ?? 3e4,
|
|
242
|
-
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3
|
|
275
|
+
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3,
|
|
276
|
+
options: "-c search_path=fx,public"
|
|
243
277
|
});
|
|
244
278
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
} catch (error) {
|
|
251
|
-
this.delegatingPool.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
252
|
-
}
|
|
279
|
+
/** Force a new database credential and atomically swap the active pool. */
|
|
280
|
+
async refreshCredentials() {
|
|
281
|
+
const password = await this.refreshPassword();
|
|
282
|
+
const pool = this.createPool(password);
|
|
283
|
+
this.delegatingPool.swap(pool);
|
|
253
284
|
}
|
|
254
285
|
getPool() {
|
|
255
286
|
return this.delegatingPool;
|
|
@@ -370,9 +401,12 @@ function registerPoolMetrics(collector, poolInstance, intervalMs = 5e3) {
|
|
|
370
401
|
return () => clearInterval(interval);
|
|
371
402
|
}
|
|
372
403
|
|
|
404
|
+
exports.LakebaseDatabaseProvider = LakebaseDatabaseProvider;
|
|
373
405
|
exports.closePool = closePool;
|
|
406
|
+
exports.exchangeLakebaseCredential = exchangeLakebaseCredential;
|
|
374
407
|
exports.getPool = getPool;
|
|
375
408
|
exports.getProvider = getProvider;
|
|
409
|
+
exports.lakebaseCredentialProvider = lakebaseCredentialProvider;
|
|
376
410
|
exports.registerPoolMetrics = registerPoolMetrics;
|
|
377
411
|
exports.resetPoolProvider = resetPoolProvider;
|
|
378
412
|
exports.updatePoolMetrics = updatePoolMetrics;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/neon.ts","../src/providers/lakebase.ts","../src/index.ts"],"names":["Pool","EventEmitter"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\nexport interface NeonProviderOptions {\n connectionString: string;\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n}\n\n/** Standard Postgres provider using a static connection string. */\nexport class NeonDatabaseProvider implements DatabaseProvider {\n readonly kind = 'neon' as const;\n private readonly pool: Pool;\n\n constructor(options: NeonProviderOptions) {\n this.pool = new Pool({\n connectionString: options.connectionString,\n max: options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5_000,\n });\n }\n\n getPool(): Pool {\n return this.pool;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\n/** A resolver that returns a valid Databricks workspace OAuth token. */\nexport type DatabricksTokenProvider = () => Promise<string>;\n\n/** Supported Databricks authentication shapes. */\nexport type DatabricksPrincipal =\n | { kind: 'pat'; token: string }\n | {\n kind: 'service-principal';\n host: string;\n clientId: string;\n clientSecret: string;\n scope?: string;\n refreshSkewSeconds?: number;\n };\n\n/** Resolve a PAT or OAuth M2M principal to a cached, auto-refreshing token provider. */\nexport function databricksIdentity(principal: DatabricksPrincipal): DatabricksTokenProvider {\n if (principal.kind === 'pat') {\n const { token } = principal;\n return async () => token;\n }\n return servicePrincipalProvider(principal);\n}\n\n/** Build a principal from environment variables. */\nexport function databricksPrincipalFromEnv(\n env: Record<string, string | undefined> = typeof process !== 'undefined' ? process.env : {},\n): Extract<DatabricksPrincipal, { kind: 'pat' | 'service-principal' }> {\n const token = env.DATABRICKS_TOKEN?.trim();\n if (token) return { kind: 'pat', token };\n\n const clientId = env.DATABRICKS_CLIENT_ID?.trim();\n const clientSecret = env.DATABRICKS_CLIENT_SECRET?.trim();\n const host = env.DATABRICKS_HOST?.trim();\n if (clientId && clientSecret) {\n if (!host) throw new Error('Missing DATABRICKS_HOST for Databricks OAuth M2M.');\n return {\n kind: 'service-principal',\n host,\n clientId,\n clientSecret,\n scope: env.DATABRICKS_SCOPE,\n refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS\n ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10)\n : undefined,\n };\n }\n throw new Error(\n 'Missing Databricks credentials: DATABRICKS_TOKEN or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET.',\n );\n}\n\ninterface CachedToken {\n token: string;\n refreshAtMs: number;\n}\n\nfunction servicePrincipalProvider(\n principal: Extract<DatabricksPrincipal, { kind: 'service-principal' }>,\n): DatabricksTokenProvider {\n const host = principal.host.replace(/\\/$/, '');\n const scope = principal.scope ?? 'all-apis';\n const refreshSkewMs = (principal.refreshSkewSeconds ?? 60) * 1_000;\n let cached: CachedToken | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (): Promise<string> => {\n const response = await fetch(`${host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${Buffer.from(`${principal.clientId}:${principal.clientSecret}`).toString('base64')}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope }),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Databricks OAuth token exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as { access_token?: unknown; expires_in?: unknown };\n const token = typeof value.access_token === 'string' ? value.access_token : undefined;\n if (!token) throw new Error('Databricks OAuth token exchange returned no access_token.');\n const expiresInSeconds = typeof value.expires_in === 'number' ? value.expires_in : 3_600;\n const expiresAtMs = Date.now() + expiresInSeconds * 1_000;\n cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };\n return token;\n };\n\n return async () => {\n if (cached && Date.now() < cached.refreshAtMs) return cached.token;\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n}\n\nexport interface LakebaseCredentialProviderOptions {\n workspaceHost: string;\n endpoint: string;\n token: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n refreshBeforeMs?: number;\n refreshJitterMs?: number;\n now?: () => number;\n random?: () => number;\n}\n\ninterface LakebaseCredentialResponse {\n token?: unknown;\n expire_time?: unknown;\n}\n\n/** Exchange a workspace OAuth token for a Lakebase database credential. Caches, refreshes early, single-flight. */\nexport function lakebaseCredentialProvider(\n options: LakebaseCredentialProviderOptions,\n): () => Promise<string> {\n const workspaceHost = options.workspaceHost.replace(/\\/$/, '');\n const fetchImpl = options.fetchImpl ?? fetch;\n const now = options.now ?? Date.now;\n const random = options.random ?? Math.random;\n const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 60_000;\n const refreshJitterMs = options.refreshJitterMs ?? 30_000;\n let cached: { token: string; refreshAt: number } | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (): Promise<string> => {\n const workspaceToken =\n typeof options.token === 'function' ? await options.token() : options.token;\n const response = await fetchImpl(`${workspaceHost}/api/2.0/postgres/credentials`, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${workspaceToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ endpoint: options.endpoint }),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as LakebaseCredentialResponse;\n if (typeof value.token !== 'string' || !value.token) {\n throw new Error('Lakebase credential exchange returned no token.');\n }\n const expiresAt = parseExpireTime(value.expire_time);\n if (!Number.isFinite(expiresAt)) {\n throw new Error('Lakebase credential exchange returned an invalid expire_time.');\n }\n const jitter = Math.max(0, refreshJitterMs) * random();\n cached = {\n token: value.token,\n refreshAt: Math.max(now(), expiresAt - Math.max(0, refreshBeforeMs) - jitter),\n };\n return value.token;\n };\n\n return async () => {\n if (cached && now() < cached.refreshAt) return cached.token;\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n}\n\nfunction parseExpireTime(value: unknown): number {\n if (typeof value === 'number') {\n return value < 1_000_000_000_000 ? value * 1_000 : value;\n }\n if (typeof value === 'string') return new Date(value).getTime();\n return Number.NaN;\n}\n\nexport interface LakebasePoolConfig {\n host: string;\n port: number;\n database: string;\n user: string;\n password?: string;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n}\n\nexport type LakebasePoolFactory = (config: LakebasePoolConfig) => Pool;\n\nexport interface LakebaseProviderOptions {\n /** Direct endpoint host from list-endpoints (NOT the pooled host). */\n host: string;\n port?: number;\n database: string;\n user: string;\n /** Static database password. If omitted, OAuth exchange is required. */\n password?: string;\n /** Workspace URL, e.g. https://adb-....azuredatabricks.net */\n workspaceHost?: string;\n /** Full endpoint resource name: projects/.../branches/.../endpoints/... */\n endpoint?: string;\n /** Workspace OAuth token or provider. */\n token?: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n /** How often to refresh the credential and recreate the pool. Default 50 minutes. */\n refreshIntervalMs?: number;\n /** Injected pool factory for tests. */\n poolFactory?: LakebasePoolFactory;\n /** Observe recoverable idle pooled-connection failures. */\n onPoolError?: (error: Error) => void;\n}\n\n/** A Pool-like facade that lazily initializes and always delegates to the current underlying pool. */\nclass DelegatingPool extends EventEmitter {\n private current: Pool | undefined;\n private initPromise: Promise<void> | undefined;\n private closed = false;\n\n private attachErrorHandler(pool: Pool) {\n pool.on('error', (error) => {\n this.emit('error', error);\n });\n }\n\n initialize(createPool: (password: string) => Pool, getPassword: () => Promise<string>) {\n this.initPromise = getPassword()\n .then((password) => {\n if (!this.closed) this.swap(createPool(password));\n })\n .catch((error) => {\n this.emit('error', error instanceof Error ? error : new Error(String(error)));\n throw error;\n });\n }\n\n swap(pool: Pool) {\n if (this.closed) {\n pool.end().catch(() => undefined);\n return;\n }\n const previous = this.current;\n this.current = pool;\n this.attachErrorHandler(pool);\n if (previous) {\n previous.removeAllListeners();\n previous.end().catch(() => undefined);\n }\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initPromise) await this.initPromise;\n }\n\n query(...args: Parameters<Pool['query']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.query(...args));\n }\n\n connect(...args: Parameters<Pool['connect']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.connect(...args));\n }\n\n end(): Promise<void> {\n this.closed = true;\n this.removeAllListeners();\n return this.current?.end() ?? Promise.resolve();\n }\n\n get totalCount() {\n return this.current?.totalCount ?? 0;\n }\n\n get idleCount() {\n return this.current?.idleCount ?? 0;\n }\n\n get waitingCount() {\n return this.current?.waitingCount ?? 0;\n }\n}\n\n/** Databricks Lakebase Postgres provider with credential refresh. */\nexport class LakebaseDatabaseProvider implements DatabaseProvider {\n readonly kind = 'databricks-lakebase' as const;\n private readonly options: LakebaseProviderOptions;\n private readonly poolFactory: LakebasePoolFactory;\n private readonly getPassword: () => Promise<string>;\n private readonly delegatingPool: DelegatingPool;\n private refreshTimer: NodeJS.Timeout | undefined;\n\n constructor(options: LakebaseProviderOptions) {\n this.options = options;\n this.poolFactory = options.poolFactory ?? defaultPoolFactory();\n this.delegatingPool = new DelegatingPool();\n if (options.password !== undefined) {\n this.getPassword = async () => options.password as string;\n this.delegatingPool.swap(this.createPool(options.password));\n } else {\n if (!options.token || !options.workspaceHost || !options.endpoint) {\n throw new Error(\n 'Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password.',\n );\n }\n this.getPassword = lakebaseCredentialProvider({\n workspaceHost: options.workspaceHost,\n endpoint: options.endpoint,\n token: options.token,\n fetchImpl: options.fetchImpl,\n });\n this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);\n const intervalMs = options.refreshIntervalMs ?? 50 * 60_000;\n this.refreshTimer = setInterval(() => {\n void this.refreshPool();\n }, intervalMs);\n }\n }\n\n private createPool(password?: string): Pool {\n return this.poolFactory({\n host: this.options.host,\n port: this.options.port ?? 5432,\n database: this.options.database,\n user: this.options.user,\n password,\n ssl: this.options.ssl ?? { rejectUnauthorized: true },\n max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: this.options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5_000,\n });\n }\n\n private async refreshPool(): Promise<void> {\n try {\n const password = await this.getPassword();\n const pool = this.createPool(password);\n this.delegatingPool.swap(pool);\n } catch (error) {\n this.delegatingPool.emit('error', error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n getPool(): Pool {\n return this.delegatingPool as unknown as Pool;\n }\n\n async close(): Promise<void> {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = undefined;\n }\n await this.delegatingPool.end();\n }\n}\n\nfunction defaultPoolFactory(): LakebasePoolFactory {\n return (config) => new Pool(config);\n}\n","import type { Gauge, MetricsCollector } from '@fabricorg/experiments-metrics';\nimport type { Pool } from 'pg';\nimport {\n type DatabaseProvider,\n LakebaseDatabaseProvider,\n NeonDatabaseProvider,\n databricksIdentity,\n databricksPrincipalFromEnv,\n} from './providers/index.js';\n\nlet provider: DatabaseProvider | null = null;\n\nfunction createProvider(): DatabaseProvider {\n const kind = process.env.DATABASE_PROVIDER ?? 'neon';\n\n if (kind === 'databricks-lakebase') {\n const host = process.env.DATABRICKS_LAKEBASE_HOST?.trim();\n const database = process.env.DATABRICKS_LAKEBASE_DATABASE?.trim() ?? 'databricks_postgres';\n const user = process.env.DATABRICKS_LAKEBASE_USER?.trim();\n const workspaceHost = process.env.DATABRICKS_HOST?.trim();\n const endpoint = process.env.DATABRICKS_LAKEBASE_ENDPOINT?.trim();\n const password = process.env.DATABRICKS_LAKEBASE_PASSWORD?.trim();\n\n if (!host) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_HOST is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n if (!user) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_USER is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n\n if (password) {\n return new LakebaseDatabaseProvider({ host, database, user, password });\n }\n\n if (!workspaceHost || !endpoint) {\n throw new Error(\n 'DATABRICKS_HOST and DATABRICKS_LAKEBASE_ENDPOINT are required for Lakebase OAuth, or provide DATABRICKS_LAKEBASE_PASSWORD for native auth.',\n );\n }\n\n const principal = databricksPrincipalFromEnv(process.env);\n return new LakebaseDatabaseProvider({\n host,\n database,\n user,\n workspaceHost,\n endpoint,\n token: databricksIdentity(principal),\n });\n }\n\n const connectionString = process.env.DATABASE_URL;\n if (!connectionString) {\n throw new Error('DATABASE_URL is required');\n }\n return new NeonDatabaseProvider({ connectionString });\n}\n\n/**\n * Return a singleton `pg.Pool` configured for the active database provider.\n *\n * Provider selection:\n * - `DATABASE_PROVIDER=neon` (default): uses `DATABASE_URL`.\n * - `DATABASE_PROVIDER=databricks-lakebase`: uses Lakebase OAuth credentials.\n *\n * On the first call, a new Pool is created with the following defaults:\n * - `max`: parseInt(process.env.PG_POOL_MAX ?? '5') — caps concurrent connections.\n * - `idleTimeoutMillis`: 30000 — recycle idle connections before Vercel's 60s timeout.\n * - `connectionTimeoutMillis`: 5000 — fail fast when the database is unreachable.\n */\nexport function getPool(): Pool {\n if (!provider) {\n provider = createProvider();\n }\n return provider.getPool();\n}\n\n/** Close the active provider and any background refresh loops. */\nexport async function closePool(): Promise<void> {\n await provider?.close();\n provider = null;\n}\n\n/** Reset the singleton provider. Intended for tests. */\nexport function resetPoolProvider(): void {\n provider = null;\n}\n\n/** Return the active provider, or create it. */\nexport function getProvider(): DatabaseProvider {\n if (!provider) {\n provider = createProvider();\n }\n return provider;\n}\n\ninterface PoolGauges {\n total: Gauge;\n idle: Gauge;\n waiting: Gauge;\n active: Gauge;\n}\n\nfunction ensurePoolGauges(collector: MetricsCollector): PoolGauges {\n const reg = collector.registry;\n const existingTotal = reg.get('db_pool_connections_total');\n if (existingTotal) {\n return {\n total: existingTotal as Gauge,\n idle: reg.get('db_pool_connections_idle') as Gauge,\n waiting: reg.get('db_pool_connections_waiting') as Gauge,\n active: reg.get('db_pool_connections_active') as Gauge,\n };\n }\n\n return {\n total: collector.gauge('db_pool_connections_total', 'Total connections in the pool'),\n idle: collector.gauge('db_pool_connections_idle', 'Idle connections in the pool'),\n waiting: collector.gauge('db_pool_connections_waiting', 'Waiting clients in the pool'),\n active: collector.gauge(\n 'db_pool_connections_active',\n 'Active (non-idle) connections in the pool',\n ),\n };\n}\n\n/** Update pool gauge values from the current pool state. */\nexport function updatePoolMetrics(collector: MetricsCollector, poolInstance?: Pool): void {\n const p = poolInstance ?? getPool();\n const total = (p as unknown as { totalCount: number }).totalCount ?? 0;\n const idle = p.idleCount ?? 0;\n const waiting = p.waitingCount ?? 0;\n\n const gauges = ensurePoolGauges(collector);\n gauges.total.set({}, total);\n gauges.idle.set({}, idle);\n gauges.waiting.set({}, waiting);\n gauges.active.set({}, Math.max(0, total - idle));\n}\n\n/**\n * Register pool metrics with the given collector.\n *\n * Returns a cleanup function that stops the update interval.\n */\nexport function registerPoolMetrics(\n collector: MetricsCollector,\n poolInstance?: Pool,\n intervalMs = 5000,\n): () => void {\n updatePoolMetrics(collector, poolInstance);\n\n const interval = setInterval(() => {\n updatePoolMetrics(collector, poolInstance);\n }, intervalMs);\n\n return () => clearInterval(interval);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/providers/neon.ts","../src/providers/lakebase.ts","../src/index.ts"],"names":["Pool","EventEmitter"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\nexport interface NeonProviderOptions {\n connectionString: string;\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n}\n\n/** Standard Postgres provider using a static connection string. */\nexport class NeonDatabaseProvider implements DatabaseProvider {\n readonly kind = 'neon' as const;\n private readonly pool: Pool;\n\n constructor(options: NeonProviderOptions) {\n this.pool = new Pool({\n connectionString: options.connectionString,\n max: options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5_000,\n });\n }\n\n getPool(): Pool {\n return this.pool;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\n/** A resolver that returns a valid Databricks workspace OAuth token. */\nexport type DatabricksTokenProvider = (() => Promise<string>) & {\n /** Bypass the cache and obtain a new token when the downstream credential is rotated. */\n refresh?: () => Promise<string>;\n};\n\n/** Supported Databricks authentication shapes. */\nexport type DatabricksPrincipal =\n | { kind: 'pat'; token: string }\n | {\n kind: 'service-principal';\n host: string;\n clientId: string;\n clientSecret: string;\n scope?: string;\n refreshSkewSeconds?: number;\n };\n\n/** Resolve a PAT or OAuth M2M principal to a cached, auto-refreshing token provider. */\nexport function databricksIdentity(principal: DatabricksPrincipal): DatabricksTokenProvider {\n if (principal.kind === 'pat') {\n const { token } = principal;\n return async () => token;\n }\n return servicePrincipalProvider(principal);\n}\n\n/** Build a principal from environment variables. */\nexport function databricksPrincipalFromEnv(\n env: Record<string, string | undefined> = typeof process !== 'undefined' ? process.env : {},\n): Extract<DatabricksPrincipal, { kind: 'pat' | 'service-principal' }> {\n const token = env.DATABRICKS_TOKEN?.trim();\n if (token) return { kind: 'pat', token };\n\n const clientId = env.DATABRICKS_CLIENT_ID?.trim();\n const clientSecret = env.DATABRICKS_CLIENT_SECRET?.trim();\n const host = env.DATABRICKS_HOST?.trim();\n if (clientId && clientSecret) {\n if (!host) throw new Error('Missing DATABRICKS_HOST for Databricks OAuth M2M.');\n return {\n kind: 'service-principal',\n host,\n clientId,\n clientSecret,\n scope: env.DATABRICKS_SCOPE,\n refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS\n ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10)\n : undefined,\n };\n }\n throw new Error(\n 'Missing Databricks credentials: DATABRICKS_TOKEN or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET.',\n );\n}\n\ninterface CachedToken {\n token: string;\n refreshAtMs: number;\n}\n\nfunction servicePrincipalProvider(\n principal: Extract<DatabricksPrincipal, { kind: 'service-principal' }>,\n): DatabricksTokenProvider {\n const host = principal.host.replace(/\\/$/, '');\n const scope = principal.scope ?? 'all-apis';\n const refreshSkewMs = (principal.refreshSkewSeconds ?? 60) * 1_000;\n let cached: CachedToken | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (): Promise<string> => {\n const response = await fetch(`${host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${Buffer.from(`${principal.clientId}:${principal.clientSecret}`).toString('base64')}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope }),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Databricks OAuth token exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as { access_token?: unknown; expires_in?: unknown };\n const token = typeof value.access_token === 'string' ? value.access_token : undefined;\n if (!token) throw new Error('Databricks OAuth token exchange returned no access_token.');\n const expiresInSeconds = typeof value.expires_in === 'number' ? value.expires_in : 3_600;\n const expiresAtMs = Date.now() + expiresInSeconds * 1_000;\n cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };\n return token;\n };\n\n const getToken: DatabricksTokenProvider = async () => {\n if (cached && Date.now() < cached.refreshAtMs) return cached.token;\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n\n getToken.refresh = async () => {\n cached = undefined;\n return getToken();\n };\n\n return getToken;\n}\n\nexport interface LakebaseCredentialProviderOptions {\n workspaceHost: string;\n endpoint: string;\n token: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n refreshBeforeMs?: number;\n refreshJitterMs?: number;\n now?: () => number;\n random?: () => number;\n}\n\ninterface LakebaseCredentialResponse {\n token?: unknown;\n expire_time?: unknown;\n}\n\nexport interface LakebaseCredential {\n token: string;\n expiresAt: number;\n}\n\nexport interface ExchangeLakebaseCredentialOptions {\n workspaceHost: string;\n endpoint: string;\n workspaceToken: string;\n fetchImpl?: typeof fetch;\n}\n\n/** Perform one uncached workspace-token to Lakebase-credential exchange. */\nexport async function exchangeLakebaseCredential(\n options: ExchangeLakebaseCredentialOptions,\n): Promise<LakebaseCredential> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const response = await fetchImpl(\n `${options.workspaceHost.replace(/\\/$/, '')}/api/2.0/postgres/credentials`,\n {\n method: 'POST',\n headers: {\n authorization: `Bearer ${options.workspaceToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ endpoint: options.endpoint }),\n },\n );\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as LakebaseCredentialResponse;\n if (typeof value.token !== 'string' || !value.token) {\n throw new Error('Lakebase credential exchange returned no token.');\n }\n const expiresAt = parseExpireTime(value.expire_time);\n if (!Number.isFinite(expiresAt)) {\n throw new Error('Lakebase credential exchange returned an invalid expire_time.');\n }\n return { token: value.token, expiresAt };\n}\n\nexport type LakebaseCredentialProvider = (() => Promise<string>) & {\n /** Force both the workspace OAuth token and database credential to rotate. */\n refresh: () => Promise<string>;\n};\n\n/** Exchange a workspace OAuth token for a Lakebase database credential. Caches, refreshes early, single-flight. */\nexport function lakebaseCredentialProvider(\n options: LakebaseCredentialProviderOptions,\n): LakebaseCredentialProvider {\n const now = options.now ?? Date.now;\n const random = options.random ?? Math.random;\n const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 60_000;\n const refreshJitterMs = options.refreshJitterMs ?? 30_000;\n let cached: { token: string; refreshAt: number } | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (forceWorkspaceToken = false): Promise<string> => {\n const workspaceToken =\n typeof options.token === 'function'\n ? await (forceWorkspaceToken && options.token.refresh\n ? options.token.refresh()\n : options.token())\n : options.token;\n const value = await exchangeLakebaseCredential({\n workspaceHost: options.workspaceHost,\n endpoint: options.endpoint,\n workspaceToken,\n fetchImpl: options.fetchImpl,\n });\n const jitter = Math.max(0, refreshJitterMs) * random();\n cached = {\n token: value.token,\n refreshAt: Math.max(now(), value.expiresAt - Math.max(0, refreshBeforeMs) - jitter),\n };\n return value.token;\n };\n\n const getCredential: LakebaseCredentialProvider = async () => {\n if (cached && now() < cached.refreshAt) return cached.token;\n refreshing ??= refresh(false).finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n\n getCredential.refresh = async () => {\n cached = undefined;\n refreshing ??= refresh(true).finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n\n return getCredential;\n}\n\nfunction parseExpireTime(value: unknown): number {\n if (typeof value === 'number') {\n return value < 1_000_000_000_000 ? value * 1_000 : value;\n }\n if (typeof value === 'string') return new Date(value).getTime();\n return Number.NaN;\n}\n\nexport interface LakebasePoolConfig {\n host: string;\n port: number;\n database: string;\n user: string;\n password?: string;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n /** Postgres `options` connection parameter, e.g. `-c search_path=fx,public`. */\n options?: string;\n}\n\nexport type LakebasePoolFactory = (config: LakebasePoolConfig) => Pool;\n\nexport interface LakebaseProviderOptions {\n /** Direct endpoint host from list-endpoints (NOT the pooled host). */\n host: string;\n port?: number;\n database: string;\n user: string;\n /** Static database password. If omitted, OAuth exchange is required. */\n password?: string;\n /** Workspace URL, e.g. https://adb-....azuredatabricks.net */\n workspaceHost?: string;\n /** Full endpoint resource name: projects/.../branches/.../endpoints/... */\n endpoint?: string;\n /** Workspace OAuth token or provider. */\n token?: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n /** How often to force-refresh the credential and recreate the pool. Default 45 minutes. */\n refreshIntervalMs?: number;\n /** Injected pool factory for tests. */\n poolFactory?: LakebasePoolFactory;\n /** Observe recoverable idle pooled-connection failures. */\n onPoolError?: (error: Error) => void;\n}\n\n/** A Pool-like facade that lazily initializes and always delegates to the current underlying pool. */\nclass DelegatingPool extends EventEmitter {\n private current: Pool | undefined;\n private initPromise: Promise<void> | undefined;\n private closed = false;\n\n private attachErrorHandler(pool: Pool) {\n pool.on('error', (error) => {\n this.emit('error', error);\n });\n }\n\n initialize(createPool: (password: string) => Pool, getPassword: () => Promise<string>) {\n this.initPromise = getPassword()\n .then((password) => {\n if (!this.closed) this.swap(createPool(password));\n })\n .catch((error) => {\n this.emit('error', error instanceof Error ? error : new Error(String(error)));\n throw error;\n });\n }\n\n swap(pool: Pool) {\n if (this.closed) {\n pool.end().catch(() => undefined);\n return;\n }\n const previous = this.current;\n this.current = pool;\n this.attachErrorHandler(pool);\n if (previous) {\n previous.removeAllListeners();\n previous.end().catch(() => undefined);\n }\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initPromise) await this.initPromise;\n }\n\n query(...args: Parameters<Pool['query']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.query(...args));\n }\n\n connect(...args: Parameters<Pool['connect']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.connect(...args));\n }\n\n end(): Promise<void> {\n this.closed = true;\n this.removeAllListeners();\n return this.current?.end() ?? Promise.resolve();\n }\n\n get totalCount() {\n return this.current?.totalCount ?? 0;\n }\n\n get idleCount() {\n return this.current?.idleCount ?? 0;\n }\n\n get waitingCount() {\n return this.current?.waitingCount ?? 0;\n }\n}\n\n/** Databricks Lakebase Postgres provider with credential refresh. */\nexport class LakebaseDatabaseProvider implements DatabaseProvider {\n readonly kind = 'databricks-lakebase' as const;\n private readonly options: LakebaseProviderOptions;\n private readonly poolFactory: LakebasePoolFactory;\n private readonly getPassword: () => Promise<string>;\n private readonly refreshPassword: () => Promise<string>;\n private readonly delegatingPool: DelegatingPool;\n private refreshTimer: NodeJS.Timeout | undefined;\n\n constructor(options: LakebaseProviderOptions) {\n this.options = options;\n this.poolFactory = options.poolFactory ?? defaultPoolFactory();\n this.delegatingPool = new DelegatingPool();\n if (options.password !== undefined) {\n this.getPassword = async () => options.password as string;\n this.refreshPassword = this.getPassword;\n this.delegatingPool.swap(this.createPool(options.password));\n } else {\n if (!options.token || !options.workspaceHost || !options.endpoint) {\n throw new Error(\n 'Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password.',\n );\n }\n const credential = lakebaseCredentialProvider({\n workspaceHost: options.workspaceHost,\n endpoint: options.endpoint,\n token: options.token,\n fetchImpl: options.fetchImpl,\n });\n this.getPassword = credential;\n this.refreshPassword = credential.refresh;\n this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);\n const intervalMs = options.refreshIntervalMs ?? 45 * 60_000;\n this.refreshTimer = setInterval(() => {\n void this.refreshCredentials().catch((error) => {\n this.delegatingPool.emit(\n 'error',\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n }, intervalMs);\n }\n }\n\n private createPool(password?: string): Pool {\n return this.poolFactory({\n host: this.options.host,\n port: this.options.port ?? 5432,\n database: this.options.database,\n user: this.options.user,\n password,\n ssl: this.options.ssl ?? { rejectUnauthorized: true },\n max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: this.options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5_000,\n options: '-c search_path=fx,public',\n });\n }\n\n /** Force a new database credential and atomically swap the active pool. */\n async refreshCredentials(): Promise<void> {\n const password = await this.refreshPassword();\n const pool = this.createPool(password);\n this.delegatingPool.swap(pool);\n }\n\n getPool(): Pool {\n return this.delegatingPool as unknown as Pool;\n }\n\n async close(): Promise<void> {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = undefined;\n }\n await this.delegatingPool.end();\n }\n}\n\nfunction defaultPoolFactory(): LakebasePoolFactory {\n return (config) => new Pool(config);\n}\n","import type { Gauge, MetricsCollector } from '@fabricorg/experiments-metrics';\nimport type { Pool } from 'pg';\nimport {\n type DatabaseProvider,\n LakebaseDatabaseProvider,\n NeonDatabaseProvider,\n databricksIdentity,\n databricksPrincipalFromEnv,\n} from './providers/index.js';\n\nexport {\n LakebaseDatabaseProvider,\n exchangeLakebaseCredential,\n lakebaseCredentialProvider,\n type ExchangeLakebaseCredentialOptions,\n type LakebaseCredential,\n type LakebasePoolFactory,\n type LakebaseProviderOptions,\n} from './providers/index.js';\n\nlet provider: DatabaseProvider | null = null;\n\nfunction createProvider(): DatabaseProvider {\n const kind = process.env.DATABASE_PROVIDER ?? 'neon';\n\n if (kind === 'databricks-lakebase') {\n const host = process.env.DATABRICKS_LAKEBASE_HOST?.trim();\n const database = process.env.DATABRICKS_LAKEBASE_DATABASE?.trim() ?? 'databricks_postgres';\n const user = process.env.DATABRICKS_LAKEBASE_USER?.trim();\n const workspaceHost = process.env.DATABRICKS_HOST?.trim();\n const endpoint = process.env.DATABRICKS_LAKEBASE_ENDPOINT?.trim();\n const password = process.env.DATABRICKS_LAKEBASE_PASSWORD?.trim();\n\n if (!host) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_HOST is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n if (!user) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_USER is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n\n if (password) {\n return new LakebaseDatabaseProvider({ host, database, user, password });\n }\n\n if (!workspaceHost || !endpoint) {\n throw new Error(\n 'DATABRICKS_HOST and DATABRICKS_LAKEBASE_ENDPOINT are required for Lakebase OAuth, or provide DATABRICKS_LAKEBASE_PASSWORD for native auth.',\n );\n }\n\n const principal = databricksPrincipalFromEnv(process.env);\n return new LakebaseDatabaseProvider({\n host,\n database,\n user,\n workspaceHost,\n endpoint,\n token: databricksIdentity(principal),\n });\n }\n\n const connectionString = process.env.DATABASE_URL;\n if (!connectionString) {\n throw new Error('DATABASE_URL is required');\n }\n return new NeonDatabaseProvider({ connectionString });\n}\n\n/**\n * Return a singleton `pg.Pool` configured for the active database provider.\n *\n * Provider selection:\n * - `DATABASE_PROVIDER=neon` (default): uses `DATABASE_URL`.\n * - `DATABASE_PROVIDER=databricks-lakebase`: uses Lakebase OAuth credentials.\n *\n * On the first call, a new Pool is created with the following defaults:\n * - `max`: parseInt(process.env.PG_POOL_MAX ?? '5') — caps concurrent connections.\n * - `idleTimeoutMillis`: 30000 — recycle idle connections before Vercel's 60s timeout.\n * - `connectionTimeoutMillis`: 5000 — fail fast when the database is unreachable.\n */\nexport function getPool(): Pool {\n if (!provider) {\n provider = createProvider();\n }\n return provider.getPool();\n}\n\n/** Close the active provider and any background refresh loops. */\nexport async function closePool(): Promise<void> {\n await provider?.close();\n provider = null;\n}\n\n/** Reset the singleton provider. Intended for tests. */\nexport function resetPoolProvider(): void {\n provider = null;\n}\n\n/** Return the active provider, or create it. */\nexport function getProvider(): DatabaseProvider {\n if (!provider) {\n provider = createProvider();\n }\n return provider;\n}\n\ninterface PoolGauges {\n total: Gauge;\n idle: Gauge;\n waiting: Gauge;\n active: Gauge;\n}\n\nfunction ensurePoolGauges(collector: MetricsCollector): PoolGauges {\n const reg = collector.registry;\n const existingTotal = reg.get('db_pool_connections_total');\n if (existingTotal) {\n return {\n total: existingTotal as Gauge,\n idle: reg.get('db_pool_connections_idle') as Gauge,\n waiting: reg.get('db_pool_connections_waiting') as Gauge,\n active: reg.get('db_pool_connections_active') as Gauge,\n };\n }\n\n return {\n total: collector.gauge('db_pool_connections_total', 'Total connections in the pool'),\n idle: collector.gauge('db_pool_connections_idle', 'Idle connections in the pool'),\n waiting: collector.gauge('db_pool_connections_waiting', 'Waiting clients in the pool'),\n active: collector.gauge(\n 'db_pool_connections_active',\n 'Active (non-idle) connections in the pool',\n ),\n };\n}\n\n/** Update pool gauge values from the current pool state. */\nexport function updatePoolMetrics(collector: MetricsCollector, poolInstance?: Pool): void {\n const p = poolInstance ?? getPool();\n const total = (p as unknown as { totalCount: number }).totalCount ?? 0;\n const idle = p.idleCount ?? 0;\n const waiting = p.waitingCount ?? 0;\n\n const gauges = ensurePoolGauges(collector);\n gauges.total.set({}, total);\n gauges.idle.set({}, idle);\n gauges.waiting.set({}, waiting);\n gauges.active.set({}, Math.max(0, total - idle));\n}\n\n/**\n * Register pool metrics with the given collector.\n *\n * Returns a cleanup function that stops the update interval.\n */\nexport function registerPoolMetrics(\n collector: MetricsCollector,\n poolInstance?: Pool,\n intervalMs = 5000,\n): () => void {\n updatePoolMetrics(collector, poolInstance);\n\n const interval = setInterval(() => {\n updatePoolMetrics(collector, poolInstance);\n }, intervalMs);\n\n return () => clearInterval(interval);\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -10,6 +10,100 @@ interface DatabaseProvider {
|
|
|
10
10
|
close(): Promise<void>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** A resolver that returns a valid Databricks workspace OAuth token. */
|
|
14
|
+
type DatabricksTokenProvider = (() => Promise<string>) & {
|
|
15
|
+
/** Bypass the cache and obtain a new token when the downstream credential is rotated. */
|
|
16
|
+
refresh?: () => Promise<string>;
|
|
17
|
+
};
|
|
18
|
+
interface LakebaseCredentialProviderOptions {
|
|
19
|
+
workspaceHost: string;
|
|
20
|
+
endpoint: string;
|
|
21
|
+
token: string | DatabricksTokenProvider;
|
|
22
|
+
fetchImpl?: typeof fetch;
|
|
23
|
+
refreshBeforeMs?: number;
|
|
24
|
+
refreshJitterMs?: number;
|
|
25
|
+
now?: () => number;
|
|
26
|
+
random?: () => number;
|
|
27
|
+
}
|
|
28
|
+
interface LakebaseCredential {
|
|
29
|
+
token: string;
|
|
30
|
+
expiresAt: number;
|
|
31
|
+
}
|
|
32
|
+
interface ExchangeLakebaseCredentialOptions {
|
|
33
|
+
workspaceHost: string;
|
|
34
|
+
endpoint: string;
|
|
35
|
+
workspaceToken: string;
|
|
36
|
+
fetchImpl?: typeof fetch;
|
|
37
|
+
}
|
|
38
|
+
/** Perform one uncached workspace-token to Lakebase-credential exchange. */
|
|
39
|
+
declare function exchangeLakebaseCredential(options: ExchangeLakebaseCredentialOptions): Promise<LakebaseCredential>;
|
|
40
|
+
type LakebaseCredentialProvider = (() => Promise<string>) & {
|
|
41
|
+
/** Force both the workspace OAuth token and database credential to rotate. */
|
|
42
|
+
refresh: () => Promise<string>;
|
|
43
|
+
};
|
|
44
|
+
/** Exchange a workspace OAuth token for a Lakebase database credential. Caches, refreshes early, single-flight. */
|
|
45
|
+
declare function lakebaseCredentialProvider(options: LakebaseCredentialProviderOptions): LakebaseCredentialProvider;
|
|
46
|
+
interface LakebasePoolConfig {
|
|
47
|
+
host: string;
|
|
48
|
+
port: number;
|
|
49
|
+
database: string;
|
|
50
|
+
user: string;
|
|
51
|
+
password?: string;
|
|
52
|
+
ssl?: boolean | {
|
|
53
|
+
rejectUnauthorized?: boolean;
|
|
54
|
+
};
|
|
55
|
+
max?: number;
|
|
56
|
+
idleTimeoutMillis?: number;
|
|
57
|
+
connectionTimeoutMillis?: number;
|
|
58
|
+
/** Postgres `options` connection parameter, e.g. `-c search_path=fx,public`. */
|
|
59
|
+
options?: string;
|
|
60
|
+
}
|
|
61
|
+
type LakebasePoolFactory = (config: LakebasePoolConfig) => Pool;
|
|
62
|
+
interface LakebaseProviderOptions {
|
|
63
|
+
/** Direct endpoint host from list-endpoints (NOT the pooled host). */
|
|
64
|
+
host: string;
|
|
65
|
+
port?: number;
|
|
66
|
+
database: string;
|
|
67
|
+
user: string;
|
|
68
|
+
/** Static database password. If omitted, OAuth exchange is required. */
|
|
69
|
+
password?: string;
|
|
70
|
+
/** Workspace URL, e.g. https://adb-....azuredatabricks.net */
|
|
71
|
+
workspaceHost?: string;
|
|
72
|
+
/** Full endpoint resource name: projects/.../branches/.../endpoints/... */
|
|
73
|
+
endpoint?: string;
|
|
74
|
+
/** Workspace OAuth token or provider. */
|
|
75
|
+
token?: string | DatabricksTokenProvider;
|
|
76
|
+
fetchImpl?: typeof fetch;
|
|
77
|
+
ssl?: boolean | {
|
|
78
|
+
rejectUnauthorized?: boolean;
|
|
79
|
+
};
|
|
80
|
+
max?: number;
|
|
81
|
+
idleTimeoutMillis?: number;
|
|
82
|
+
connectionTimeoutMillis?: number;
|
|
83
|
+
/** How often to force-refresh the credential and recreate the pool. Default 45 minutes. */
|
|
84
|
+
refreshIntervalMs?: number;
|
|
85
|
+
/** Injected pool factory for tests. */
|
|
86
|
+
poolFactory?: LakebasePoolFactory;
|
|
87
|
+
/** Observe recoverable idle pooled-connection failures. */
|
|
88
|
+
onPoolError?: (error: Error) => void;
|
|
89
|
+
}
|
|
90
|
+
/** Databricks Lakebase Postgres provider with credential refresh. */
|
|
91
|
+
declare class LakebaseDatabaseProvider implements DatabaseProvider {
|
|
92
|
+
readonly kind: "databricks-lakebase";
|
|
93
|
+
private readonly options;
|
|
94
|
+
private readonly poolFactory;
|
|
95
|
+
private readonly getPassword;
|
|
96
|
+
private readonly refreshPassword;
|
|
97
|
+
private readonly delegatingPool;
|
|
98
|
+
private refreshTimer;
|
|
99
|
+
constructor(options: LakebaseProviderOptions);
|
|
100
|
+
private createPool;
|
|
101
|
+
/** Force a new database credential and atomically swap the active pool. */
|
|
102
|
+
refreshCredentials(): Promise<void>;
|
|
103
|
+
getPool(): Pool;
|
|
104
|
+
close(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
13
107
|
/**
|
|
14
108
|
* Return a singleton `pg.Pool` configured for the active database provider.
|
|
15
109
|
*
|
|
@@ -38,4 +132,4 @@ declare function updatePoolMetrics(collector: MetricsCollector, poolInstance?: P
|
|
|
38
132
|
*/
|
|
39
133
|
declare function registerPoolMetrics(collector: MetricsCollector, poolInstance?: Pool, intervalMs?: number): () => void;
|
|
40
134
|
|
|
41
|
-
export { closePool, getPool, getProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
|
135
|
+
export { type ExchangeLakebaseCredentialOptions, type LakebaseCredential, LakebaseDatabaseProvider, type LakebasePoolFactory, type LakebaseProviderOptions, closePool, exchangeLakebaseCredential, getPool, getProvider, lakebaseCredentialProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,100 @@ interface DatabaseProvider {
|
|
|
10
10
|
close(): Promise<void>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** A resolver that returns a valid Databricks workspace OAuth token. */
|
|
14
|
+
type DatabricksTokenProvider = (() => Promise<string>) & {
|
|
15
|
+
/** Bypass the cache and obtain a new token when the downstream credential is rotated. */
|
|
16
|
+
refresh?: () => Promise<string>;
|
|
17
|
+
};
|
|
18
|
+
interface LakebaseCredentialProviderOptions {
|
|
19
|
+
workspaceHost: string;
|
|
20
|
+
endpoint: string;
|
|
21
|
+
token: string | DatabricksTokenProvider;
|
|
22
|
+
fetchImpl?: typeof fetch;
|
|
23
|
+
refreshBeforeMs?: number;
|
|
24
|
+
refreshJitterMs?: number;
|
|
25
|
+
now?: () => number;
|
|
26
|
+
random?: () => number;
|
|
27
|
+
}
|
|
28
|
+
interface LakebaseCredential {
|
|
29
|
+
token: string;
|
|
30
|
+
expiresAt: number;
|
|
31
|
+
}
|
|
32
|
+
interface ExchangeLakebaseCredentialOptions {
|
|
33
|
+
workspaceHost: string;
|
|
34
|
+
endpoint: string;
|
|
35
|
+
workspaceToken: string;
|
|
36
|
+
fetchImpl?: typeof fetch;
|
|
37
|
+
}
|
|
38
|
+
/** Perform one uncached workspace-token to Lakebase-credential exchange. */
|
|
39
|
+
declare function exchangeLakebaseCredential(options: ExchangeLakebaseCredentialOptions): Promise<LakebaseCredential>;
|
|
40
|
+
type LakebaseCredentialProvider = (() => Promise<string>) & {
|
|
41
|
+
/** Force both the workspace OAuth token and database credential to rotate. */
|
|
42
|
+
refresh: () => Promise<string>;
|
|
43
|
+
};
|
|
44
|
+
/** Exchange a workspace OAuth token for a Lakebase database credential. Caches, refreshes early, single-flight. */
|
|
45
|
+
declare function lakebaseCredentialProvider(options: LakebaseCredentialProviderOptions): LakebaseCredentialProvider;
|
|
46
|
+
interface LakebasePoolConfig {
|
|
47
|
+
host: string;
|
|
48
|
+
port: number;
|
|
49
|
+
database: string;
|
|
50
|
+
user: string;
|
|
51
|
+
password?: string;
|
|
52
|
+
ssl?: boolean | {
|
|
53
|
+
rejectUnauthorized?: boolean;
|
|
54
|
+
};
|
|
55
|
+
max?: number;
|
|
56
|
+
idleTimeoutMillis?: number;
|
|
57
|
+
connectionTimeoutMillis?: number;
|
|
58
|
+
/** Postgres `options` connection parameter, e.g. `-c search_path=fx,public`. */
|
|
59
|
+
options?: string;
|
|
60
|
+
}
|
|
61
|
+
type LakebasePoolFactory = (config: LakebasePoolConfig) => Pool;
|
|
62
|
+
interface LakebaseProviderOptions {
|
|
63
|
+
/** Direct endpoint host from list-endpoints (NOT the pooled host). */
|
|
64
|
+
host: string;
|
|
65
|
+
port?: number;
|
|
66
|
+
database: string;
|
|
67
|
+
user: string;
|
|
68
|
+
/** Static database password. If omitted, OAuth exchange is required. */
|
|
69
|
+
password?: string;
|
|
70
|
+
/** Workspace URL, e.g. https://adb-....azuredatabricks.net */
|
|
71
|
+
workspaceHost?: string;
|
|
72
|
+
/** Full endpoint resource name: projects/.../branches/.../endpoints/... */
|
|
73
|
+
endpoint?: string;
|
|
74
|
+
/** Workspace OAuth token or provider. */
|
|
75
|
+
token?: string | DatabricksTokenProvider;
|
|
76
|
+
fetchImpl?: typeof fetch;
|
|
77
|
+
ssl?: boolean | {
|
|
78
|
+
rejectUnauthorized?: boolean;
|
|
79
|
+
};
|
|
80
|
+
max?: number;
|
|
81
|
+
idleTimeoutMillis?: number;
|
|
82
|
+
connectionTimeoutMillis?: number;
|
|
83
|
+
/** How often to force-refresh the credential and recreate the pool. Default 45 minutes. */
|
|
84
|
+
refreshIntervalMs?: number;
|
|
85
|
+
/** Injected pool factory for tests. */
|
|
86
|
+
poolFactory?: LakebasePoolFactory;
|
|
87
|
+
/** Observe recoverable idle pooled-connection failures. */
|
|
88
|
+
onPoolError?: (error: Error) => void;
|
|
89
|
+
}
|
|
90
|
+
/** Databricks Lakebase Postgres provider with credential refresh. */
|
|
91
|
+
declare class LakebaseDatabaseProvider implements DatabaseProvider {
|
|
92
|
+
readonly kind: "databricks-lakebase";
|
|
93
|
+
private readonly options;
|
|
94
|
+
private readonly poolFactory;
|
|
95
|
+
private readonly getPassword;
|
|
96
|
+
private readonly refreshPassword;
|
|
97
|
+
private readonly delegatingPool;
|
|
98
|
+
private refreshTimer;
|
|
99
|
+
constructor(options: LakebaseProviderOptions);
|
|
100
|
+
private createPool;
|
|
101
|
+
/** Force a new database credential and atomically swap the active pool. */
|
|
102
|
+
refreshCredentials(): Promise<void>;
|
|
103
|
+
getPool(): Pool;
|
|
104
|
+
close(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
13
107
|
/**
|
|
14
108
|
* Return a singleton `pg.Pool` configured for the active database provider.
|
|
15
109
|
*
|
|
@@ -38,4 +132,4 @@ declare function updatePoolMetrics(collector: MetricsCollector, poolInstance?: P
|
|
|
38
132
|
*/
|
|
39
133
|
declare function registerPoolMetrics(collector: MetricsCollector, poolInstance?: Pool, intervalMs?: number): () => void;
|
|
40
134
|
|
|
41
|
-
export { closePool, getPool, getProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
|
135
|
+
export { type ExchangeLakebaseCredentialOptions, type LakebaseCredential, LakebaseDatabaseProvider, type LakebasePoolFactory, type LakebaseProviderOptions, closePool, exchangeLakebaseCredential, getPool, getProvider, lakebaseCredentialProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
package/dist/index.js
CHANGED
|
@@ -77,61 +77,85 @@ function servicePrincipalProvider(principal) {
|
|
|
77
77
|
cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };
|
|
78
78
|
return token;
|
|
79
79
|
};
|
|
80
|
-
|
|
80
|
+
const getToken = async () => {
|
|
81
81
|
if (cached && Date.now() < cached.refreshAtMs) return cached.token;
|
|
82
82
|
refreshing ??= refresh().finally(() => {
|
|
83
83
|
refreshing = void 0;
|
|
84
84
|
});
|
|
85
85
|
return refreshing;
|
|
86
86
|
};
|
|
87
|
+
getToken.refresh = async () => {
|
|
88
|
+
cached = void 0;
|
|
89
|
+
return getToken();
|
|
90
|
+
};
|
|
91
|
+
return getToken;
|
|
87
92
|
}
|
|
88
|
-
function
|
|
89
|
-
const workspaceHost = options.workspaceHost.replace(/\/$/, "");
|
|
93
|
+
async function exchangeLakebaseCredential(options) {
|
|
90
94
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
95
|
+
const response = await fetchImpl(
|
|
96
|
+
`${options.workspaceHost.replace(/\/$/, "")}/api/2.0/postgres/credentials`,
|
|
97
|
+
{
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: {
|
|
100
|
+
authorization: `Bearer ${options.workspaceToken}`,
|
|
101
|
+
"content-type": "application/json"
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify({ endpoint: options.endpoint })
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const text = await response.text();
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const value = await response.json();
|
|
113
|
+
if (typeof value.token !== "string" || !value.token) {
|
|
114
|
+
throw new Error("Lakebase credential exchange returned no token.");
|
|
115
|
+
}
|
|
116
|
+
const expiresAt = parseExpireTime(value.expire_time);
|
|
117
|
+
if (!Number.isFinite(expiresAt)) {
|
|
118
|
+
throw new Error("Lakebase credential exchange returned an invalid expire_time.");
|
|
119
|
+
}
|
|
120
|
+
return { token: value.token, expiresAt };
|
|
121
|
+
}
|
|
122
|
+
function lakebaseCredentialProvider(options) {
|
|
91
123
|
const now = options.now ?? Date.now;
|
|
92
124
|
const random = options.random ?? Math.random;
|
|
93
125
|
const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 6e4;
|
|
94
126
|
const refreshJitterMs = options.refreshJitterMs ?? 3e4;
|
|
95
127
|
let cached;
|
|
96
128
|
let refreshing;
|
|
97
|
-
const refresh = async () => {
|
|
98
|
-
const workspaceToken = typeof options.token === "function" ? await options.token() : options.token;
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
},
|
|
105
|
-
body: JSON.stringify({ endpoint: options.endpoint })
|
|
129
|
+
const refresh = async (forceWorkspaceToken = false) => {
|
|
130
|
+
const workspaceToken = typeof options.token === "function" ? await (forceWorkspaceToken && options.token.refresh ? options.token.refresh() : options.token()) : options.token;
|
|
131
|
+
const value = await exchangeLakebaseCredential({
|
|
132
|
+
workspaceHost: options.workspaceHost,
|
|
133
|
+
endpoint: options.endpoint,
|
|
134
|
+
workspaceToken,
|
|
135
|
+
fetchImpl: options.fetchImpl
|
|
106
136
|
});
|
|
107
|
-
if (!response.ok) {
|
|
108
|
-
const text = await response.text();
|
|
109
|
-
throw new Error(
|
|
110
|
-
`Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
const value = await response.json();
|
|
114
|
-
if (typeof value.token !== "string" || !value.token) {
|
|
115
|
-
throw new Error("Lakebase credential exchange returned no token.");
|
|
116
|
-
}
|
|
117
|
-
const expiresAt = parseExpireTime(value.expire_time);
|
|
118
|
-
if (!Number.isFinite(expiresAt)) {
|
|
119
|
-
throw new Error("Lakebase credential exchange returned an invalid expire_time.");
|
|
120
|
-
}
|
|
121
137
|
const jitter = Math.max(0, refreshJitterMs) * random();
|
|
122
138
|
cached = {
|
|
123
139
|
token: value.token,
|
|
124
|
-
refreshAt: Math.max(now(), expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
140
|
+
refreshAt: Math.max(now(), value.expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
125
141
|
};
|
|
126
142
|
return value.token;
|
|
127
143
|
};
|
|
128
|
-
|
|
144
|
+
const getCredential = async () => {
|
|
129
145
|
if (cached && now() < cached.refreshAt) return cached.token;
|
|
130
|
-
refreshing ??= refresh().finally(() => {
|
|
146
|
+
refreshing ??= refresh(false).finally(() => {
|
|
147
|
+
refreshing = void 0;
|
|
148
|
+
});
|
|
149
|
+
return refreshing;
|
|
150
|
+
};
|
|
151
|
+
getCredential.refresh = async () => {
|
|
152
|
+
cached = void 0;
|
|
153
|
+
refreshing ??= refresh(true).finally(() => {
|
|
131
154
|
refreshing = void 0;
|
|
132
155
|
});
|
|
133
156
|
return refreshing;
|
|
134
157
|
};
|
|
158
|
+
return getCredential;
|
|
135
159
|
}
|
|
136
160
|
function parseExpireTime(value) {
|
|
137
161
|
if (typeof value === "number") {
|
|
@@ -199,6 +223,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
199
223
|
options;
|
|
200
224
|
poolFactory;
|
|
201
225
|
getPassword;
|
|
226
|
+
refreshPassword;
|
|
202
227
|
delegatingPool;
|
|
203
228
|
refreshTimer;
|
|
204
229
|
constructor(options) {
|
|
@@ -207,6 +232,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
207
232
|
this.delegatingPool = new DelegatingPool();
|
|
208
233
|
if (options.password !== void 0) {
|
|
209
234
|
this.getPassword = async () => options.password;
|
|
235
|
+
this.refreshPassword = this.getPassword;
|
|
210
236
|
this.delegatingPool.swap(this.createPool(options.password));
|
|
211
237
|
} else {
|
|
212
238
|
if (!options.token || !options.workspaceHost || !options.endpoint) {
|
|
@@ -214,16 +240,23 @@ var LakebaseDatabaseProvider = class {
|
|
|
214
240
|
"Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password."
|
|
215
241
|
);
|
|
216
242
|
}
|
|
217
|
-
|
|
243
|
+
const credential = lakebaseCredentialProvider({
|
|
218
244
|
workspaceHost: options.workspaceHost,
|
|
219
245
|
endpoint: options.endpoint,
|
|
220
246
|
token: options.token,
|
|
221
247
|
fetchImpl: options.fetchImpl
|
|
222
248
|
});
|
|
249
|
+
this.getPassword = credential;
|
|
250
|
+
this.refreshPassword = credential.refresh;
|
|
223
251
|
this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);
|
|
224
|
-
const intervalMs = options.refreshIntervalMs ??
|
|
252
|
+
const intervalMs = options.refreshIntervalMs ?? 45 * 6e4;
|
|
225
253
|
this.refreshTimer = setInterval(() => {
|
|
226
|
-
void this.
|
|
254
|
+
void this.refreshCredentials().catch((error) => {
|
|
255
|
+
this.delegatingPool.emit(
|
|
256
|
+
"error",
|
|
257
|
+
error instanceof Error ? error : new Error(String(error))
|
|
258
|
+
);
|
|
259
|
+
});
|
|
227
260
|
}, intervalMs);
|
|
228
261
|
}
|
|
229
262
|
}
|
|
@@ -237,17 +270,15 @@ var LakebaseDatabaseProvider = class {
|
|
|
237
270
|
ssl: this.options.ssl ?? { rejectUnauthorized: true },
|
|
238
271
|
max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? "5", 10),
|
|
239
272
|
idleTimeoutMillis: this.options.idleTimeoutMillis ?? 3e4,
|
|
240
|
-
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3
|
|
273
|
+
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3,
|
|
274
|
+
options: "-c search_path=fx,public"
|
|
241
275
|
});
|
|
242
276
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
} catch (error) {
|
|
249
|
-
this.delegatingPool.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
250
|
-
}
|
|
277
|
+
/** Force a new database credential and atomically swap the active pool. */
|
|
278
|
+
async refreshCredentials() {
|
|
279
|
+
const password = await this.refreshPassword();
|
|
280
|
+
const pool = this.createPool(password);
|
|
281
|
+
this.delegatingPool.swap(pool);
|
|
251
282
|
}
|
|
252
283
|
getPool() {
|
|
253
284
|
return this.delegatingPool;
|
|
@@ -368,6 +399,6 @@ function registerPoolMetrics(collector, poolInstance, intervalMs = 5e3) {
|
|
|
368
399
|
return () => clearInterval(interval);
|
|
369
400
|
}
|
|
370
401
|
|
|
371
|
-
export { closePool, getPool, getProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
|
402
|
+
export { LakebaseDatabaseProvider, closePool, exchangeLakebaseCredential, getPool, getProvider, lakebaseCredentialProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
|
372
403
|
//# sourceMappingURL=index.js.map
|
|
373
404
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/neon.ts","../src/providers/lakebase.ts","../src/index.ts"],"names":["Pool"],"mappings":"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","file":"index.js","sourcesContent":["import { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\nexport interface NeonProviderOptions {\n connectionString: string;\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n}\n\n/** Standard Postgres provider using a static connection string. */\nexport class NeonDatabaseProvider implements DatabaseProvider {\n readonly kind = 'neon' as const;\n private readonly pool: Pool;\n\n constructor(options: NeonProviderOptions) {\n this.pool = new Pool({\n connectionString: options.connectionString,\n max: options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5_000,\n });\n }\n\n getPool(): Pool {\n return this.pool;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\n/** A resolver that returns a valid Databricks workspace OAuth token. */\nexport type DatabricksTokenProvider = () => Promise<string>;\n\n/** Supported Databricks authentication shapes. */\nexport type DatabricksPrincipal =\n | { kind: 'pat'; token: string }\n | {\n kind: 'service-principal';\n host: string;\n clientId: string;\n clientSecret: string;\n scope?: string;\n refreshSkewSeconds?: number;\n };\n\n/** Resolve a PAT or OAuth M2M principal to a cached, auto-refreshing token provider. */\nexport function databricksIdentity(principal: DatabricksPrincipal): DatabricksTokenProvider {\n if (principal.kind === 'pat') {\n const { token } = principal;\n return async () => token;\n }\n return servicePrincipalProvider(principal);\n}\n\n/** Build a principal from environment variables. */\nexport function databricksPrincipalFromEnv(\n env: Record<string, string | undefined> = typeof process !== 'undefined' ? process.env : {},\n): Extract<DatabricksPrincipal, { kind: 'pat' | 'service-principal' }> {\n const token = env.DATABRICKS_TOKEN?.trim();\n if (token) return { kind: 'pat', token };\n\n const clientId = env.DATABRICKS_CLIENT_ID?.trim();\n const clientSecret = env.DATABRICKS_CLIENT_SECRET?.trim();\n const host = env.DATABRICKS_HOST?.trim();\n if (clientId && clientSecret) {\n if (!host) throw new Error('Missing DATABRICKS_HOST for Databricks OAuth M2M.');\n return {\n kind: 'service-principal',\n host,\n clientId,\n clientSecret,\n scope: env.DATABRICKS_SCOPE,\n refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS\n ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10)\n : undefined,\n };\n }\n throw new Error(\n 'Missing Databricks credentials: DATABRICKS_TOKEN or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET.',\n );\n}\n\ninterface CachedToken {\n token: string;\n refreshAtMs: number;\n}\n\nfunction servicePrincipalProvider(\n principal: Extract<DatabricksPrincipal, { kind: 'service-principal' }>,\n): DatabricksTokenProvider {\n const host = principal.host.replace(/\\/$/, '');\n const scope = principal.scope ?? 'all-apis';\n const refreshSkewMs = (principal.refreshSkewSeconds ?? 60) * 1_000;\n let cached: CachedToken | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (): Promise<string> => {\n const response = await fetch(`${host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${Buffer.from(`${principal.clientId}:${principal.clientSecret}`).toString('base64')}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope }),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Databricks OAuth token exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as { access_token?: unknown; expires_in?: unknown };\n const token = typeof value.access_token === 'string' ? value.access_token : undefined;\n if (!token) throw new Error('Databricks OAuth token exchange returned no access_token.');\n const expiresInSeconds = typeof value.expires_in === 'number' ? value.expires_in : 3_600;\n const expiresAtMs = Date.now() + expiresInSeconds * 1_000;\n cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };\n return token;\n };\n\n return async () => {\n if (cached && Date.now() < cached.refreshAtMs) return cached.token;\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n}\n\nexport interface LakebaseCredentialProviderOptions {\n workspaceHost: string;\n endpoint: string;\n token: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n refreshBeforeMs?: number;\n refreshJitterMs?: number;\n now?: () => number;\n random?: () => number;\n}\n\ninterface LakebaseCredentialResponse {\n token?: unknown;\n expire_time?: unknown;\n}\n\n/** Exchange a workspace OAuth token for a Lakebase database credential. Caches, refreshes early, single-flight. */\nexport function lakebaseCredentialProvider(\n options: LakebaseCredentialProviderOptions,\n): () => Promise<string> {\n const workspaceHost = options.workspaceHost.replace(/\\/$/, '');\n const fetchImpl = options.fetchImpl ?? fetch;\n const now = options.now ?? Date.now;\n const random = options.random ?? Math.random;\n const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 60_000;\n const refreshJitterMs = options.refreshJitterMs ?? 30_000;\n let cached: { token: string; refreshAt: number } | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (): Promise<string> => {\n const workspaceToken =\n typeof options.token === 'function' ? await options.token() : options.token;\n const response = await fetchImpl(`${workspaceHost}/api/2.0/postgres/credentials`, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${workspaceToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ endpoint: options.endpoint }),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as LakebaseCredentialResponse;\n if (typeof value.token !== 'string' || !value.token) {\n throw new Error('Lakebase credential exchange returned no token.');\n }\n const expiresAt = parseExpireTime(value.expire_time);\n if (!Number.isFinite(expiresAt)) {\n throw new Error('Lakebase credential exchange returned an invalid expire_time.');\n }\n const jitter = Math.max(0, refreshJitterMs) * random();\n cached = {\n token: value.token,\n refreshAt: Math.max(now(), expiresAt - Math.max(0, refreshBeforeMs) - jitter),\n };\n return value.token;\n };\n\n return async () => {\n if (cached && now() < cached.refreshAt) return cached.token;\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n}\n\nfunction parseExpireTime(value: unknown): number {\n if (typeof value === 'number') {\n return value < 1_000_000_000_000 ? value * 1_000 : value;\n }\n if (typeof value === 'string') return new Date(value).getTime();\n return Number.NaN;\n}\n\nexport interface LakebasePoolConfig {\n host: string;\n port: number;\n database: string;\n user: string;\n password?: string;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n}\n\nexport type LakebasePoolFactory = (config: LakebasePoolConfig) => Pool;\n\nexport interface LakebaseProviderOptions {\n /** Direct endpoint host from list-endpoints (NOT the pooled host). */\n host: string;\n port?: number;\n database: string;\n user: string;\n /** Static database password. If omitted, OAuth exchange is required. */\n password?: string;\n /** Workspace URL, e.g. https://adb-....azuredatabricks.net */\n workspaceHost?: string;\n /** Full endpoint resource name: projects/.../branches/.../endpoints/... */\n endpoint?: string;\n /** Workspace OAuth token or provider. */\n token?: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n /** How often to refresh the credential and recreate the pool. Default 50 minutes. */\n refreshIntervalMs?: number;\n /** Injected pool factory for tests. */\n poolFactory?: LakebasePoolFactory;\n /** Observe recoverable idle pooled-connection failures. */\n onPoolError?: (error: Error) => void;\n}\n\n/** A Pool-like facade that lazily initializes and always delegates to the current underlying pool. */\nclass DelegatingPool extends EventEmitter {\n private current: Pool | undefined;\n private initPromise: Promise<void> | undefined;\n private closed = false;\n\n private attachErrorHandler(pool: Pool) {\n pool.on('error', (error) => {\n this.emit('error', error);\n });\n }\n\n initialize(createPool: (password: string) => Pool, getPassword: () => Promise<string>) {\n this.initPromise = getPassword()\n .then((password) => {\n if (!this.closed) this.swap(createPool(password));\n })\n .catch((error) => {\n this.emit('error', error instanceof Error ? error : new Error(String(error)));\n throw error;\n });\n }\n\n swap(pool: Pool) {\n if (this.closed) {\n pool.end().catch(() => undefined);\n return;\n }\n const previous = this.current;\n this.current = pool;\n this.attachErrorHandler(pool);\n if (previous) {\n previous.removeAllListeners();\n previous.end().catch(() => undefined);\n }\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initPromise) await this.initPromise;\n }\n\n query(...args: Parameters<Pool['query']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.query(...args));\n }\n\n connect(...args: Parameters<Pool['connect']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.connect(...args));\n }\n\n end(): Promise<void> {\n this.closed = true;\n this.removeAllListeners();\n return this.current?.end() ?? Promise.resolve();\n }\n\n get totalCount() {\n return this.current?.totalCount ?? 0;\n }\n\n get idleCount() {\n return this.current?.idleCount ?? 0;\n }\n\n get waitingCount() {\n return this.current?.waitingCount ?? 0;\n }\n}\n\n/** Databricks Lakebase Postgres provider with credential refresh. */\nexport class LakebaseDatabaseProvider implements DatabaseProvider {\n readonly kind = 'databricks-lakebase' as const;\n private readonly options: LakebaseProviderOptions;\n private readonly poolFactory: LakebasePoolFactory;\n private readonly getPassword: () => Promise<string>;\n private readonly delegatingPool: DelegatingPool;\n private refreshTimer: NodeJS.Timeout | undefined;\n\n constructor(options: LakebaseProviderOptions) {\n this.options = options;\n this.poolFactory = options.poolFactory ?? defaultPoolFactory();\n this.delegatingPool = new DelegatingPool();\n if (options.password !== undefined) {\n this.getPassword = async () => options.password as string;\n this.delegatingPool.swap(this.createPool(options.password));\n } else {\n if (!options.token || !options.workspaceHost || !options.endpoint) {\n throw new Error(\n 'Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password.',\n );\n }\n this.getPassword = lakebaseCredentialProvider({\n workspaceHost: options.workspaceHost,\n endpoint: options.endpoint,\n token: options.token,\n fetchImpl: options.fetchImpl,\n });\n this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);\n const intervalMs = options.refreshIntervalMs ?? 50 * 60_000;\n this.refreshTimer = setInterval(() => {\n void this.refreshPool();\n }, intervalMs);\n }\n }\n\n private createPool(password?: string): Pool {\n return this.poolFactory({\n host: this.options.host,\n port: this.options.port ?? 5432,\n database: this.options.database,\n user: this.options.user,\n password,\n ssl: this.options.ssl ?? { rejectUnauthorized: true },\n max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: this.options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5_000,\n });\n }\n\n private async refreshPool(): Promise<void> {\n try {\n const password = await this.getPassword();\n const pool = this.createPool(password);\n this.delegatingPool.swap(pool);\n } catch (error) {\n this.delegatingPool.emit('error', error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n getPool(): Pool {\n return this.delegatingPool as unknown as Pool;\n }\n\n async close(): Promise<void> {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = undefined;\n }\n await this.delegatingPool.end();\n }\n}\n\nfunction defaultPoolFactory(): LakebasePoolFactory {\n return (config) => new Pool(config);\n}\n","import type { Gauge, MetricsCollector } from '@fabricorg/experiments-metrics';\nimport type { Pool } from 'pg';\nimport {\n type DatabaseProvider,\n LakebaseDatabaseProvider,\n NeonDatabaseProvider,\n databricksIdentity,\n databricksPrincipalFromEnv,\n} from './providers/index.js';\n\nlet provider: DatabaseProvider | null = null;\n\nfunction createProvider(): DatabaseProvider {\n const kind = process.env.DATABASE_PROVIDER ?? 'neon';\n\n if (kind === 'databricks-lakebase') {\n const host = process.env.DATABRICKS_LAKEBASE_HOST?.trim();\n const database = process.env.DATABRICKS_LAKEBASE_DATABASE?.trim() ?? 'databricks_postgres';\n const user = process.env.DATABRICKS_LAKEBASE_USER?.trim();\n const workspaceHost = process.env.DATABRICKS_HOST?.trim();\n const endpoint = process.env.DATABRICKS_LAKEBASE_ENDPOINT?.trim();\n const password = process.env.DATABRICKS_LAKEBASE_PASSWORD?.trim();\n\n if (!host) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_HOST is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n if (!user) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_USER is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n\n if (password) {\n return new LakebaseDatabaseProvider({ host, database, user, password });\n }\n\n if (!workspaceHost || !endpoint) {\n throw new Error(\n 'DATABRICKS_HOST and DATABRICKS_LAKEBASE_ENDPOINT are required for Lakebase OAuth, or provide DATABRICKS_LAKEBASE_PASSWORD for native auth.',\n );\n }\n\n const principal = databricksPrincipalFromEnv(process.env);\n return new LakebaseDatabaseProvider({\n host,\n database,\n user,\n workspaceHost,\n endpoint,\n token: databricksIdentity(principal),\n });\n }\n\n const connectionString = process.env.DATABASE_URL;\n if (!connectionString) {\n throw new Error('DATABASE_URL is required');\n }\n return new NeonDatabaseProvider({ connectionString });\n}\n\n/**\n * Return a singleton `pg.Pool` configured for the active database provider.\n *\n * Provider selection:\n * - `DATABASE_PROVIDER=neon` (default): uses `DATABASE_URL`.\n * - `DATABASE_PROVIDER=databricks-lakebase`: uses Lakebase OAuth credentials.\n *\n * On the first call, a new Pool is created with the following defaults:\n * - `max`: parseInt(process.env.PG_POOL_MAX ?? '5') — caps concurrent connections.\n * - `idleTimeoutMillis`: 30000 — recycle idle connections before Vercel's 60s timeout.\n * - `connectionTimeoutMillis`: 5000 — fail fast when the database is unreachable.\n */\nexport function getPool(): Pool {\n if (!provider) {\n provider = createProvider();\n }\n return provider.getPool();\n}\n\n/** Close the active provider and any background refresh loops. */\nexport async function closePool(): Promise<void> {\n await provider?.close();\n provider = null;\n}\n\n/** Reset the singleton provider. Intended for tests. */\nexport function resetPoolProvider(): void {\n provider = null;\n}\n\n/** Return the active provider, or create it. */\nexport function getProvider(): DatabaseProvider {\n if (!provider) {\n provider = createProvider();\n }\n return provider;\n}\n\ninterface PoolGauges {\n total: Gauge;\n idle: Gauge;\n waiting: Gauge;\n active: Gauge;\n}\n\nfunction ensurePoolGauges(collector: MetricsCollector): PoolGauges {\n const reg = collector.registry;\n const existingTotal = reg.get('db_pool_connections_total');\n if (existingTotal) {\n return {\n total: existingTotal as Gauge,\n idle: reg.get('db_pool_connections_idle') as Gauge,\n waiting: reg.get('db_pool_connections_waiting') as Gauge,\n active: reg.get('db_pool_connections_active') as Gauge,\n };\n }\n\n return {\n total: collector.gauge('db_pool_connections_total', 'Total connections in the pool'),\n idle: collector.gauge('db_pool_connections_idle', 'Idle connections in the pool'),\n waiting: collector.gauge('db_pool_connections_waiting', 'Waiting clients in the pool'),\n active: collector.gauge(\n 'db_pool_connections_active',\n 'Active (non-idle) connections in the pool',\n ),\n };\n}\n\n/** Update pool gauge values from the current pool state. */\nexport function updatePoolMetrics(collector: MetricsCollector, poolInstance?: Pool): void {\n const p = poolInstance ?? getPool();\n const total = (p as unknown as { totalCount: number }).totalCount ?? 0;\n const idle = p.idleCount ?? 0;\n const waiting = p.waitingCount ?? 0;\n\n const gauges = ensurePoolGauges(collector);\n gauges.total.set({}, total);\n gauges.idle.set({}, idle);\n gauges.waiting.set({}, waiting);\n gauges.active.set({}, Math.max(0, total - idle));\n}\n\n/**\n * Register pool metrics with the given collector.\n *\n * Returns a cleanup function that stops the update interval.\n */\nexport function registerPoolMetrics(\n collector: MetricsCollector,\n poolInstance?: Pool,\n intervalMs = 5000,\n): () => void {\n updatePoolMetrics(collector, poolInstance);\n\n const interval = setInterval(() => {\n updatePoolMetrics(collector, poolInstance);\n }, intervalMs);\n\n return () => clearInterval(interval);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/providers/neon.ts","../src/providers/lakebase.ts","../src/index.ts"],"names":["Pool"],"mappings":"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","file":"index.js","sourcesContent":["import { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\nexport interface NeonProviderOptions {\n connectionString: string;\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n}\n\n/** Standard Postgres provider using a static connection string. */\nexport class NeonDatabaseProvider implements DatabaseProvider {\n readonly kind = 'neon' as const;\n private readonly pool: Pool;\n\n constructor(options: NeonProviderOptions) {\n this.pool = new Pool({\n connectionString: options.connectionString,\n max: options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5_000,\n });\n }\n\n getPool(): Pool {\n return this.pool;\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { Pool } from 'pg';\nimport type { DatabaseProvider } from './types.js';\n\n/** A resolver that returns a valid Databricks workspace OAuth token. */\nexport type DatabricksTokenProvider = (() => Promise<string>) & {\n /** Bypass the cache and obtain a new token when the downstream credential is rotated. */\n refresh?: () => Promise<string>;\n};\n\n/** Supported Databricks authentication shapes. */\nexport type DatabricksPrincipal =\n | { kind: 'pat'; token: string }\n | {\n kind: 'service-principal';\n host: string;\n clientId: string;\n clientSecret: string;\n scope?: string;\n refreshSkewSeconds?: number;\n };\n\n/** Resolve a PAT or OAuth M2M principal to a cached, auto-refreshing token provider. */\nexport function databricksIdentity(principal: DatabricksPrincipal): DatabricksTokenProvider {\n if (principal.kind === 'pat') {\n const { token } = principal;\n return async () => token;\n }\n return servicePrincipalProvider(principal);\n}\n\n/** Build a principal from environment variables. */\nexport function databricksPrincipalFromEnv(\n env: Record<string, string | undefined> = typeof process !== 'undefined' ? process.env : {},\n): Extract<DatabricksPrincipal, { kind: 'pat' | 'service-principal' }> {\n const token = env.DATABRICKS_TOKEN?.trim();\n if (token) return { kind: 'pat', token };\n\n const clientId = env.DATABRICKS_CLIENT_ID?.trim();\n const clientSecret = env.DATABRICKS_CLIENT_SECRET?.trim();\n const host = env.DATABRICKS_HOST?.trim();\n if (clientId && clientSecret) {\n if (!host) throw new Error('Missing DATABRICKS_HOST for Databricks OAuth M2M.');\n return {\n kind: 'service-principal',\n host,\n clientId,\n clientSecret,\n scope: env.DATABRICKS_SCOPE,\n refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS\n ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10)\n : undefined,\n };\n }\n throw new Error(\n 'Missing Databricks credentials: DATABRICKS_TOKEN or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET.',\n );\n}\n\ninterface CachedToken {\n token: string;\n refreshAtMs: number;\n}\n\nfunction servicePrincipalProvider(\n principal: Extract<DatabricksPrincipal, { kind: 'service-principal' }>,\n): DatabricksTokenProvider {\n const host = principal.host.replace(/\\/$/, '');\n const scope = principal.scope ?? 'all-apis';\n const refreshSkewMs = (principal.refreshSkewSeconds ?? 60) * 1_000;\n let cached: CachedToken | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (): Promise<string> => {\n const response = await fetch(`${host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${Buffer.from(`${principal.clientId}:${principal.clientSecret}`).toString('base64')}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope }),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Databricks OAuth token exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as { access_token?: unknown; expires_in?: unknown };\n const token = typeof value.access_token === 'string' ? value.access_token : undefined;\n if (!token) throw new Error('Databricks OAuth token exchange returned no access_token.');\n const expiresInSeconds = typeof value.expires_in === 'number' ? value.expires_in : 3_600;\n const expiresAtMs = Date.now() + expiresInSeconds * 1_000;\n cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };\n return token;\n };\n\n const getToken: DatabricksTokenProvider = async () => {\n if (cached && Date.now() < cached.refreshAtMs) return cached.token;\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n\n getToken.refresh = async () => {\n cached = undefined;\n return getToken();\n };\n\n return getToken;\n}\n\nexport interface LakebaseCredentialProviderOptions {\n workspaceHost: string;\n endpoint: string;\n token: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n refreshBeforeMs?: number;\n refreshJitterMs?: number;\n now?: () => number;\n random?: () => number;\n}\n\ninterface LakebaseCredentialResponse {\n token?: unknown;\n expire_time?: unknown;\n}\n\nexport interface LakebaseCredential {\n token: string;\n expiresAt: number;\n}\n\nexport interface ExchangeLakebaseCredentialOptions {\n workspaceHost: string;\n endpoint: string;\n workspaceToken: string;\n fetchImpl?: typeof fetch;\n}\n\n/** Perform one uncached workspace-token to Lakebase-credential exchange. */\nexport async function exchangeLakebaseCredential(\n options: ExchangeLakebaseCredentialOptions,\n): Promise<LakebaseCredential> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const response = await fetchImpl(\n `${options.workspaceHost.replace(/\\/$/, '')}/api/2.0/postgres/credentials`,\n {\n method: 'POST',\n headers: {\n authorization: `Bearer ${options.workspaceToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ endpoint: options.endpoint }),\n },\n );\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`,\n );\n }\n const value = (await response.json()) as LakebaseCredentialResponse;\n if (typeof value.token !== 'string' || !value.token) {\n throw new Error('Lakebase credential exchange returned no token.');\n }\n const expiresAt = parseExpireTime(value.expire_time);\n if (!Number.isFinite(expiresAt)) {\n throw new Error('Lakebase credential exchange returned an invalid expire_time.');\n }\n return { token: value.token, expiresAt };\n}\n\nexport type LakebaseCredentialProvider = (() => Promise<string>) & {\n /** Force both the workspace OAuth token and database credential to rotate. */\n refresh: () => Promise<string>;\n};\n\n/** Exchange a workspace OAuth token for a Lakebase database credential. Caches, refreshes early, single-flight. */\nexport function lakebaseCredentialProvider(\n options: LakebaseCredentialProviderOptions,\n): LakebaseCredentialProvider {\n const now = options.now ?? Date.now;\n const random = options.random ?? Math.random;\n const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 60_000;\n const refreshJitterMs = options.refreshJitterMs ?? 30_000;\n let cached: { token: string; refreshAt: number } | undefined;\n let refreshing: Promise<string> | undefined;\n\n const refresh = async (forceWorkspaceToken = false): Promise<string> => {\n const workspaceToken =\n typeof options.token === 'function'\n ? await (forceWorkspaceToken && options.token.refresh\n ? options.token.refresh()\n : options.token())\n : options.token;\n const value = await exchangeLakebaseCredential({\n workspaceHost: options.workspaceHost,\n endpoint: options.endpoint,\n workspaceToken,\n fetchImpl: options.fetchImpl,\n });\n const jitter = Math.max(0, refreshJitterMs) * random();\n cached = {\n token: value.token,\n refreshAt: Math.max(now(), value.expiresAt - Math.max(0, refreshBeforeMs) - jitter),\n };\n return value.token;\n };\n\n const getCredential: LakebaseCredentialProvider = async () => {\n if (cached && now() < cached.refreshAt) return cached.token;\n refreshing ??= refresh(false).finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n\n getCredential.refresh = async () => {\n cached = undefined;\n refreshing ??= refresh(true).finally(() => {\n refreshing = undefined;\n });\n return refreshing;\n };\n\n return getCredential;\n}\n\nfunction parseExpireTime(value: unknown): number {\n if (typeof value === 'number') {\n return value < 1_000_000_000_000 ? value * 1_000 : value;\n }\n if (typeof value === 'string') return new Date(value).getTime();\n return Number.NaN;\n}\n\nexport interface LakebasePoolConfig {\n host: string;\n port: number;\n database: string;\n user: string;\n password?: string;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n /** Postgres `options` connection parameter, e.g. `-c search_path=fx,public`. */\n options?: string;\n}\n\nexport type LakebasePoolFactory = (config: LakebasePoolConfig) => Pool;\n\nexport interface LakebaseProviderOptions {\n /** Direct endpoint host from list-endpoints (NOT the pooled host). */\n host: string;\n port?: number;\n database: string;\n user: string;\n /** Static database password. If omitted, OAuth exchange is required. */\n password?: string;\n /** Workspace URL, e.g. https://adb-....azuredatabricks.net */\n workspaceHost?: string;\n /** Full endpoint resource name: projects/.../branches/.../endpoints/... */\n endpoint?: string;\n /** Workspace OAuth token or provider. */\n token?: string | DatabricksTokenProvider;\n fetchImpl?: typeof fetch;\n ssl?: boolean | { rejectUnauthorized?: boolean };\n max?: number;\n idleTimeoutMillis?: number;\n connectionTimeoutMillis?: number;\n /** How often to force-refresh the credential and recreate the pool. Default 45 minutes. */\n refreshIntervalMs?: number;\n /** Injected pool factory for tests. */\n poolFactory?: LakebasePoolFactory;\n /** Observe recoverable idle pooled-connection failures. */\n onPoolError?: (error: Error) => void;\n}\n\n/** A Pool-like facade that lazily initializes and always delegates to the current underlying pool. */\nclass DelegatingPool extends EventEmitter {\n private current: Pool | undefined;\n private initPromise: Promise<void> | undefined;\n private closed = false;\n\n private attachErrorHandler(pool: Pool) {\n pool.on('error', (error) => {\n this.emit('error', error);\n });\n }\n\n initialize(createPool: (password: string) => Pool, getPassword: () => Promise<string>) {\n this.initPromise = getPassword()\n .then((password) => {\n if (!this.closed) this.swap(createPool(password));\n })\n .catch((error) => {\n this.emit('error', error instanceof Error ? error : new Error(String(error)));\n throw error;\n });\n }\n\n swap(pool: Pool) {\n if (this.closed) {\n pool.end().catch(() => undefined);\n return;\n }\n const previous = this.current;\n this.current = pool;\n this.attachErrorHandler(pool);\n if (previous) {\n previous.removeAllListeners();\n previous.end().catch(() => undefined);\n }\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initPromise) await this.initPromise;\n }\n\n query(...args: Parameters<Pool['query']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.query(...args));\n }\n\n connect(...args: Parameters<Pool['connect']>): Promise<unknown> {\n return this.ensureInitialized().then(() => this.current!.connect(...args));\n }\n\n end(): Promise<void> {\n this.closed = true;\n this.removeAllListeners();\n return this.current?.end() ?? Promise.resolve();\n }\n\n get totalCount() {\n return this.current?.totalCount ?? 0;\n }\n\n get idleCount() {\n return this.current?.idleCount ?? 0;\n }\n\n get waitingCount() {\n return this.current?.waitingCount ?? 0;\n }\n}\n\n/** Databricks Lakebase Postgres provider with credential refresh. */\nexport class LakebaseDatabaseProvider implements DatabaseProvider {\n readonly kind = 'databricks-lakebase' as const;\n private readonly options: LakebaseProviderOptions;\n private readonly poolFactory: LakebasePoolFactory;\n private readonly getPassword: () => Promise<string>;\n private readonly refreshPassword: () => Promise<string>;\n private readonly delegatingPool: DelegatingPool;\n private refreshTimer: NodeJS.Timeout | undefined;\n\n constructor(options: LakebaseProviderOptions) {\n this.options = options;\n this.poolFactory = options.poolFactory ?? defaultPoolFactory();\n this.delegatingPool = new DelegatingPool();\n if (options.password !== undefined) {\n this.getPassword = async () => options.password as string;\n this.refreshPassword = this.getPassword;\n this.delegatingPool.swap(this.createPool(options.password));\n } else {\n if (!options.token || !options.workspaceHost || !options.endpoint) {\n throw new Error(\n 'Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password.',\n );\n }\n const credential = lakebaseCredentialProvider({\n workspaceHost: options.workspaceHost,\n endpoint: options.endpoint,\n token: options.token,\n fetchImpl: options.fetchImpl,\n });\n this.getPassword = credential;\n this.refreshPassword = credential.refresh;\n this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);\n const intervalMs = options.refreshIntervalMs ?? 45 * 60_000;\n this.refreshTimer = setInterval(() => {\n void this.refreshCredentials().catch((error) => {\n this.delegatingPool.emit(\n 'error',\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n }, intervalMs);\n }\n }\n\n private createPool(password?: string): Pool {\n return this.poolFactory({\n host: this.options.host,\n port: this.options.port ?? 5432,\n database: this.options.database,\n user: this.options.user,\n password,\n ssl: this.options.ssl ?? { rejectUnauthorized: true },\n max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? '5', 10),\n idleTimeoutMillis: this.options.idleTimeoutMillis ?? 30_000,\n connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5_000,\n options: '-c search_path=fx,public',\n });\n }\n\n /** Force a new database credential and atomically swap the active pool. */\n async refreshCredentials(): Promise<void> {\n const password = await this.refreshPassword();\n const pool = this.createPool(password);\n this.delegatingPool.swap(pool);\n }\n\n getPool(): Pool {\n return this.delegatingPool as unknown as Pool;\n }\n\n async close(): Promise<void> {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = undefined;\n }\n await this.delegatingPool.end();\n }\n}\n\nfunction defaultPoolFactory(): LakebasePoolFactory {\n return (config) => new Pool(config);\n}\n","import type { Gauge, MetricsCollector } from '@fabricorg/experiments-metrics';\nimport type { Pool } from 'pg';\nimport {\n type DatabaseProvider,\n LakebaseDatabaseProvider,\n NeonDatabaseProvider,\n databricksIdentity,\n databricksPrincipalFromEnv,\n} from './providers/index.js';\n\nexport {\n LakebaseDatabaseProvider,\n exchangeLakebaseCredential,\n lakebaseCredentialProvider,\n type ExchangeLakebaseCredentialOptions,\n type LakebaseCredential,\n type LakebasePoolFactory,\n type LakebaseProviderOptions,\n} from './providers/index.js';\n\nlet provider: DatabaseProvider | null = null;\n\nfunction createProvider(): DatabaseProvider {\n const kind = process.env.DATABASE_PROVIDER ?? 'neon';\n\n if (kind === 'databricks-lakebase') {\n const host = process.env.DATABRICKS_LAKEBASE_HOST?.trim();\n const database = process.env.DATABRICKS_LAKEBASE_DATABASE?.trim() ?? 'databricks_postgres';\n const user = process.env.DATABRICKS_LAKEBASE_USER?.trim();\n const workspaceHost = process.env.DATABRICKS_HOST?.trim();\n const endpoint = process.env.DATABRICKS_LAKEBASE_ENDPOINT?.trim();\n const password = process.env.DATABRICKS_LAKEBASE_PASSWORD?.trim();\n\n if (!host) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_HOST is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n if (!user) {\n throw new Error(\n 'DATABRICKS_LAKEBASE_USER is required when DATABASE_PROVIDER=databricks-lakebase',\n );\n }\n\n if (password) {\n return new LakebaseDatabaseProvider({ host, database, user, password });\n }\n\n if (!workspaceHost || !endpoint) {\n throw new Error(\n 'DATABRICKS_HOST and DATABRICKS_LAKEBASE_ENDPOINT are required for Lakebase OAuth, or provide DATABRICKS_LAKEBASE_PASSWORD for native auth.',\n );\n }\n\n const principal = databricksPrincipalFromEnv(process.env);\n return new LakebaseDatabaseProvider({\n host,\n database,\n user,\n workspaceHost,\n endpoint,\n token: databricksIdentity(principal),\n });\n }\n\n const connectionString = process.env.DATABASE_URL;\n if (!connectionString) {\n throw new Error('DATABASE_URL is required');\n }\n return new NeonDatabaseProvider({ connectionString });\n}\n\n/**\n * Return a singleton `pg.Pool` configured for the active database provider.\n *\n * Provider selection:\n * - `DATABASE_PROVIDER=neon` (default): uses `DATABASE_URL`.\n * - `DATABASE_PROVIDER=databricks-lakebase`: uses Lakebase OAuth credentials.\n *\n * On the first call, a new Pool is created with the following defaults:\n * - `max`: parseInt(process.env.PG_POOL_MAX ?? '5') — caps concurrent connections.\n * - `idleTimeoutMillis`: 30000 — recycle idle connections before Vercel's 60s timeout.\n * - `connectionTimeoutMillis`: 5000 — fail fast when the database is unreachable.\n */\nexport function getPool(): Pool {\n if (!provider) {\n provider = createProvider();\n }\n return provider.getPool();\n}\n\n/** Close the active provider and any background refresh loops. */\nexport async function closePool(): Promise<void> {\n await provider?.close();\n provider = null;\n}\n\n/** Reset the singleton provider. Intended for tests. */\nexport function resetPoolProvider(): void {\n provider = null;\n}\n\n/** Return the active provider, or create it. */\nexport function getProvider(): DatabaseProvider {\n if (!provider) {\n provider = createProvider();\n }\n return provider;\n}\n\ninterface PoolGauges {\n total: Gauge;\n idle: Gauge;\n waiting: Gauge;\n active: Gauge;\n}\n\nfunction ensurePoolGauges(collector: MetricsCollector): PoolGauges {\n const reg = collector.registry;\n const existingTotal = reg.get('db_pool_connections_total');\n if (existingTotal) {\n return {\n total: existingTotal as Gauge,\n idle: reg.get('db_pool_connections_idle') as Gauge,\n waiting: reg.get('db_pool_connections_waiting') as Gauge,\n active: reg.get('db_pool_connections_active') as Gauge,\n };\n }\n\n return {\n total: collector.gauge('db_pool_connections_total', 'Total connections in the pool'),\n idle: collector.gauge('db_pool_connections_idle', 'Idle connections in the pool'),\n waiting: collector.gauge('db_pool_connections_waiting', 'Waiting clients in the pool'),\n active: collector.gauge(\n 'db_pool_connections_active',\n 'Active (non-idle) connections in the pool',\n ),\n };\n}\n\n/** Update pool gauge values from the current pool state. */\nexport function updatePoolMetrics(collector: MetricsCollector, poolInstance?: Pool): void {\n const p = poolInstance ?? getPool();\n const total = (p as unknown as { totalCount: number }).totalCount ?? 0;\n const idle = p.idleCount ?? 0;\n const waiting = p.waitingCount ?? 0;\n\n const gauges = ensurePoolGauges(collector);\n gauges.total.set({}, total);\n gauges.idle.set({}, idle);\n gauges.waiting.set({}, waiting);\n gauges.active.set({}, Math.max(0, total - idle));\n}\n\n/**\n * Register pool metrics with the given collector.\n *\n * Returns a cleanup function that stops the update interval.\n */\nexport function registerPoolMetrics(\n collector: MetricsCollector,\n poolInstance?: Pool,\n intervalMs = 5000,\n): () => void {\n updatePoolMetrics(collector, poolInstance);\n\n const interval = setInterval(() => {\n updatePoolMetrics(collector, poolInstance);\n }, intervalMs);\n\n return () => clearInterval(interval);\n}\n"]}
|