@fabricorg/experiments-db-pool 0.1.0 → 0.1.2
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 +158 -39
- 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 +156 -40
- package/dist/index.js.map +1 -1
- package/package.json +11 -13
- package/LICENSE +0 -21
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var pg = require('pg');
|
|
4
4
|
var events = require('events');
|
|
5
|
+
var promises = require('fs/promises');
|
|
5
6
|
|
|
6
7
|
// src/providers/neon.ts
|
|
7
8
|
var NeonDatabaseProvider = class {
|
|
@@ -11,8 +12,16 @@ var NeonDatabaseProvider = class {
|
|
|
11
12
|
this.pool = new pg.Pool({
|
|
12
13
|
connectionString: options.connectionString,
|
|
13
14
|
max: options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? "5", 10),
|
|
14
|
-
idleTimeoutMillis: options.idleTimeoutMillis ??
|
|
15
|
-
connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5e3
|
|
15
|
+
idleTimeoutMillis: options.idleTimeoutMillis ?? Number.parseInt(process.env.PG_POOL_IDLE_TIMEOUT_MS ?? "30000", 10),
|
|
16
|
+
connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5e3,
|
|
17
|
+
// Cloudflare request isolates cannot safely carry a TCP client owned by
|
|
18
|
+
// one request into the next. Set PG_POOL_MAX_USES=1 there so release()
|
|
19
|
+
// destroys the client synchronously; conventional Node runtimes retain
|
|
20
|
+
// pg-pool's effectively-unbounded default.
|
|
21
|
+
maxUses: Number.parseInt(process.env.PG_POOL_MAX_USES ?? "7500", 10),
|
|
22
|
+
// Lets an edge isolate release its final idle socket instead of keeping
|
|
23
|
+
// an I/O handle owned by a completed request alive indefinitely.
|
|
24
|
+
allowExitOnIdle: true
|
|
16
25
|
});
|
|
17
26
|
}
|
|
18
27
|
getPool() {
|
|
@@ -27,9 +36,37 @@ function databricksIdentity(principal) {
|
|
|
27
36
|
const { token } = principal;
|
|
28
37
|
return async () => token;
|
|
29
38
|
}
|
|
39
|
+
if (principal.kind === "federated") return federatedIdentityProvider(principal);
|
|
30
40
|
return servicePrincipalProvider(principal);
|
|
31
41
|
}
|
|
32
42
|
function databricksPrincipalFromEnv(env = typeof process !== "undefined" ? process.env : {}) {
|
|
43
|
+
const authType = env.DATABRICKS_AUTH_TYPE?.trim();
|
|
44
|
+
if (authType === "env-oidc" || authType === "file-oidc") {
|
|
45
|
+
const host2 = env.DATABRICKS_HOST?.trim();
|
|
46
|
+
if (!host2) throw new Error("Missing DATABRICKS_HOST for Databricks OAuth federation.");
|
|
47
|
+
return {
|
|
48
|
+
kind: "federated",
|
|
49
|
+
host: host2,
|
|
50
|
+
clientId: env.DATABRICKS_CLIENT_ID?.trim() || void 0,
|
|
51
|
+
scope: env.DATABRICKS_SCOPE,
|
|
52
|
+
refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10) : void 0,
|
|
53
|
+
subjectToken: async () => {
|
|
54
|
+
if (authType === "env-oidc") {
|
|
55
|
+
const tokenEnv = env.DATABRICKS_OIDC_TOKEN_ENV?.trim() || "DATABRICKS_OIDC_TOKEN";
|
|
56
|
+
const token3 = env[tokenEnv]?.trim();
|
|
57
|
+
if (!token3) throw new Error(`Databricks OIDC token environment ${tokenEnv} is empty.`);
|
|
58
|
+
return token3;
|
|
59
|
+
}
|
|
60
|
+
const path = env.DATABRICKS_OIDC_TOKEN_FILEPATH?.trim();
|
|
61
|
+
if (!path) {
|
|
62
|
+
throw new Error("Missing DATABRICKS_OIDC_TOKEN_FILEPATH for file-oidc federation.");
|
|
63
|
+
}
|
|
64
|
+
const token2 = (await promises.readFile(path, "utf8")).trim();
|
|
65
|
+
if (!token2) throw new Error(`Databricks OIDC token file ${path} is empty.`);
|
|
66
|
+
return token2;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
33
70
|
const token = env.DATABRICKS_TOKEN?.trim();
|
|
34
71
|
if (token) return { kind: "pat", token };
|
|
35
72
|
const clientId = env.DATABRICKS_CLIENT_ID?.trim();
|
|
@@ -47,7 +84,7 @@ function databricksPrincipalFromEnv(env = typeof process !== "undefined" ? proce
|
|
|
47
84
|
};
|
|
48
85
|
}
|
|
49
86
|
throw new Error(
|
|
50
|
-
"Missing Databricks credentials: DATABRICKS_TOKEN or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET."
|
|
87
|
+
"Missing Databricks credentials: env/file OIDC federation, DATABRICKS_TOKEN, or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET."
|
|
51
88
|
);
|
|
52
89
|
}
|
|
53
90
|
function servicePrincipalProvider(principal) {
|
|
@@ -79,61 +116,133 @@ function servicePrincipalProvider(principal) {
|
|
|
79
116
|
cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };
|
|
80
117
|
return token;
|
|
81
118
|
};
|
|
82
|
-
|
|
119
|
+
const getToken = async () => {
|
|
83
120
|
if (cached && Date.now() < cached.refreshAtMs) return cached.token;
|
|
84
121
|
refreshing ??= refresh().finally(() => {
|
|
85
122
|
refreshing = void 0;
|
|
86
123
|
});
|
|
87
124
|
return refreshing;
|
|
88
125
|
};
|
|
126
|
+
getToken.refresh = async () => {
|
|
127
|
+
cached = void 0;
|
|
128
|
+
return getToken();
|
|
129
|
+
};
|
|
130
|
+
return getToken;
|
|
89
131
|
}
|
|
90
|
-
function
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const now = options.now ?? Date.now;
|
|
94
|
-
const random = options.random ?? Math.random;
|
|
95
|
-
const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 6e4;
|
|
96
|
-
const refreshJitterMs = options.refreshJitterMs ?? 3e4;
|
|
132
|
+
function federatedIdentityProvider(principal) {
|
|
133
|
+
const host = principal.host.replace(/\/$/, "");
|
|
134
|
+
const refreshSkewMs = (principal.refreshSkewSeconds ?? 60) * 1e3;
|
|
97
135
|
let cached;
|
|
98
136
|
let refreshing;
|
|
99
137
|
const refresh = async () => {
|
|
100
|
-
const
|
|
101
|
-
|
|
138
|
+
const params = new URLSearchParams({
|
|
139
|
+
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
140
|
+
subject_token: await principal.subjectToken(),
|
|
141
|
+
subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
|
|
142
|
+
scope: principal.scope ?? "all-apis"
|
|
143
|
+
});
|
|
144
|
+
if (principal.clientId) params.set("client_id", principal.clientId);
|
|
145
|
+
const response = await fetch(`${host}/oidc/v1/token`, {
|
|
102
146
|
method: "POST",
|
|
103
|
-
headers: {
|
|
104
|
-
|
|
105
|
-
"content-type": "application/json"
|
|
106
|
-
},
|
|
107
|
-
body: JSON.stringify({ endpoint: options.endpoint })
|
|
147
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
148
|
+
body: params
|
|
108
149
|
});
|
|
109
150
|
if (!response.ok) {
|
|
110
151
|
const text = await response.text();
|
|
111
152
|
throw new Error(
|
|
112
|
-
`
|
|
153
|
+
`Databricks federated token exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
113
154
|
);
|
|
114
155
|
}
|
|
115
156
|
const value = await response.json();
|
|
116
|
-
if (typeof value.
|
|
117
|
-
throw new Error("
|
|
157
|
+
if (typeof value.access_token !== "string" || !value.access_token) {
|
|
158
|
+
throw new Error("Databricks federated token exchange returned no access_token.");
|
|
118
159
|
}
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
160
|
+
const expiresInSeconds = typeof value.expires_in === "number" ? value.expires_in : 3600;
|
|
161
|
+
cached = {
|
|
162
|
+
token: value.access_token,
|
|
163
|
+
refreshAtMs: Date.now() + expiresInSeconds * 1e3 - refreshSkewMs
|
|
164
|
+
};
|
|
165
|
+
return value.access_token;
|
|
166
|
+
};
|
|
167
|
+
const getToken = async () => {
|
|
168
|
+
if (cached && Date.now() < cached.refreshAtMs) return cached.token;
|
|
169
|
+
refreshing ??= refresh().finally(() => {
|
|
170
|
+
refreshing = void 0;
|
|
171
|
+
});
|
|
172
|
+
return refreshing;
|
|
173
|
+
};
|
|
174
|
+
getToken.refresh = async () => {
|
|
175
|
+
cached = void 0;
|
|
176
|
+
return getToken();
|
|
177
|
+
};
|
|
178
|
+
return getToken;
|
|
179
|
+
}
|
|
180
|
+
async function exchangeLakebaseCredential(options) {
|
|
181
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
182
|
+
const response = await fetchImpl(
|
|
183
|
+
`${options.workspaceHost.replace(/\/$/, "")}/api/2.0/postgres/credentials`,
|
|
184
|
+
{
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: {
|
|
187
|
+
authorization: `Bearer ${options.workspaceToken}`,
|
|
188
|
+
"content-type": "application/json"
|
|
189
|
+
},
|
|
190
|
+
body: JSON.stringify({ endpoint: options.endpoint })
|
|
122
191
|
}
|
|
192
|
+
);
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
const text = await response.text();
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const value = await response.json();
|
|
200
|
+
if (typeof value.token !== "string" || !value.token) {
|
|
201
|
+
throw new Error("Lakebase credential exchange returned no token.");
|
|
202
|
+
}
|
|
203
|
+
const expiresAt = parseExpireTime(value.expire_time);
|
|
204
|
+
if (!Number.isFinite(expiresAt)) {
|
|
205
|
+
throw new Error("Lakebase credential exchange returned an invalid expire_time.");
|
|
206
|
+
}
|
|
207
|
+
return { token: value.token, expiresAt };
|
|
208
|
+
}
|
|
209
|
+
function lakebaseCredentialProvider(options) {
|
|
210
|
+
const now = options.now ?? Date.now;
|
|
211
|
+
const random = options.random ?? Math.random;
|
|
212
|
+
const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 6e4;
|
|
213
|
+
const refreshJitterMs = options.refreshJitterMs ?? 3e4;
|
|
214
|
+
let cached;
|
|
215
|
+
let refreshing;
|
|
216
|
+
const refresh = async (forceWorkspaceToken = false) => {
|
|
217
|
+
const workspaceToken = typeof options.token === "function" ? await (forceWorkspaceToken && options.token.refresh ? options.token.refresh() : options.token()) : options.token;
|
|
218
|
+
const value = await exchangeLakebaseCredential({
|
|
219
|
+
workspaceHost: options.workspaceHost,
|
|
220
|
+
endpoint: options.endpoint,
|
|
221
|
+
workspaceToken,
|
|
222
|
+
fetchImpl: options.fetchImpl
|
|
223
|
+
});
|
|
123
224
|
const jitter = Math.max(0, refreshJitterMs) * random();
|
|
124
225
|
cached = {
|
|
125
226
|
token: value.token,
|
|
126
|
-
refreshAt: Math.max(now(), expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
227
|
+
refreshAt: Math.max(now(), value.expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
127
228
|
};
|
|
128
229
|
return value.token;
|
|
129
230
|
};
|
|
130
|
-
|
|
231
|
+
const getCredential = async () => {
|
|
131
232
|
if (cached && now() < cached.refreshAt) return cached.token;
|
|
132
|
-
refreshing ??= refresh().finally(() => {
|
|
233
|
+
refreshing ??= refresh(false).finally(() => {
|
|
133
234
|
refreshing = void 0;
|
|
134
235
|
});
|
|
135
236
|
return refreshing;
|
|
136
237
|
};
|
|
238
|
+
getCredential.refresh = async () => {
|
|
239
|
+
cached = void 0;
|
|
240
|
+
refreshing ??= refresh(true).finally(() => {
|
|
241
|
+
refreshing = void 0;
|
|
242
|
+
});
|
|
243
|
+
return refreshing;
|
|
244
|
+
};
|
|
245
|
+
return getCredential;
|
|
137
246
|
}
|
|
138
247
|
function parseExpireTime(value) {
|
|
139
248
|
if (typeof value === "number") {
|
|
@@ -201,6 +310,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
201
310
|
options;
|
|
202
311
|
poolFactory;
|
|
203
312
|
getPassword;
|
|
313
|
+
refreshPassword;
|
|
204
314
|
delegatingPool;
|
|
205
315
|
refreshTimer;
|
|
206
316
|
constructor(options) {
|
|
@@ -209,6 +319,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
209
319
|
this.delegatingPool = new DelegatingPool();
|
|
210
320
|
if (options.password !== void 0) {
|
|
211
321
|
this.getPassword = async () => options.password;
|
|
322
|
+
this.refreshPassword = this.getPassword;
|
|
212
323
|
this.delegatingPool.swap(this.createPool(options.password));
|
|
213
324
|
} else {
|
|
214
325
|
if (!options.token || !options.workspaceHost || !options.endpoint) {
|
|
@@ -216,16 +327,23 @@ var LakebaseDatabaseProvider = class {
|
|
|
216
327
|
"Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password."
|
|
217
328
|
);
|
|
218
329
|
}
|
|
219
|
-
|
|
330
|
+
const credential = lakebaseCredentialProvider({
|
|
220
331
|
workspaceHost: options.workspaceHost,
|
|
221
332
|
endpoint: options.endpoint,
|
|
222
333
|
token: options.token,
|
|
223
334
|
fetchImpl: options.fetchImpl
|
|
224
335
|
});
|
|
336
|
+
this.getPassword = credential;
|
|
337
|
+
this.refreshPassword = credential.refresh;
|
|
225
338
|
this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);
|
|
226
|
-
const intervalMs = options.refreshIntervalMs ??
|
|
339
|
+
const intervalMs = options.refreshIntervalMs ?? 45 * 6e4;
|
|
227
340
|
this.refreshTimer = setInterval(() => {
|
|
228
|
-
void this.
|
|
341
|
+
void this.refreshCredentials().catch((error) => {
|
|
342
|
+
this.delegatingPool.emit(
|
|
343
|
+
"error",
|
|
344
|
+
error instanceof Error ? error : new Error(String(error))
|
|
345
|
+
);
|
|
346
|
+
});
|
|
229
347
|
}, intervalMs);
|
|
230
348
|
}
|
|
231
349
|
}
|
|
@@ -239,17 +357,15 @@ var LakebaseDatabaseProvider = class {
|
|
|
239
357
|
ssl: this.options.ssl ?? { rejectUnauthorized: true },
|
|
240
358
|
max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? "5", 10),
|
|
241
359
|
idleTimeoutMillis: this.options.idleTimeoutMillis ?? 3e4,
|
|
242
|
-
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3
|
|
360
|
+
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3,
|
|
361
|
+
options: "-c search_path=fx,public"
|
|
243
362
|
});
|
|
244
363
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
} catch (error) {
|
|
251
|
-
this.delegatingPool.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
252
|
-
}
|
|
364
|
+
/** Force a new database credential and atomically swap the active pool. */
|
|
365
|
+
async refreshCredentials() {
|
|
366
|
+
const password = await this.refreshPassword();
|
|
367
|
+
const pool = this.createPool(password);
|
|
368
|
+
this.delegatingPool.swap(pool);
|
|
253
369
|
}
|
|
254
370
|
getPool() {
|
|
255
371
|
return this.delegatingPool;
|
|
@@ -370,9 +486,12 @@ function registerPoolMetrics(collector, poolInstance, intervalMs = 5e3) {
|
|
|
370
486
|
return () => clearInterval(interval);
|
|
371
487
|
}
|
|
372
488
|
|
|
489
|
+
exports.LakebaseDatabaseProvider = LakebaseDatabaseProvider;
|
|
373
490
|
exports.closePool = closePool;
|
|
491
|
+
exports.exchangeLakebaseCredential = exchangeLakebaseCredential;
|
|
374
492
|
exports.getPool = getPool;
|
|
375
493
|
exports.getProvider = getProvider;
|
|
494
|
+
exports.lakebaseCredentialProvider = lakebaseCredentialProvider;
|
|
376
495
|
exports.registerPoolMetrics = registerPoolMetrics;
|
|
377
496
|
exports.resetPoolProvider = resetPoolProvider;
|
|
378
497
|
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","host","token","readFile","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:\n options.idleTimeoutMillis ??\n Number.parseInt(process.env.PG_POOL_IDLE_TIMEOUT_MS ?? '30000', 10),\n connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5_000,\n // Cloudflare request isolates cannot safely carry a TCP client owned by\n // one request into the next. Set PG_POOL_MAX_USES=1 there so release()\n // destroys the client synchronously; conventional Node runtimes retain\n // pg-pool's effectively-unbounded default.\n maxUses: Number.parseInt(process.env.PG_POOL_MAX_USES ?? '7500', 10),\n // Lets an edge isolate release its final idle socket instead of keeping\n // an I/O handle owned by a completed request alive indefinitely.\n allowExitOnIdle: true,\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 { readFile } from 'node:fs/promises';\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 kind: 'federated';\n host: string;\n clientId?: string;\n subjectToken: () => Promise<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 if (principal.kind === 'federated') return federatedIdentityProvider(principal);\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): DatabricksPrincipal {\n const authType = env.DATABRICKS_AUTH_TYPE?.trim();\n if (authType === 'env-oidc' || authType === 'file-oidc') {\n const host = env.DATABRICKS_HOST?.trim();\n if (!host) throw new Error('Missing DATABRICKS_HOST for Databricks OAuth federation.');\n return {\n kind: 'federated',\n host,\n clientId: env.DATABRICKS_CLIENT_ID?.trim() || undefined,\n scope: env.DATABRICKS_SCOPE,\n refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS\n ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10)\n : undefined,\n subjectToken: async () => {\n if (authType === 'env-oidc') {\n const tokenEnv = env.DATABRICKS_OIDC_TOKEN_ENV?.trim() || 'DATABRICKS_OIDC_TOKEN';\n const token = env[tokenEnv]?.trim();\n if (!token) throw new Error(`Databricks OIDC token environment ${tokenEnv} is empty.`);\n return token;\n }\n const path = env.DATABRICKS_OIDC_TOKEN_FILEPATH?.trim();\n if (!path) {\n throw new Error('Missing DATABRICKS_OIDC_TOKEN_FILEPATH for file-oidc federation.');\n }\n const token = (await readFile(path, 'utf8')).trim();\n if (!token) throw new Error(`Databricks OIDC token file ${path} is empty.`);\n return token;\n },\n };\n }\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: env/file OIDC federation, 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\nfunction federatedIdentityProvider(\n principal: Extract<DatabricksPrincipal, { kind: 'federated' }>,\n): DatabricksTokenProvider {\n const host = principal.host.replace(/\\/$/, '');\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 params = new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',\n subject_token: await principal.subjectToken(),\n subject_token_type: 'urn:ietf:params:oauth:token-type:jwt',\n scope: principal.scope ?? 'all-apis',\n });\n if (principal.clientId) params.set('client_id', principal.clientId);\n const response = await fetch(`${host}/oidc/v1/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: params,\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Databricks federated 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 if (typeof value.access_token !== 'string' || !value.access_token) {\n throw new Error('Databricks federated token exchange returned no access_token.');\n }\n const expiresInSeconds = typeof value.expires_in === 'number' ? value.expires_in : 3_600;\n cached = {\n token: value.access_token,\n refreshAtMs: Date.now() + expiresInSeconds * 1_000 - refreshSkewMs,\n };\n return value.access_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 getToken.refresh = async () => {\n cached = undefined;\n return getToken();\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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
+
import { readFile } from 'fs/promises';
|
|
3
4
|
|
|
4
5
|
// src/providers/neon.ts
|
|
5
6
|
var NeonDatabaseProvider = class {
|
|
@@ -9,8 +10,16 @@ var NeonDatabaseProvider = class {
|
|
|
9
10
|
this.pool = new Pool({
|
|
10
11
|
connectionString: options.connectionString,
|
|
11
12
|
max: options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? "5", 10),
|
|
12
|
-
idleTimeoutMillis: options.idleTimeoutMillis ??
|
|
13
|
-
connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5e3
|
|
13
|
+
idleTimeoutMillis: options.idleTimeoutMillis ?? Number.parseInt(process.env.PG_POOL_IDLE_TIMEOUT_MS ?? "30000", 10),
|
|
14
|
+
connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5e3,
|
|
15
|
+
// Cloudflare request isolates cannot safely carry a TCP client owned by
|
|
16
|
+
// one request into the next. Set PG_POOL_MAX_USES=1 there so release()
|
|
17
|
+
// destroys the client synchronously; conventional Node runtimes retain
|
|
18
|
+
// pg-pool's effectively-unbounded default.
|
|
19
|
+
maxUses: Number.parseInt(process.env.PG_POOL_MAX_USES ?? "7500", 10),
|
|
20
|
+
// Lets an edge isolate release its final idle socket instead of keeping
|
|
21
|
+
// an I/O handle owned by a completed request alive indefinitely.
|
|
22
|
+
allowExitOnIdle: true
|
|
14
23
|
});
|
|
15
24
|
}
|
|
16
25
|
getPool() {
|
|
@@ -25,9 +34,37 @@ function databricksIdentity(principal) {
|
|
|
25
34
|
const { token } = principal;
|
|
26
35
|
return async () => token;
|
|
27
36
|
}
|
|
37
|
+
if (principal.kind === "federated") return federatedIdentityProvider(principal);
|
|
28
38
|
return servicePrincipalProvider(principal);
|
|
29
39
|
}
|
|
30
40
|
function databricksPrincipalFromEnv(env = typeof process !== "undefined" ? process.env : {}) {
|
|
41
|
+
const authType = env.DATABRICKS_AUTH_TYPE?.trim();
|
|
42
|
+
if (authType === "env-oidc" || authType === "file-oidc") {
|
|
43
|
+
const host2 = env.DATABRICKS_HOST?.trim();
|
|
44
|
+
if (!host2) throw new Error("Missing DATABRICKS_HOST for Databricks OAuth federation.");
|
|
45
|
+
return {
|
|
46
|
+
kind: "federated",
|
|
47
|
+
host: host2,
|
|
48
|
+
clientId: env.DATABRICKS_CLIENT_ID?.trim() || void 0,
|
|
49
|
+
scope: env.DATABRICKS_SCOPE,
|
|
50
|
+
refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10) : void 0,
|
|
51
|
+
subjectToken: async () => {
|
|
52
|
+
if (authType === "env-oidc") {
|
|
53
|
+
const tokenEnv = env.DATABRICKS_OIDC_TOKEN_ENV?.trim() || "DATABRICKS_OIDC_TOKEN";
|
|
54
|
+
const token3 = env[tokenEnv]?.trim();
|
|
55
|
+
if (!token3) throw new Error(`Databricks OIDC token environment ${tokenEnv} is empty.`);
|
|
56
|
+
return token3;
|
|
57
|
+
}
|
|
58
|
+
const path = env.DATABRICKS_OIDC_TOKEN_FILEPATH?.trim();
|
|
59
|
+
if (!path) {
|
|
60
|
+
throw new Error("Missing DATABRICKS_OIDC_TOKEN_FILEPATH for file-oidc federation.");
|
|
61
|
+
}
|
|
62
|
+
const token2 = (await readFile(path, "utf8")).trim();
|
|
63
|
+
if (!token2) throw new Error(`Databricks OIDC token file ${path} is empty.`);
|
|
64
|
+
return token2;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
31
68
|
const token = env.DATABRICKS_TOKEN?.trim();
|
|
32
69
|
if (token) return { kind: "pat", token };
|
|
33
70
|
const clientId = env.DATABRICKS_CLIENT_ID?.trim();
|
|
@@ -45,7 +82,7 @@ function databricksPrincipalFromEnv(env = typeof process !== "undefined" ? proce
|
|
|
45
82
|
};
|
|
46
83
|
}
|
|
47
84
|
throw new Error(
|
|
48
|
-
"Missing Databricks credentials: DATABRICKS_TOKEN or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET."
|
|
85
|
+
"Missing Databricks credentials: env/file OIDC federation, DATABRICKS_TOKEN, or DATABRICKS_CLIENT_ID+DATABRICKS_CLIENT_SECRET."
|
|
49
86
|
);
|
|
50
87
|
}
|
|
51
88
|
function servicePrincipalProvider(principal) {
|
|
@@ -77,61 +114,133 @@ function servicePrincipalProvider(principal) {
|
|
|
77
114
|
cached = { token, refreshAtMs: expiresAtMs - refreshSkewMs };
|
|
78
115
|
return token;
|
|
79
116
|
};
|
|
80
|
-
|
|
117
|
+
const getToken = async () => {
|
|
81
118
|
if (cached && Date.now() < cached.refreshAtMs) return cached.token;
|
|
82
119
|
refreshing ??= refresh().finally(() => {
|
|
83
120
|
refreshing = void 0;
|
|
84
121
|
});
|
|
85
122
|
return refreshing;
|
|
86
123
|
};
|
|
124
|
+
getToken.refresh = async () => {
|
|
125
|
+
cached = void 0;
|
|
126
|
+
return getToken();
|
|
127
|
+
};
|
|
128
|
+
return getToken;
|
|
87
129
|
}
|
|
88
|
-
function
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const now = options.now ?? Date.now;
|
|
92
|
-
const random = options.random ?? Math.random;
|
|
93
|
-
const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 6e4;
|
|
94
|
-
const refreshJitterMs = options.refreshJitterMs ?? 3e4;
|
|
130
|
+
function federatedIdentityProvider(principal) {
|
|
131
|
+
const host = principal.host.replace(/\/$/, "");
|
|
132
|
+
const refreshSkewMs = (principal.refreshSkewSeconds ?? 60) * 1e3;
|
|
95
133
|
let cached;
|
|
96
134
|
let refreshing;
|
|
97
135
|
const refresh = async () => {
|
|
98
|
-
const
|
|
99
|
-
|
|
136
|
+
const params = new URLSearchParams({
|
|
137
|
+
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
138
|
+
subject_token: await principal.subjectToken(),
|
|
139
|
+
subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
|
|
140
|
+
scope: principal.scope ?? "all-apis"
|
|
141
|
+
});
|
|
142
|
+
if (principal.clientId) params.set("client_id", principal.clientId);
|
|
143
|
+
const response = await fetch(`${host}/oidc/v1/token`, {
|
|
100
144
|
method: "POST",
|
|
101
|
-
headers: {
|
|
102
|
-
|
|
103
|
-
"content-type": "application/json"
|
|
104
|
-
},
|
|
105
|
-
body: JSON.stringify({ endpoint: options.endpoint })
|
|
145
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
146
|
+
body: params
|
|
106
147
|
});
|
|
107
148
|
if (!response.ok) {
|
|
108
149
|
const text = await response.text();
|
|
109
150
|
throw new Error(
|
|
110
|
-
`
|
|
151
|
+
`Databricks federated token exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
111
152
|
);
|
|
112
153
|
}
|
|
113
154
|
const value = await response.json();
|
|
114
|
-
if (typeof value.
|
|
115
|
-
throw new Error("
|
|
155
|
+
if (typeof value.access_token !== "string" || !value.access_token) {
|
|
156
|
+
throw new Error("Databricks federated token exchange returned no access_token.");
|
|
116
157
|
}
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
158
|
+
const expiresInSeconds = typeof value.expires_in === "number" ? value.expires_in : 3600;
|
|
159
|
+
cached = {
|
|
160
|
+
token: value.access_token,
|
|
161
|
+
refreshAtMs: Date.now() + expiresInSeconds * 1e3 - refreshSkewMs
|
|
162
|
+
};
|
|
163
|
+
return value.access_token;
|
|
164
|
+
};
|
|
165
|
+
const getToken = async () => {
|
|
166
|
+
if (cached && Date.now() < cached.refreshAtMs) return cached.token;
|
|
167
|
+
refreshing ??= refresh().finally(() => {
|
|
168
|
+
refreshing = void 0;
|
|
169
|
+
});
|
|
170
|
+
return refreshing;
|
|
171
|
+
};
|
|
172
|
+
getToken.refresh = async () => {
|
|
173
|
+
cached = void 0;
|
|
174
|
+
return getToken();
|
|
175
|
+
};
|
|
176
|
+
return getToken;
|
|
177
|
+
}
|
|
178
|
+
async function exchangeLakebaseCredential(options) {
|
|
179
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
180
|
+
const response = await fetchImpl(
|
|
181
|
+
`${options.workspaceHost.replace(/\/$/, "")}/api/2.0/postgres/credentials`,
|
|
182
|
+
{
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: {
|
|
185
|
+
authorization: `Bearer ${options.workspaceToken}`,
|
|
186
|
+
"content-type": "application/json"
|
|
187
|
+
},
|
|
188
|
+
body: JSON.stringify({ endpoint: options.endpoint })
|
|
120
189
|
}
|
|
190
|
+
);
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
const text = await response.text();
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Lakebase credential exchange failed: ${response.status} ${response.statusText}: ${text.slice(0, 500)}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const value = await response.json();
|
|
198
|
+
if (typeof value.token !== "string" || !value.token) {
|
|
199
|
+
throw new Error("Lakebase credential exchange returned no token.");
|
|
200
|
+
}
|
|
201
|
+
const expiresAt = parseExpireTime(value.expire_time);
|
|
202
|
+
if (!Number.isFinite(expiresAt)) {
|
|
203
|
+
throw new Error("Lakebase credential exchange returned an invalid expire_time.");
|
|
204
|
+
}
|
|
205
|
+
return { token: value.token, expiresAt };
|
|
206
|
+
}
|
|
207
|
+
function lakebaseCredentialProvider(options) {
|
|
208
|
+
const now = options.now ?? Date.now;
|
|
209
|
+
const random = options.random ?? Math.random;
|
|
210
|
+
const refreshBeforeMs = options.refreshBeforeMs ?? 5 * 6e4;
|
|
211
|
+
const refreshJitterMs = options.refreshJitterMs ?? 3e4;
|
|
212
|
+
let cached;
|
|
213
|
+
let refreshing;
|
|
214
|
+
const refresh = async (forceWorkspaceToken = false) => {
|
|
215
|
+
const workspaceToken = typeof options.token === "function" ? await (forceWorkspaceToken && options.token.refresh ? options.token.refresh() : options.token()) : options.token;
|
|
216
|
+
const value = await exchangeLakebaseCredential({
|
|
217
|
+
workspaceHost: options.workspaceHost,
|
|
218
|
+
endpoint: options.endpoint,
|
|
219
|
+
workspaceToken,
|
|
220
|
+
fetchImpl: options.fetchImpl
|
|
221
|
+
});
|
|
121
222
|
const jitter = Math.max(0, refreshJitterMs) * random();
|
|
122
223
|
cached = {
|
|
123
224
|
token: value.token,
|
|
124
|
-
refreshAt: Math.max(now(), expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
225
|
+
refreshAt: Math.max(now(), value.expiresAt - Math.max(0, refreshBeforeMs) - jitter)
|
|
125
226
|
};
|
|
126
227
|
return value.token;
|
|
127
228
|
};
|
|
128
|
-
|
|
229
|
+
const getCredential = async () => {
|
|
129
230
|
if (cached && now() < cached.refreshAt) return cached.token;
|
|
130
|
-
refreshing ??= refresh().finally(() => {
|
|
231
|
+
refreshing ??= refresh(false).finally(() => {
|
|
131
232
|
refreshing = void 0;
|
|
132
233
|
});
|
|
133
234
|
return refreshing;
|
|
134
235
|
};
|
|
236
|
+
getCredential.refresh = async () => {
|
|
237
|
+
cached = void 0;
|
|
238
|
+
refreshing ??= refresh(true).finally(() => {
|
|
239
|
+
refreshing = void 0;
|
|
240
|
+
});
|
|
241
|
+
return refreshing;
|
|
242
|
+
};
|
|
243
|
+
return getCredential;
|
|
135
244
|
}
|
|
136
245
|
function parseExpireTime(value) {
|
|
137
246
|
if (typeof value === "number") {
|
|
@@ -199,6 +308,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
199
308
|
options;
|
|
200
309
|
poolFactory;
|
|
201
310
|
getPassword;
|
|
311
|
+
refreshPassword;
|
|
202
312
|
delegatingPool;
|
|
203
313
|
refreshTimer;
|
|
204
314
|
constructor(options) {
|
|
@@ -207,6 +317,7 @@ var LakebaseDatabaseProvider = class {
|
|
|
207
317
|
this.delegatingPool = new DelegatingPool();
|
|
208
318
|
if (options.password !== void 0) {
|
|
209
319
|
this.getPassword = async () => options.password;
|
|
320
|
+
this.refreshPassword = this.getPassword;
|
|
210
321
|
this.delegatingPool.swap(this.createPool(options.password));
|
|
211
322
|
} else {
|
|
212
323
|
if (!options.token || !options.workspaceHost || !options.endpoint) {
|
|
@@ -214,16 +325,23 @@ var LakebaseDatabaseProvider = class {
|
|
|
214
325
|
"Lakebase OAuth requires token, workspaceHost, and endpoint. A workspace token cannot be used directly as the Postgres password."
|
|
215
326
|
);
|
|
216
327
|
}
|
|
217
|
-
|
|
328
|
+
const credential = lakebaseCredentialProvider({
|
|
218
329
|
workspaceHost: options.workspaceHost,
|
|
219
330
|
endpoint: options.endpoint,
|
|
220
331
|
token: options.token,
|
|
221
332
|
fetchImpl: options.fetchImpl
|
|
222
333
|
});
|
|
334
|
+
this.getPassword = credential;
|
|
335
|
+
this.refreshPassword = credential.refresh;
|
|
223
336
|
this.delegatingPool.initialize((password) => this.createPool(password), this.getPassword);
|
|
224
|
-
const intervalMs = options.refreshIntervalMs ??
|
|
337
|
+
const intervalMs = options.refreshIntervalMs ?? 45 * 6e4;
|
|
225
338
|
this.refreshTimer = setInterval(() => {
|
|
226
|
-
void this.
|
|
339
|
+
void this.refreshCredentials().catch((error) => {
|
|
340
|
+
this.delegatingPool.emit(
|
|
341
|
+
"error",
|
|
342
|
+
error instanceof Error ? error : new Error(String(error))
|
|
343
|
+
);
|
|
344
|
+
});
|
|
227
345
|
}, intervalMs);
|
|
228
346
|
}
|
|
229
347
|
}
|
|
@@ -237,17 +355,15 @@ var LakebaseDatabaseProvider = class {
|
|
|
237
355
|
ssl: this.options.ssl ?? { rejectUnauthorized: true },
|
|
238
356
|
max: this.options.max ?? Number.parseInt(process.env.PG_POOL_MAX ?? "5", 10),
|
|
239
357
|
idleTimeoutMillis: this.options.idleTimeoutMillis ?? 3e4,
|
|
240
|
-
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3
|
|
358
|
+
connectionTimeoutMillis: this.options.connectionTimeoutMillis ?? 5e3,
|
|
359
|
+
options: "-c search_path=fx,public"
|
|
241
360
|
});
|
|
242
361
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
} catch (error) {
|
|
249
|
-
this.delegatingPool.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
250
|
-
}
|
|
362
|
+
/** Force a new database credential and atomically swap the active pool. */
|
|
363
|
+
async refreshCredentials() {
|
|
364
|
+
const password = await this.refreshPassword();
|
|
365
|
+
const pool = this.createPool(password);
|
|
366
|
+
this.delegatingPool.swap(pool);
|
|
251
367
|
}
|
|
252
368
|
getPool() {
|
|
253
369
|
return this.delegatingPool;
|
|
@@ -368,6 +484,6 @@ function registerPoolMetrics(collector, poolInstance, intervalMs = 5e3) {
|
|
|
368
484
|
return () => clearInterval(interval);
|
|
369
485
|
}
|
|
370
486
|
|
|
371
|
-
export { closePool, getPool, getProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
|
487
|
+
export { LakebaseDatabaseProvider, closePool, exchangeLakebaseCredential, getPool, getProvider, lakebaseCredentialProvider, registerPoolMetrics, resetPoolProvider, updatePoolMetrics };
|
|
372
488
|
//# sourceMappingURL=index.js.map
|
|
373
489
|
//# 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":["host","token","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:\n options.idleTimeoutMillis ??\n Number.parseInt(process.env.PG_POOL_IDLE_TIMEOUT_MS ?? '30000', 10),\n connectionTimeoutMillis: options.connectionTimeoutMillis ?? 5_000,\n // Cloudflare request isolates cannot safely carry a TCP client owned by\n // one request into the next. Set PG_POOL_MAX_USES=1 there so release()\n // destroys the client synchronously; conventional Node runtimes retain\n // pg-pool's effectively-unbounded default.\n maxUses: Number.parseInt(process.env.PG_POOL_MAX_USES ?? '7500', 10),\n // Lets an edge isolate release its final idle socket instead of keeping\n // an I/O handle owned by a completed request alive indefinitely.\n allowExitOnIdle: true,\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 { readFile } from 'node:fs/promises';\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 kind: 'federated';\n host: string;\n clientId?: string;\n subjectToken: () => Promise<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 if (principal.kind === 'federated') return federatedIdentityProvider(principal);\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): DatabricksPrincipal {\n const authType = env.DATABRICKS_AUTH_TYPE?.trim();\n if (authType === 'env-oidc' || authType === 'file-oidc') {\n const host = env.DATABRICKS_HOST?.trim();\n if (!host) throw new Error('Missing DATABRICKS_HOST for Databricks OAuth federation.');\n return {\n kind: 'federated',\n host,\n clientId: env.DATABRICKS_CLIENT_ID?.trim() || undefined,\n scope: env.DATABRICKS_SCOPE,\n refreshSkewSeconds: env.DATABRICKS_REFRESH_SKEW_SECONDS\n ? Number.parseInt(env.DATABRICKS_REFRESH_SKEW_SECONDS, 10)\n : undefined,\n subjectToken: async () => {\n if (authType === 'env-oidc') {\n const tokenEnv = env.DATABRICKS_OIDC_TOKEN_ENV?.trim() || 'DATABRICKS_OIDC_TOKEN';\n const token = env[tokenEnv]?.trim();\n if (!token) throw new Error(`Databricks OIDC token environment ${tokenEnv} is empty.`);\n return token;\n }\n const path = env.DATABRICKS_OIDC_TOKEN_FILEPATH?.trim();\n if (!path) {\n throw new Error('Missing DATABRICKS_OIDC_TOKEN_FILEPATH for file-oidc federation.');\n }\n const token = (await readFile(path, 'utf8')).trim();\n if (!token) throw new Error(`Databricks OIDC token file ${path} is empty.`);\n return token;\n },\n };\n }\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: env/file OIDC federation, 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\nfunction federatedIdentityProvider(\n principal: Extract<DatabricksPrincipal, { kind: 'federated' }>,\n): DatabricksTokenProvider {\n const host = principal.host.replace(/\\/$/, '');\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 params = new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',\n subject_token: await principal.subjectToken(),\n subject_token_type: 'urn:ietf:params:oauth:token-type:jwt',\n scope: principal.scope ?? 'all-apis',\n });\n if (principal.clientId) params.set('client_id', principal.clientId);\n const response = await fetch(`${host}/oidc/v1/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: params,\n });\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Databricks federated 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 if (typeof value.access_token !== 'string' || !value.access_token) {\n throw new Error('Databricks federated token exchange returned no access_token.');\n }\n const expiresInSeconds = typeof value.expires_in === 'number' ? value.expires_in : 3_600;\n cached = {\n token: value.access_token,\n refreshAtMs: Date.now() + expiresInSeconds * 1_000 - refreshSkewMs,\n };\n return value.access_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 getToken.refresh = async () => {\n cached = undefined;\n return getToken();\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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabricorg/experiments-db-pool",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Singleton pg.Pool for serverless runtimes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,12 +20,16 @@
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
|
-
"files": [
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
"files": ["dist"],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"type-check": "tsc --noEmit",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"clean": "rm -rf dist"
|
|
29
|
+
},
|
|
26
30
|
"dependencies": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
31
|
+
"@fabricorg/experiments-metrics": "^0.0.1",
|
|
32
|
+
"pg": "^8.13.0"
|
|
29
33
|
},
|
|
30
34
|
"devDependencies": {
|
|
31
35
|
"@types/node": "^22.15.3",
|
|
@@ -33,11 +37,5 @@
|
|
|
33
37
|
"tsup": "^8.3.5",
|
|
34
38
|
"typescript": "^5.8.3",
|
|
35
39
|
"vitest": "^2.1.8"
|
|
36
|
-
},
|
|
37
|
-
"scripts": {
|
|
38
|
-
"build": "tsup",
|
|
39
|
-
"type-check": "tsc --noEmit",
|
|
40
|
-
"test": "vitest run",
|
|
41
|
-
"clean": "rm -rf dist"
|
|
42
40
|
}
|
|
43
|
-
}
|
|
41
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Fabric
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
all copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
THE SOFTWARE.
|