()
| 294 | let _initPromise: Promise<void> | undefined; |
| 295 | |
| 296 | async function initClient(): Promise<void> { |
| 297 | if (_exec) return; |
| 298 | |
| 299 | const dialect = getDialect(); |
| 300 | |
| 301 | // Cloudflare D1 |
| 302 | if (dialect === "d1") { |
| 303 | const d1 = globalThis.__cf_env?.DB; |
| 304 | _exec = { |
| 305 | async execute(sql) { |
| 306 | if (typeof sql === "string") { |
| 307 | const r = await d1.prepare(sql).all(); |
| 308 | return { |
| 309 | rows: r.results || [], |
| 310 | rowsAffected: r.meta?.changes ?? 0, |
| 311 | }; |
| 312 | } |
| 313 | const r = await d1 |
| 314 | .prepare(sql.sql) |
| 315 | .bind(...sql.args) |
| 316 | .all(); |
| 317 | return { rows: r.results || [], rowsAffected: r.meta?.changes ?? 0 }; |
| 318 | }, |
| 319 | }; |
| 320 | return; |
| 321 | } |
| 322 | |
| 323 | let url = getDatabaseUrl("file:./data/app.db"); |
| 324 | |
| 325 | // Postgres — uses postgres.js. Works on Node.js natively and on Cloudflare |
| 326 | // Workers with the nodejs_compat compatibility flag (provides net/tls polyfills). |
| 327 | // On Workers, connections can't be shared across requests, so we create a |
| 328 | // fresh connection per query (max:1) to avoid the "I/O on behalf of a |
| 329 | // different request" error. |
| 330 | if (dialect === "postgres") { |
| 331 | const { isNeonUrl } = await import("./create-get-db.js"); |
| 332 | |
| 333 | // Neon over @neondatabase/serverless (WebSocket upgrade on port 443). |
| 334 | // postgres-js uses a raw TCP socket on 5432 that frequently fails on |
| 335 | // serverless runtimes (Netlify Functions, Vercel, CF Workers) when |
| 336 | // Neon's pooler is cold — every request after an idle period times out |
| 337 | // with CONNECT_TIMEOUT. The serverless Pool handles wake-up transparently |
| 338 | // and keeps the same `pg`-compatible query(...) interface we need here. |
| 339 | if (isNeonUrl(url)) { |
| 340 | const { Pool } = await import("@neondatabase/serverless"); |
| 341 | _neonPool = new Pool({ connectionString: url }); |
| 342 | const pool = _neonPool; |
| 343 | _exec = { |
| 344 | async execute(sql) { |
| 345 | const rawSql = typeof sql === "string" ? sql : sql.sql; |
| 346 | const args = typeof sql === "string" ? [] : sql.args || []; |
| 347 | const pgSql = sqliteToPostgresParams(rawSql); |
| 348 | const result = await retryOnConnectionError<{ |
| 349 | rows: unknown[]; |
| 350 | rowCount?: number; |
| 351 | }>(() => pool.query(pgSql, args as any[])); |
| 352 | return { |
| 353 | rows: result.rows, |
no test coverage detected