@nmakarov/cli-toolkit 0.44.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/db.js CHANGED
@@ -116,10 +116,35 @@ async function ensureSchemaEverywhere(dbs, spec, options = {}) {
116
116
  // src/db/index.js
117
117
  var KNEX_DEFAULTS = {
118
118
  testConnection: true,
119
- pool: { min: 2, max: 10 },
119
+ // min: 0 avoids holding idle sockets that go stale during long-running CLIs
120
+ pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
120
121
  acquireConnectionTimeout: 1e4,
121
122
  ssl: { rejectUnauthorized: false }
122
123
  };
124
+ var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
125
+ "ECONNRESET",
126
+ "ECONNREFUSED",
127
+ "EPIPE",
128
+ "ETIMEDOUT",
129
+ "ENOTFOUND",
130
+ "EHOSTUNREACH",
131
+ "ENETUNREACH",
132
+ "ECONNABORTED",
133
+ "CONNECTION_ENDED",
134
+ "CONNECTION_CLOSED",
135
+ // PostgreSQL SQLSTATE class 08xxx (connection exception) + admin shutdowns
136
+ "08000",
137
+ "08001",
138
+ "08003",
139
+ "08004",
140
+ "08006",
141
+ "08007",
142
+ "08P01",
143
+ "57P01",
144
+ "57P02",
145
+ "57P03"
146
+ ]);
147
+ var CONNECTION_ERROR_MESSAGE_RE = /connection (terminated|ended|closed|destroyed|reset|refused|not open)|Connection terminated unexpectedly|Client has encountered a connection error|server closed the connection|Cannot use a pool after calling end|This socket has been ended|connect ECONNRESET|Timeout acquiring a connection/i;
123
148
  var Db = class _Db {
124
149
  static async init(context, options = {}) {
125
150
  const buildConfig = async () => {
@@ -367,10 +392,11 @@ var Db = class _Db {
367
392
  this.knexInstance = null;
368
393
  this.isConnected = false;
369
394
  this.queriesLog = [];
395
+ this._reconnectPromise = null;
370
396
  this.config = {
371
397
  testConnection: true,
372
398
  profile: false,
373
- pool: { min: 2, max: 10 },
399
+ pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
374
400
  acquireConnectionTimeout: 1e4,
375
401
  ssl: { rejectUnauthorized: false },
376
402
  logger: console,
@@ -388,7 +414,7 @@ var Db = class _Db {
388
414
  if (!inst.knexInstance) {
389
415
  throw new Error("Db: Not connected. Call connect() first.");
390
416
  }
391
- return inst.knexInstance(...argumentsList);
417
+ return inst.wrapQueryBuilder(inst.knexInstance(...argumentsList));
392
418
  },
393
419
  get: (target, prop) => {
394
420
  if (prop === "_instance") {
@@ -398,8 +424,12 @@ var Db = class _Db {
398
424
  const ownMethods = [
399
425
  "connect",
400
426
  "disconnect",
427
+ "reconnect",
401
428
  "testConnection",
402
429
  "tableExists",
430
+ "raw",
431
+ "withConnectionRetry",
432
+ "isConnectionError",
403
433
  "getQueryLog",
404
434
  "getKnex",
405
435
  "isConnectedToDb",
@@ -419,6 +449,9 @@ var Db = class _Db {
419
449
  if (inst.knexInstance) {
420
450
  const knexProp = inst.knexInstance[prop];
421
451
  if (typeof knexProp === "function") {
452
+ if (prop === "raw") {
453
+ return (...args) => inst.raw(...args);
454
+ }
422
455
  return knexProp.bind(inst.knexInstance);
423
456
  }
424
457
  return knexProp;
@@ -498,6 +531,130 @@ var Db = class _Db {
498
531
  throw error;
499
532
  }
500
533
  }
534
+ /**
535
+ * True when the error indicates a dead socket / pool that a fresh connect may fix.
536
+ * Safe to call as `Db.prototype.isConnectionError(err)` or via a connected handle.
537
+ */
538
+ isConnectionError(error) {
539
+ if (!error) {
540
+ return false;
541
+ }
542
+ if (error instanceof AggregateError && Array.isArray(error.errors)) {
543
+ return error.errors.some((e) => this.isConnectionError(e));
544
+ }
545
+ const code = error.code ?? error.errno;
546
+ if (code != null && CONNECTION_ERROR_CODES.has(String(code))) {
547
+ return true;
548
+ }
549
+ const msg = error instanceof Error ? error.message : String(error);
550
+ return CONNECTION_ERROR_MESSAGE_RE.test(msg);
551
+ }
552
+ /**
553
+ * Destroy the current knex pool and open a new one. Concurrent callers share one attempt.
554
+ */
555
+ async reconnect() {
556
+ if (this._reconnectPromise) {
557
+ await this._reconnectPromise;
558
+ return;
559
+ }
560
+ this._reconnectPromise = (async () => {
561
+ const old = this.knexInstance;
562
+ this.isConnected = false;
563
+ this.knexInstance = null;
564
+ this.queriesLog = [];
565
+ if (old) {
566
+ try {
567
+ await old.destroy();
568
+ } catch (error) {
569
+ this.logger.debug?.(
570
+ `[Db] destroy during reconnect: ${this.getErrorMessage(error)}`
571
+ );
572
+ }
573
+ }
574
+ await this.connect();
575
+ })();
576
+ try {
577
+ await this._reconnectPromise;
578
+ } finally {
579
+ this._reconnectPromise = null;
580
+ }
581
+ }
582
+ async reconnectAfterConnectionError(error) {
583
+ this.logger.warn?.(
584
+ `[Db] Connection lost (${this.getErrorMessage(error)}) \u2014 reconnecting\u2026`
585
+ );
586
+ await this.reconnect();
587
+ }
588
+ /**
589
+ * Run `fn`; on a connection error, reconnect once (by default) and retry `fn`.
590
+ * `fn` should look up `this.knexInstance` each call so the retry uses the new client.
591
+ */
592
+ async withConnectionRetry(fn, { retries = 1 } = {}) {
593
+ let lastError;
594
+ for (let attempt = 0; attempt <= retries; attempt++) {
595
+ try {
596
+ return await fn();
597
+ } catch (error) {
598
+ lastError = error;
599
+ if (!this.isConnectionError(error) || attempt >= retries) {
600
+ throw error;
601
+ }
602
+ await this.reconnectAfterConnectionError(error);
603
+ }
604
+ }
605
+ throw lastError;
606
+ }
607
+ /**
608
+ * Wrap a knex QueryBuilder / Raw so that awaiting it retries once after reconnect
609
+ * when the first attempt dies with a connection error.
610
+ */
611
+ wrapQueryBuilder(builder) {
612
+ if (!builder || typeof builder.then !== "function" || builder.__dbReconnectWrapped) {
613
+ return builder;
614
+ }
615
+ builder.__dbReconnectWrapped = true;
616
+ const inst = this;
617
+ const protoThen = Object.getPrototypeOf(builder)?.then;
618
+ if (typeof protoThen !== "function") {
619
+ return builder;
620
+ }
621
+ builder.then = function(onFulfilled, onRejected) {
622
+ const run = async () => {
623
+ try {
624
+ return await protoThen.call(builder);
625
+ } catch (error) {
626
+ if (!inst.isConnectionError(error)) {
627
+ throw error;
628
+ }
629
+ await inst.reconnectAfterConnectionError(error);
630
+ if (typeof builder.clone === "function") {
631
+ const retry = builder.clone();
632
+ retry.client = inst.knexInstance.client;
633
+ return await protoThen.call(retry);
634
+ }
635
+ if (typeof builder.toSQL === "function" && inst.knexInstance) {
636
+ const sql = builder.toSQL();
637
+ const statements = Array.isArray(sql) ? sql : [sql];
638
+ let last;
639
+ for (const stmt of statements) {
640
+ last = await inst.knexInstance.raw(stmt.sql, stmt.bindings);
641
+ }
642
+ return last;
643
+ }
644
+ throw error;
645
+ }
646
+ };
647
+ return run().then(onFulfilled, onRejected);
648
+ };
649
+ return builder;
650
+ }
651
+ /** knex.raw with auto-reconnect on dead connections. */
652
+ raw(...args) {
653
+ if (!this.knexInstance) {
654
+ throw new Error("Db: Not connected. Call connect() first.");
655
+ }
656
+ return this.wrapQueryBuilder(this.knexInstance.raw(...args));
657
+ }
501
658
  getErrorMessage(error) {
502
659
  if (error instanceof AggregateError) {
503
660
  const errors = error.errors || [];
@@ -668,7 +825,9 @@ var Db = class _Db {
668
825
  throw new Error("Db: Not connected. Call connect() first.");
669
826
  }
670
827
  try {
671
- return await this.knexInstance.schema.hasTable(tableName);
828
+ return await this.withConnectionRetry(
829
+ () => this.knexInstance.schema.hasTable(tableName)
830
+ );
672
831
  } catch (error) {
673
832
  this.logger.error?.(`[Db] Error checking table existence: ${error.message}`);
674
833
  throw error;
package/dist/db.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/db/index.js","../src/errors.js","../src/db/ensure.js"],"sourcesContent":["import knex from \"knex\";\nimport { ParamError } from \"../errors.js\";\n\nexport {\n ensureExtension,\n ensureTable,\n ensureIndex,\n ensureSchema,\n ensureSchemaEverywhere,\n} from \"./ensure.js\";\n\nconst KNEX_DEFAULTS = {\n testConnection: true,\n pool: { min: 2, max: 10 },\n acquireConnectionTimeout: 10000,\n ssl: { rejectUnauthorized: false },\n};\n\nexport class Db {\n static async init(context, options = {}) {\n const buildConfig = async () => {\n const defs = {\n dbName: \"string\",\n dbProfile: \"boolean default false\",\n };\n const discovered = context?.params?.getAllForModule?.(\"db\", defs) ?? {};\n const merged = { ...discovered, ...options };\n\n let { dbName, dbProfile } = merged;\n let dbConnectionString = options.dbConnectionString ?? options.connectionString;\n let connectionParam = dbConnectionString ? \"options\" : null;\n\n if (!dbConnectionString) {\n const src = context?.args?.getSource?.(\"dbConnectionString\");\n if (src === \"cli\" || src === \"overrides\" || src === \"config\") {\n dbConnectionString = await context.params.get(\"dbConnectionString\", \"string\");\n connectionParam = \"dbConnectionString\";\n }\n }\n\n if (!dbConnectionString && dbName && /^(postgresql|mysql):\\/\\//.test(dbName)) {\n dbConnectionString = dbName;\n dbName = undefined;\n }\n\n if (!dbConnectionString && dbName) {\n const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;\n dbConnectionString = await context.params.get(paramName, \"string\");\n connectionParam = paramName;\n if (!dbConnectionString) {\n throw new ParamError(\n `Db: cannot find dbConnectionString for dbName=\"${dbName}\" (looked for param \"${paramName}\")`\n );\n }\n }\n\n if (!dbConnectionString) {\n dbConnectionString = await context.params.get(\"dbConnectionString\", \"string\");\n if (dbConnectionString) {\n connectionParam = \"dbConnectionString\";\n }\n }\n\n if (!dbConnectionString) {\n if (!dbName) {\n dbName = \"local\";\n }\n const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;\n dbConnectionString = await context.params.get(paramName, \"string\");\n connectionParam = paramName;\n if (!dbConnectionString) {\n throw new ParamError(\n `Db: cannot find dbConnectionString for dbName=\"${dbName}\" (looked for param \"${paramName}\")`\n );\n }\n }\n\n const displayName = resolveDbDisplayName(\n dbName,\n connectionParam,\n context?.args?.env,\n merged.name\n );\n\n return {\n ...KNEX_DEFAULTS,\n connectionString: dbConnectionString,\n name: displayName,\n profile: !!dbProfile,\n logger: context.logger,\n };\n };\n\n const config = context?.params?.runWithModuleAsync\n ? await context.params.runWithModuleAsync(\"db\", buildConfig)\n : await buildConfig();\n\n return dbConnect(context, config);\n }\n\n /**\n * Initialize a SIBLING database handler: a database that lives on the same\n * server with the same credentials/options as an existing (\"base\") one, and\n * differs only by its database name. Typical use: a per-tenant / per-subject\n * database alongside a main database that keeps the shared tables.\n *\n * context.db = await Db.init(context); // main\n * const sub = await Db.initSibling(context, \"src_bright\"); // sibling\n *\n * LOCATION-AGNOSTIC BY DESIGN — the call gracefully falls back to the main\n * database, so callers can use it for all subject data without knowing what\n * has been migrated where:\n * - empty `siblingName` → the main handler (same as Db.init)\n * - sibling DB does not exist yet → the main handler (data not migrated;\n * it still lives in the main database)\n * Callers that must not fall back can check `handler === context.db`.\n *\n * Connection string resolution for an actual sibling, in order:\n * 1. Explicit override — param `dbConnectionStringSib<SiblingName>` (env\n * `DB_CONNECTION_STRING_SIB_<SIBLING_NAME>`, e.g. `src_bright` →\n * `DB_CONNECTION_STRING_SIB_SRC_BRIGHT`). Nobody needs this on day\n * one; it is the escape hatch for when a sibling later moves to its\n * own server — one env var, no code changes (the `SIB_` namespace\n * both overrides the connection and declares that the sibling\n * exists, so it never falls back to main).\n * 2. Derived — take the base connection string and swap the database\n * name (after confirming the database exists on that server). The\n * base is `options.baseDb` (a Db handler), then\n * `options.baseConnectionString`, then `context.db`.\n *\n * Handlers are cached per name on the context (`context.siblingDbs`), so\n * any number of components asking for the same sibling share one pool —\n * including the \"falls back to main\" answer, which is remembered for the\n * lifetime of the process (a mid-run migration is picked up on restart).\n * Disconnect is registered via `context.registerCleanup`, same as the\n * main handler.\n *\n * @param {object} context - context with params/logger (and usually .db)\n * @param {string} [siblingName] - the sibling's database name (e.g. \"src_bright\")\n * @param {object} [options] - { baseDb, baseConnectionString, dbProfile }\n * @returns {Promise<Db>} connected handler (same proxy shape as Db.init)\n */\n static async initSibling(context, siblingName, options = {}) {\n // Empty name → the main database, so call sites stay location-agnostic.\n if (!siblingName) {\n return resolveMainHandler(context, options);\n }\n if (!/^[a-zA-Z0-9_]+$/.test(siblingName)) {\n throw new ParamError(\n `Db.initSibling: invalid sibling database name \"${siblingName}\" (letters, digits and _ only)`\n );\n }\n\n if (!context.siblingDbs) {\n context.siblingDbs = new Map();\n }\n const cached = context.siblingDbs.get(siblingName);\n if (cached) {\n return cached;\n }\n\n // 1. explicit override: dbConnectionStringSib<SiblingName> param / env var.\n // Its presence also DECLARES the sibling (it may live on another server),\n // so the existence check below is skipped.\n const overrideParam = `dbConnectionStringSib${camelizeDbName(siblingName)}`;\n let connectionString = await context?.params?.get?.(overrideParam, \"string\");\n if (connectionString) {\n context.logger?.debug?.(\n `[Db] sibling \"${siblingName}\": using override param \"${overrideParam}\"`\n );\n } else {\n // 2. derive from the base connection string\n const baseHandle = options.baseDb ?? context.db;\n const base =\n baseHandle?.config?.connectionString ?? options.baseConnectionString;\n if (!base) {\n throw new ParamError(\n `Db.initSibling: no base connection to derive \"${siblingName}\" from — ` +\n `init the main Db first (context.db = await Db.init(context)), ` +\n `or pass options.baseDb / options.baseConnectionString, ` +\n `or set the ${overrideParam} param (env ${toEnvKey(overrideParam)})`\n );\n }\n\n // Not migrated yet? Fall back to the main database so the caller's\n // code works the same before and after the split.\n const exists = await databaseExistsOnServer(baseHandle, base, siblingName);\n if (!exists) {\n context.logger?.debug?.(\n `[Db] sibling \"${siblingName}\" does not exist — falling back to the main database`\n );\n const main = await resolveMainHandler(context, options);\n context.siblingDbs.set(siblingName, main);\n return main;\n }\n\n connectionString = replaceDatabaseName(base, siblingName);\n context.logger?.debug?.(\n `[Db] sibling \"${siblingName}\": derived from base (${formatConnectionEndpoint(base) ?? \"?\"})`\n );\n }\n\n const config = {\n ...KNEX_DEFAULTS,\n connectionString,\n name: siblingName,\n profile: !!options.dbProfile,\n logger: context.logger,\n };\n\n const handler = await dbConnect(context, config);\n context.siblingDbs.set(siblingName, handler);\n return handler;\n }\n\n /**\n * Discover the sibling databases that are \"currently in use\", by name.\n * Two sources, merged (env wins on duplicates):\n *\n * 1. ENV-DECLARED — every `DB_CONNECTION_STRING_SIB_<NAME>` env var\n * (the dedicated `SIB_` namespace) whose decoded name matches. This\n * covers siblings that moved to their own server: the same var that\n * overrides the connection also *registers* the database, so `.env`\n * stays the single convenient list.\n * 2. SAME-SERVER SCAN — `SELECT datname FROM pg_database` on the base\n * server (PostgreSQL only), filtered the same way.\n *\n * The caller says what \"matches\": `{ prefix: \"src_\" }` or\n * `{ match: /^src_/ }` — the toolkit does not guess a naming convention.\n *\n * @param {object} context - needs context.db (or options.baseDb) for the server scan\n * @param {{ prefix?: string, match?: RegExp, baseDb?: object, env?: object }} options\n * @returns {Promise<Array<{ name: string, origin: \"env\"|\"server\" }>>} sorted by name\n */\n static async discoverSiblings(context, options = {}) {\n const { prefix, match, env = process.env } = options;\n if (!prefix && !match) {\n throw new ParamError(`Db.discoverSiblings: pass { prefix: \"...\" } or { match: /.../ }`);\n }\n const matches = match instanceof RegExp ? (n) => match.test(n) : (n) => n.startsWith(prefix);\n\n const found = new Map();\n\n // 1. env-declared siblings (possibly on other servers) — SIB_ namespace\n for (const key of Object.keys(env)) {\n const m = /^DB_CONNECTION_STRING_SIB_(.+)$/.exec(key);\n if (!m || !env[key]) continue;\n const name = m[1].toLowerCase();\n if (matches(name)) {\n found.set(name, \"env\");\n }\n }\n\n // 2. same-server scan (PostgreSQL)\n const base = options.baseDb ?? context?.db;\n if (base) {\n const connectionString = String(base.config?.connectionString ?? \"\");\n if (connectionString.startsWith(\"postgresql\")) {\n const { rows } = await base.raw(\n \"SELECT datname FROM pg_database WHERE datistemplate = false\",\n );\n for (const { datname } of rows) {\n if (matches(datname) && !found.has(datname)) {\n found.set(datname, \"server\");\n }\n }\n } else {\n context?.logger?.warn?.(\n \"[Db] discoverSiblings: server scan supported for PostgreSQL only; using env-declared siblings\",\n );\n }\n }\n\n return [...found]\n .map(([name, origin]) => ({ name, origin }))\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Discover + connect: one handler per active sibling (cached, pooled —\n * see initSibling). Pass `includeMain: true` to get `[context.db, ...]`,\n * which is the usual shape for \"apply this DDL everywhere\" loops:\n *\n * const dbs = await Db.initAllSiblings(context, { prefix: \"src_\", includeMain: true });\n * await ensureSchemaEverywhere(dbs, spec, { logger: context.logger });\n *\n * @param {object} context\n * @param {{ prefix?: string, match?: RegExp, includeMain?: boolean, baseDb?: object, env?: object }} options\n * @returns {Promise<Function[]>} connected handlers\n */\n static async initAllSiblings(context, options = {}) {\n const discovered = await Db.discoverSiblings(context, options);\n const handlers = [];\n for (const { name } of discovered) {\n handlers.push(await Db.initSibling(context, name, options));\n }\n return options.includeMain && context.db ? [context.db, ...handlers] : handlers;\n }\n\n constructor(config) {\n if (!config || !config.connectionString) {\n throw new ParamError(\"Db: connectionString is required\");\n }\n\n this.knexInstance = null;\n this.isConnected = false;\n this.queriesLog = [];\n\n this.config = {\n testConnection: true,\n profile: false,\n pool: { min: 2, max: 10 },\n acquireConnectionTimeout: 10000,\n ssl: { rejectUnauthorized: false },\n logger: console,\n ...config,\n };\n\n this.logger = this.config.logger;\n\n const instance = this;\n const callableWrapper = function () {\n throw new Error(\"This should never be called directly\");\n };\n callableWrapper._instance = instance;\n\n return new Proxy(callableWrapper, {\n apply: (target, _thisArg, argumentsList) => {\n const inst = target._instance;\n if (!inst.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n return inst.knexInstance(...argumentsList);\n },\n get: (target, prop) => {\n if (prop === \"_instance\") {\n return target._instance;\n }\n\n const inst = target._instance;\n\n const ownMethods = [\n \"connect\",\n \"disconnect\",\n \"testConnection\",\n \"tableExists\",\n \"getQueryLog\",\n \"getKnex\",\n \"isConnectedToDb\",\n \"getErrorMessage\",\n \"detectClient\",\n \"attachProfiler\",\n ];\n\n if (prop in inst) {\n const value = inst[prop];\n if (typeof value === \"function\" && ownMethods.includes(prop)) {\n return value.bind(inst);\n }\n if (typeof value !== \"function\") {\n return value;\n }\n }\n\n if (inst.knexInstance) {\n const knexProp = inst.knexInstance[prop];\n if (typeof knexProp === \"function\") {\n return knexProp.bind(inst.knexInstance);\n }\n return knexProp;\n }\n\n if (prop in inst) {\n const method = inst[prop];\n if (typeof method === \"function\") {\n return method.bind(inst);\n }\n return method;\n }\n\n return undefined;\n },\n });\n }\n\n detectClient(connectionString) {\n if (connectionString.match(/^postgresql/)) {\n return \"pg\";\n }\n if (connectionString.match(/^mysql/)) {\n return \"mysql2\";\n }\n return null;\n }\n\n async connect() {\n if (this.isConnected && this.knexInstance) {\n this.logger.warn?.(\"[Db] Already connected\");\n return;\n }\n\n const client = this.detectClient(this.config.connectionString);\n if (!client) {\n throw new ParamError(\n \"Db: Cannot determine client type from connection string. Expected postgresql:// or mysql://\"\n );\n }\n\n try {\n const connectionConfig = {\n connectionString: this.config.connectionString,\n family: 4,\n };\n\n this.knexInstance = knex({\n client,\n connection: connectionConfig,\n pool: this.config.pool,\n acquireConnectionTimeout: this.config.acquireConnectionTimeout,\n ...(this.config.ssl && { ssl: this.config.ssl }),\n });\n\n if (this.config.profile) {\n this.attachProfiler();\n }\n\n if (this.config.testConnection) {\n await this.testConnection();\n }\n\n this.isConnected = true;\n this.logger.debug?.(formatDbConnectMessage(this.config.name, this.config.connectionString));\n } catch (error) {\n if (error instanceof ParamError) {\n throw error;\n }\n const errorMsg = this.getErrorMessage(error);\n throw new ParamError(`Db: Connection failed - ${errorMsg}`);\n }\n }\n\n async disconnect() {\n if (!this.knexInstance) {\n return;\n }\n\n try {\n await this.knexInstance.destroy();\n this.knexInstance = null;\n this.isConnected = false;\n this.queriesLog = [];\n this.logger.debug?.(formatDbDisconnectMessage(this.config.name, this.config.connectionString));\n } catch (error) {\n const errorMsg = this.getErrorMessage(error);\n this.logger.error?.(`[Db] Error disconnecting: ${errorMsg}`);\n throw error;\n }\n }\n\n getErrorMessage(error) {\n if (error instanceof AggregateError) {\n const errors = error.errors || [];\n\n if (errors.length > 0) {\n const firstError = errors[0];\n const firstErrorMsg =\n firstError instanceof Error ? firstError.message : String(firstError);\n\n const allSimilar = errors.every((e) => {\n const msg = e instanceof Error ? e.message : String(e);\n const codeMatch = msg.match(/^(\\w+)\\s/);\n const firstCodeMatch = firstErrorMsg.match(/^(\\w+)\\s/);\n return codeMatch && firstCodeMatch && codeMatch[1] === firstCodeMatch[1];\n });\n\n if (allSimilar && errors.length > 1) {\n const addresses = errors\n .map((e) => {\n const msg = e instanceof Error ? e.message : String(e);\n const addrMatch = msg.match(/([:\\d.]+:\\d+)/);\n return addrMatch ? addrMatch[1] : null;\n })\n .filter(Boolean);\n\n if (addresses.length > 0) {\n const codeMatch = firstErrorMsg.match(/^(\\w+)\\s/);\n const code = codeMatch ? codeMatch[1] : \"Connection error\";\n return `${code} (tried: ${addresses.join(\", \")})`;\n }\n }\n\n const uniqueMessages = [\n ...new Set(\n errors.map((e) => (e instanceof Error ? e.message : String(e)))\n ),\n ];\n\n if (uniqueMessages.length === 1) {\n return uniqueMessages[0];\n }\n\n return uniqueMessages.join(\"; \");\n }\n\n return error.message || \"Multiple errors occurred\";\n }\n\n if (error instanceof Error) {\n const code = error.code;\n if (code) {\n return `${code}: ${error.message || String(error)}`;\n }\n return error.message || String(error);\n }\n\n if (typeof error === \"string\") {\n return error;\n }\n\n if (error && typeof error === \"object\" && \"message\" in error) {\n const msg = String(error.message);\n const code = error.code;\n if (code) {\n return `${code}: ${msg}`;\n }\n return msg;\n }\n\n return String(error) || \"Unknown error\";\n }\n\n async testConnection() {\n if (!this.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n\n try {\n const result = await this.knexInstance.raw(\"SELECT 2+3 AS result\");\n const isOk = result.rows?.[0]?.result === 5 || result[0]?.[0]?.result === 5;\n this.logger.debug?.(`[Db] Connection test: ${isOk ? \"OK\" : \"FAILED\"}`);\n return isOk;\n } catch (error) {\n const errorMsg = this.getErrorMessage(error);\n this.logger.error?.(`[Db] Connection test failed: ${errorMsg}`);\n throw new ParamError(`Db: Connection test failed - ${errorMsg}`);\n }\n }\n\n attachProfiler() {\n if (!this.knexInstance) {\n return;\n }\n\n this.queriesLog = [];\n this.knexInstance.queriesLog = this.queriesLog;\n\n // Knex emits fresh Object.assign(...) copies on \"query\" and \"query-response\",\n // so __startTime on the query object does not survive — track per-connection FIFO.\n const startsByConnection = new Map();\n\n const pushStart = (query) => {\n const uid = query?.__knexUid ?? \"\";\n if (!startsByConnection.has(uid)) {\n startsByConnection.set(uid, []);\n }\n startsByConnection.get(uid).push(process.hrtime());\n };\n\n const popStart = (query) => {\n const uid = query?.__knexUid ?? \"\";\n const stack = startsByConnection.get(uid);\n if (!stack?.length) {\n return null;\n }\n return stack.pop();\n };\n\n const isTimeoutError = (error) =>\n error?.name === \"KnexTimeoutError\" ||\n /query timeout|timeout exceeded/i.test(String(error?.message ?? \"\"));\n\n const logQueryEntry = (query, start, { error } = {}) => {\n let executionTimeMs;\n if (start) {\n const [seconds, nanoseconds] = process.hrtime(start);\n executionTimeMs = (seconds * 1000 + nanoseconds / 1e6).toFixed(2);\n } else if (error?.timeout != null) {\n executionTimeMs = Number(error.timeout).toFixed(2);\n } else {\n executionTimeMs = \"?\";\n }\n\n let status = \"ok\";\n if (error) {\n status = isTimeoutError(error) ? \"timeout\" : \"error\";\n }\n\n const logEntry = {\n sql: query?.sql,\n bindings: query?.bindings || [],\n executionTimeMs,\n status,\n };\n if (error) {\n logEntry.error = error.message ?? String(error);\n }\n\n this.queriesLog.push(logEntry);\n const suffix = error ? ` | ${status}: ${logEntry.error}` : \"\";\n this.logger.debug?.(\n `[Db] Query: ${query?.sql} | Duration: ${executionTimeMs}ms${suffix}`,\n );\n };\n\n const handleQueryError = (error, query) => {\n const start = popStart(query);\n logQueryEntry(query, start, { error });\n this.logger.error?.(`[Db] Query failed: ${query?.sql}`, error);\n };\n\n this.knexInstance.on(\"query\", (query) => {\n pushStart(query);\n });\n\n this.knexInstance.on(\"query-response\", (_response, query) => {\n const start = popStart(query);\n if (!start) {\n return;\n }\n logQueryEntry(query, start);\n });\n\n this.knexInstance.on(\"query-error\", (error, query) => {\n handleQueryError(error, query);\n });\n\n // Builder queries emit \"query\" / \"query-error\" on the builder, not always on client.\n const hookBuilder = (builder) => {\n if (!builder || builder.__dbProfilerHooked) {\n return;\n }\n builder.__dbProfilerHooked = true;\n builder.on(\"query\", pushStart);\n builder.on(\"query-error\", handleQueryError);\n };\n\n const client = this.knexInstance.client;\n if (client && !client.__dbProfilerPatched) {\n client.__dbProfilerPatched = true;\n if (typeof client.queryBuilder === \"function\") {\n const origQueryBuilder = client.queryBuilder.bind(client);\n client.queryBuilder = (...args) => {\n const builder = origQueryBuilder(...args);\n hookBuilder(builder);\n return builder;\n };\n }\n }\n }\n\n getQueryLog() {\n return [...this.queriesLog];\n }\n\n async tableExists(tableName) {\n if (!this.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n\n try {\n return await this.knexInstance.schema.hasTable(tableName);\n } catch (error) {\n this.logger.error?.(`[Db] Error checking table existence: ${error.message}`);\n throw error;\n }\n }\n\n getKnex() {\n if (!this.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n return this.knexInstance;\n }\n\n isConnectedToDb() {\n return this.isConnected && this.knexInstance !== null;\n }\n}\n\nfunction capitalizeFirstLetter(str) {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * The main database handler, for initSibling's fallbacks: the base handle when\n * one was passed, otherwise `context.db` (initialized on demand, so an\n * initSibling-only script still works).\n */\nasync function resolveMainHandler(context, options = {}) {\n if (options.baseDb) {\n return options.baseDb;\n }\n if (!context.db) {\n context.db = await Db.init(context);\n }\n return context.db;\n}\n\n/**\n * Does `databaseName` exist on the base server? Checked via an existing handle\n * when available (no extra connection), else a short-lived one. Non-PostgreSQL\n * bases skip the check (returns true — connection will tell).\n */\nasync function databaseExistsOnServer(baseHandle, baseConnectionString, databaseName) {\n if (!String(baseConnectionString).startsWith(\"postgresql\")) {\n return true;\n }\n const query = \"SELECT 1 FROM pg_database WHERE datname = ?\";\n if (baseHandle) {\n const { rows } = await baseHandle.raw(query, [databaseName]);\n return rows.length > 0;\n }\n const shortLived = knex({\n client: \"pg\",\n connection: { connectionString: baseConnectionString },\n pool: { min: 0, max: 1 },\n });\n try {\n const { rows } = await shortLived.raw(query, [databaseName]);\n return rows.length > 0;\n } finally {\n await shortLived.destroy();\n }\n}\n\n/**\n * Database name → camelCase param suffix: \"src_bright\" → \"SrcBright\",\n * so the override param becomes dbConnectionStringSibSrcBright\n * (env DB_CONNECTION_STRING_SIB_SRC_BRIGHT).\n */\nfunction camelizeDbName(name) {\n return name\n .split(/[_-]+/)\n .filter(Boolean)\n .map(capitalizeFirstLetter)\n .join(\"\");\n}\n\n/** camelCase param name → its SNAKE_CASE env var (for error messages only). */\nfunction toEnvKey(key) {\n return key.replace(/([A-Z])/g, \"_$1\").toUpperCase();\n}\n\n/**\n * Replace the database name in a connection string, keeping everything else\n * (credentials, host, port, query options like sslmode) intact.\n * Exported for reuse (e.g. database-splitting tools) and tests.\n */\nexport function replaceDatabaseName(connectionString, databaseName) {\n let url;\n try {\n url = new URL(connectionString);\n } catch {\n throw new ParamError(\n `Db: cannot parse connection string to derive a sibling database from it`\n );\n }\n url.pathname = `/${databaseName}`;\n return url.toString();\n}\n\nfunction resolveDbDisplayName(dbName, connectionParam, argsEnv, mergedName) {\n if (dbName) {\n return dbName;\n }\n if (mergedName) {\n return mergedName;\n }\n if (\n connectionParam?.startsWith(\"dbConnectionString\") &&\n connectionParam.length > \"dbConnectionString\".length\n ) {\n return connectionParam.slice(\"dbConnectionString\".length).toLowerCase();\n }\n if (connectionParam === \"dbConnectionString\" && argsEnv) {\n return argsEnv;\n }\n return undefined;\n}\n\nfunction formatDbConnectMessage(name, connectionString) {\n const endpointSuffix = formatConnectionEndpointSuffix(connectionString);\n if (name) {\n return `[Db] Connected to database \"${name}\"${endpointSuffix}`;\n }\n return `[Db] Connected${endpointSuffix}`;\n}\n\nfunction formatDbDisconnectMessage(name, connectionString) {\n const endpointSuffix = formatConnectionEndpointSuffix(connectionString);\n if (name) {\n return `[Db] Disconnected from database \"${name}\"${endpointSuffix}`;\n }\n return `[Db] Disconnected${endpointSuffix}`;\n}\n\nfunction formatDbInstanceMessage(action, name) {\n if (name) {\n return `[Db] instance \"${name}\" ${action}`;\n }\n return `[Db] instance ${action}`;\n}\n\nfunction formatConnectionEndpointSuffix(connectionString) {\n const endpoint = formatConnectionEndpoint(connectionString);\n return endpoint ? ` (${endpoint})` : \"\";\n}\n\nfunction formatConnectionEndpoint(connectionString) {\n try {\n const url = new URL(connectionString);\n const host = url.hostname;\n if (!host) {\n return null;\n }\n\n let port = url.port;\n if (!port) {\n if (url.protocol === \"postgresql:\") {\n port = \"5432\";\n } else if (url.protocol === \"mysql:\") {\n port = \"3306\";\n }\n }\n\n return port ? `${host}:${port}` : host;\n } catch {\n return null;\n }\n}\n\nasync function dbConnect(context, config) {\n try {\n const db = new Db(config);\n\n context.registerCleanup(async () => {\n await db.disconnect();\n context.logger.debug?.(formatDbInstanceMessage(\"disconnected\", config.name));\n });\n\n await db.connect();\n\n context.logger.debug?.(formatDbInstanceMessage(\"initialized\", config.name));\n\n return db;\n } catch (error) {\n if (error instanceof ParamError) {\n throw error;\n }\n const errorMsg = error instanceof Error ? error.message : String(error);\n throw new ParamError(`[Db] connect error: ${errorMsg}`);\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n","/**\n * Declarative, idempotent DDL — \"make sure this schema exists\" helpers.\n *\n * Components describe the schema they need (tables, columns, indexes,\n * extensions) once, as data; `ensureSchema` diffs that spec against a live\n * database and applies only what's missing:\n *\n * - table missing → CREATE TABLE with all columns + indexes\n * - table there, column not → ALTER TABLE ADD COLUMN (never drops/changes)\n * - index/unique missing → CREATE [UNIQUE] INDEX IF NOT EXISTS\n * - extension missing → CREATE EXTENSION IF NOT EXISTS\n *\n * Because the input is a spec (not imperative code), the same call can be\n * repeated over any number of databases — the main one and every sibling —\n * and each gets exactly the DDL it lacks. See Db.initSibling /\n * Db.discoverSiblings for getting those handles.\n *\n * Spec shape:\n *\n * {\n * extensions: [\"uuid-ossp\"],\n * tables: {\n * my_table: {\n * columns: {\n * id: (t, db) => t.uuid(\"id\").primary().defaultTo(db.raw(\"uuid_generate_v4()\")),\n * name: (t) => t.text(\"name\").notNullable(),\n * },\n * indexes: [\n * { columns: [\"name\"], name: \"my_table_name_idx\" },\n * { columns: [\"a\", \"b\"], name: \"my_table_ab_uniq\", unique: true },\n * ],\n * },\n * },\n * }\n *\n * Every function accepts `{ dryRun: true }` and then only *reports* what it\n * would do (returned in the report, logged when a logger is given) without\n * touching the database.\n */\n\n/** @typedef {{ columns: Record<string, Function>, indexes?: Array<{ columns: string[], name: string, unique?: boolean }> }} TableSpec */\n/** @typedef {{ extensions?: string[], tables?: Record<string, TableSpec> }} SchemaSpec */\n\nconst dbLabel = (db) => db?.config?.name ?? \"db\";\n\n/**\n * CREATE EXTENSION IF NOT EXISTS (PostgreSQL). No-op report entry on dryRun.\n *\n * @param {Function} db - a connected Db/knex handle\n * @param {string} name - extension name, e.g. \"uuid-ossp\"\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<{ action: string }>}\n */\nexport async function ensureExtension(db, name, options = {}) {\n const action = `CREATE EXTENSION IF NOT EXISTS \"${name}\"`;\n if (!options.dryRun) {\n await db.raw(action);\n }\n options.logger?.silly?.(`[ensure] ${dbLabel(db)}: ${action}${options.dryRun ? \" (dryRun)\" : \"\"}`);\n return { action };\n}\n\n/**\n * Create a table when missing; when it exists, add any missing columns and\n * indexes from the spec. Never drops or alters existing columns.\n *\n * @param {Function} db\n * @param {string} tableName\n * @param {TableSpec} spec\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<string[]>} human-readable list of applied (or planned) DDL\n */\nexport async function ensureTable(db, tableName, spec, options = {}) {\n const { dryRun = false, logger } = options;\n const actions = [];\n const exists = await db.tableExists(tableName);\n\n if (!exists) {\n actions.push(`CREATE TABLE ${tableName} (${Object.keys(spec.columns).length} columns)`);\n if (!dryRun) {\n await db.schema.createTable(tableName, (t) => {\n for (const define of Object.values(spec.columns)) {\n define(t, db);\n }\n });\n }\n } else {\n // Column-level ensure: add whatever the spec has that the table lacks.\n const missing = [];\n for (const column of Object.keys(spec.columns)) {\n if (!(await db.schema.hasColumn(tableName, column))) {\n missing.push(column);\n }\n }\n if (missing.length > 0) {\n actions.push(`ALTER TABLE ${tableName} ADD COLUMN ${missing.join(\", \")}`);\n if (!dryRun) {\n await db.schema.alterTable(tableName, (t) => {\n for (const column of missing) {\n spec.columns[column](t, db);\n }\n });\n }\n }\n }\n\n for (const index of spec.indexes ?? []) {\n const indexActions = await ensureIndex(db, tableName, index, options);\n actions.push(...indexActions);\n }\n\n for (const action of actions) {\n logger?.silly?.(`[ensure] ${dbLabel(db)}: ${action}${dryRun ? \" (dryRun)\" : \"\"}`);\n }\n return actions;\n}\n\n/**\n * Ensure a (unique) index exists. On PostgreSQL this is a real existence check\n * (`CREATE [UNIQUE] INDEX IF NOT EXISTS`); other dialects fall back to\n * attempt-and-tolerate-\"already exists\".\n *\n * @param {Function} db\n * @param {string} tableName\n * @param {{ columns: string[], name: string, unique?: boolean }} index\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<string[]>}\n */\nexport async function ensureIndex(db, tableName, index, options = {}) {\n const { dryRun = false } = options;\n const kind = index.unique ? \"UNIQUE INDEX\" : \"INDEX\";\n const cols = index.columns.map((c) => `\"${c}\"`).join(\", \");\n const isPg = String(db?.config?.connectionString ?? \"\").startsWith(\"postgresql\");\n\n if (isPg) {\n const sql = `CREATE ${kind} IF NOT EXISTS \"${index.name}\" ON \"${tableName}\" (${cols})`;\n const { rows } = await db.raw(`SELECT 1 FROM pg_indexes WHERE indexname = ?`, [index.name]);\n if (rows.length > 0) return [];\n if (!dryRun) await db.raw(sql);\n return [sql];\n }\n\n // Non-pg fallback: try, and treat \"already exists\" as success.\n const sql = `CREATE ${kind} ${index.name} ON ${tableName} (${cols})`;\n if (!dryRun) {\n try {\n await db.raw(sql);\n } catch (error) {\n if (!/already exists|duplicate/i.test(error?.message ?? \"\")) throw error;\n return [];\n }\n }\n return [sql];\n}\n\n/**\n * Apply a full schema spec to ONE database. Returns the list of applied\n * (or, with dryRun, planned) DDL statements — empty when everything is\n * already in place.\n *\n * @param {Function} db\n * @param {SchemaSpec} spec\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<{ database: string, actions: string[] }>}\n */\nexport async function ensureSchema(db, spec, options = {}) {\n const actions = [];\n for (const extension of spec.extensions ?? []) {\n // Extensions are cheap no-ops when present; report only in dryRun.\n const { action } = await ensureExtension(db, extension, options);\n if (options.dryRun) actions.push(action);\n }\n for (const [tableName, tableSpec] of Object.entries(spec.tables ?? {})) {\n actions.push(...(await ensureTable(db, tableName, tableSpec, options)));\n }\n return { database: dbLabel(db), actions };\n}\n\n/**\n * Apply a schema spec to MANY databases (e.g. the main DB plus every sibling).\n * Convenience loop over {@link ensureSchema} with a combined report and\n * per-database info logging.\n *\n * @param {Function[]} dbs - connected handles (see Db.initAllSiblings)\n * @param {SchemaSpec} spec\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<Array<{ database: string, actions: string[] }>>}\n */\nexport async function ensureSchemaEverywhere(dbs, spec, options = {}) {\n const reports = [];\n for (const db of dbs) {\n const report = await ensureSchema(db, spec, options);\n if (report.actions.length > 0) {\n options.logger?.info?.(\n `[ensure] ${report.database}: ${options.dryRun ? \"would apply\" : \"applied\"} ${report.actions.length} DDL statement(s)`,\n );\n }\n reports.push(report);\n }\n return reports;\n}\n"],"mappings":"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","names":["sql"]}
1
+ {"version":3,"sources":["../src/db/index.js","../src/errors.js","../src/db/ensure.js"],"sourcesContent":["import knex from \"knex\";\nimport { ParamError } from \"../errors.js\";\n\nexport {\n ensureExtension,\n ensureTable,\n ensureIndex,\n ensureSchema,\n ensureSchemaEverywhere,\n} from \"./ensure.js\";\n\nconst KNEX_DEFAULTS = {\n testConnection: true,\n // min: 0 avoids holding idle sockets that go stale during long-running CLIs\n pool: { min: 0, max: 10, idleTimeoutMillis: 30_000 },\n acquireConnectionTimeout: 10000,\n ssl: { rejectUnauthorized: false },\n};\n\n/** Node / pg / knex signals that the socket or pool is dead and a reconnect may help. */\nconst CONNECTION_ERROR_CODES = new Set([\n \"ECONNRESET\",\n \"ECONNREFUSED\",\n \"EPIPE\",\n \"ETIMEDOUT\",\n \"ENOTFOUND\",\n \"EHOSTUNREACH\",\n \"ENETUNREACH\",\n \"ECONNABORTED\",\n \"CONNECTION_ENDED\",\n \"CONNECTION_CLOSED\",\n // PostgreSQL SQLSTATE class 08xxx (connection exception) + admin shutdowns\n \"08000\",\n \"08001\",\n \"08003\",\n \"08004\",\n \"08006\",\n \"08007\",\n \"08P01\",\n \"57P01\",\n \"57P02\",\n \"57P03\",\n]);\n\nconst CONNECTION_ERROR_MESSAGE_RE =\n /connection (terminated|ended|closed|destroyed|reset|refused|not open)|Connection terminated unexpectedly|Client has encountered a connection error|server closed the connection|Cannot use a pool after calling end|This socket has been ended|connect ECONNRESET|Timeout acquiring a connection/i;\n\nexport class Db {\n static async init(context, options = {}) {\n const buildConfig = async () => {\n const defs = {\n dbName: \"string\",\n dbProfile: \"boolean default false\",\n };\n const discovered = context?.params?.getAllForModule?.(\"db\", defs) ?? {};\n const merged = { ...discovered, ...options };\n\n let { dbName, dbProfile } = merged;\n let dbConnectionString = options.dbConnectionString ?? options.connectionString;\n let connectionParam = dbConnectionString ? \"options\" : null;\n\n if (!dbConnectionString) {\n const src = context?.args?.getSource?.(\"dbConnectionString\");\n if (src === \"cli\" || src === \"overrides\" || src === \"config\") {\n dbConnectionString = await context.params.get(\"dbConnectionString\", \"string\");\n connectionParam = \"dbConnectionString\";\n }\n }\n\n if (!dbConnectionString && dbName && /^(postgresql|mysql):\\/\\//.test(dbName)) {\n dbConnectionString = dbName;\n dbName = undefined;\n }\n\n if (!dbConnectionString && dbName) {\n const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;\n dbConnectionString = await context.params.get(paramName, \"string\");\n connectionParam = paramName;\n if (!dbConnectionString) {\n throw new ParamError(\n `Db: cannot find dbConnectionString for dbName=\"${dbName}\" (looked for param \"${paramName}\")`\n );\n }\n }\n\n if (!dbConnectionString) {\n dbConnectionString = await context.params.get(\"dbConnectionString\", \"string\");\n if (dbConnectionString) {\n connectionParam = \"dbConnectionString\";\n }\n }\n\n if (!dbConnectionString) {\n if (!dbName) {\n dbName = \"local\";\n }\n const paramName = `dbConnectionString${capitalizeFirstLetter(dbName)}`;\n dbConnectionString = await context.params.get(paramName, \"string\");\n connectionParam = paramName;\n if (!dbConnectionString) {\n throw new ParamError(\n `Db: cannot find dbConnectionString for dbName=\"${dbName}\" (looked for param \"${paramName}\")`\n );\n }\n }\n\n const displayName = resolveDbDisplayName(\n dbName,\n connectionParam,\n context?.args?.env,\n merged.name\n );\n\n return {\n ...KNEX_DEFAULTS,\n connectionString: dbConnectionString,\n name: displayName,\n profile: !!dbProfile,\n logger: context.logger,\n };\n };\n\n const config = context?.params?.runWithModuleAsync\n ? await context.params.runWithModuleAsync(\"db\", buildConfig)\n : await buildConfig();\n\n return dbConnect(context, config);\n }\n\n /**\n * Initialize a SIBLING database handler: a database that lives on the same\n * server with the same credentials/options as an existing (\"base\") one, and\n * differs only by its database name. Typical use: a per-tenant / per-subject\n * database alongside a main database that keeps the shared tables.\n *\n * context.db = await Db.init(context); // main\n * const sub = await Db.initSibling(context, \"src_bright\"); // sibling\n *\n * LOCATION-AGNOSTIC BY DESIGN — the call gracefully falls back to the main\n * database, so callers can use it for all subject data without knowing what\n * has been migrated where:\n * - empty `siblingName` → the main handler (same as Db.init)\n * - sibling DB does not exist yet → the main handler (data not migrated;\n * it still lives in the main database)\n * Callers that must not fall back can check `handler === context.db`.\n *\n * Connection string resolution for an actual sibling, in order:\n * 1. Explicit override — param `dbConnectionStringSib<SiblingName>` (env\n * `DB_CONNECTION_STRING_SIB_<SIBLING_NAME>`, e.g. `src_bright` →\n * `DB_CONNECTION_STRING_SIB_SRC_BRIGHT`). Nobody needs this on day\n * one; it is the escape hatch for when a sibling later moves to its\n * own server — one env var, no code changes (the `SIB_` namespace\n * both overrides the connection and declares that the sibling\n * exists, so it never falls back to main).\n * 2. Derived — take the base connection string and swap the database\n * name (after confirming the database exists on that server). The\n * base is `options.baseDb` (a Db handler), then\n * `options.baseConnectionString`, then `context.db`.\n *\n * Handlers are cached per name on the context (`context.siblingDbs`), so\n * any number of components asking for the same sibling share one pool —\n * including the \"falls back to main\" answer, which is remembered for the\n * lifetime of the process (a mid-run migration is picked up on restart).\n * Disconnect is registered via `context.registerCleanup`, same as the\n * main handler.\n *\n * @param {object} context - context with params/logger (and usually .db)\n * @param {string} [siblingName] - the sibling's database name (e.g. \"src_bright\")\n * @param {object} [options] - { baseDb, baseConnectionString, dbProfile }\n * @returns {Promise<Db>} connected handler (same proxy shape as Db.init)\n */\n static async initSibling(context, siblingName, options = {}) {\n // Empty name → the main database, so call sites stay location-agnostic.\n if (!siblingName) {\n return resolveMainHandler(context, options);\n }\n if (!/^[a-zA-Z0-9_]+$/.test(siblingName)) {\n throw new ParamError(\n `Db.initSibling: invalid sibling database name \"${siblingName}\" (letters, digits and _ only)`\n );\n }\n\n if (!context.siblingDbs) {\n context.siblingDbs = new Map();\n }\n const cached = context.siblingDbs.get(siblingName);\n if (cached) {\n return cached;\n }\n\n // 1. explicit override: dbConnectionStringSib<SiblingName> param / env var.\n // Its presence also DECLARES the sibling (it may live on another server),\n // so the existence check below is skipped.\n const overrideParam = `dbConnectionStringSib${camelizeDbName(siblingName)}`;\n let connectionString = await context?.params?.get?.(overrideParam, \"string\");\n if (connectionString) {\n context.logger?.debug?.(\n `[Db] sibling \"${siblingName}\": using override param \"${overrideParam}\"`\n );\n } else {\n // 2. derive from the base connection string\n const baseHandle = options.baseDb ?? context.db;\n const base =\n baseHandle?.config?.connectionString ?? options.baseConnectionString;\n if (!base) {\n throw new ParamError(\n `Db.initSibling: no base connection to derive \"${siblingName}\" from — ` +\n `init the main Db first (context.db = await Db.init(context)), ` +\n `or pass options.baseDb / options.baseConnectionString, ` +\n `or set the ${overrideParam} param (env ${toEnvKey(overrideParam)})`\n );\n }\n\n // Not migrated yet? Fall back to the main database so the caller's\n // code works the same before and after the split.\n const exists = await databaseExistsOnServer(baseHandle, base, siblingName);\n if (!exists) {\n context.logger?.debug?.(\n `[Db] sibling \"${siblingName}\" does not exist — falling back to the main database`\n );\n const main = await resolveMainHandler(context, options);\n context.siblingDbs.set(siblingName, main);\n return main;\n }\n\n connectionString = replaceDatabaseName(base, siblingName);\n context.logger?.debug?.(\n `[Db] sibling \"${siblingName}\": derived from base (${formatConnectionEndpoint(base) ?? \"?\"})`\n );\n }\n\n const config = {\n ...KNEX_DEFAULTS,\n connectionString,\n name: siblingName,\n profile: !!options.dbProfile,\n logger: context.logger,\n };\n\n const handler = await dbConnect(context, config);\n context.siblingDbs.set(siblingName, handler);\n return handler;\n }\n\n /**\n * Discover the sibling databases that are \"currently in use\", by name.\n * Two sources, merged (env wins on duplicates):\n *\n * 1. ENV-DECLARED — every `DB_CONNECTION_STRING_SIB_<NAME>` env var\n * (the dedicated `SIB_` namespace) whose decoded name matches. This\n * covers siblings that moved to their own server: the same var that\n * overrides the connection also *registers* the database, so `.env`\n * stays the single convenient list.\n * 2. SAME-SERVER SCAN — `SELECT datname FROM pg_database` on the base\n * server (PostgreSQL only), filtered the same way.\n *\n * The caller says what \"matches\": `{ prefix: \"src_\" }` or\n * `{ match: /^src_/ }` — the toolkit does not guess a naming convention.\n *\n * @param {object} context - needs context.db (or options.baseDb) for the server scan\n * @param {{ prefix?: string, match?: RegExp, baseDb?: object, env?: object }} options\n * @returns {Promise<Array<{ name: string, origin: \"env\"|\"server\" }>>} sorted by name\n */\n static async discoverSiblings(context, options = {}) {\n const { prefix, match, env = process.env } = options;\n if (!prefix && !match) {\n throw new ParamError(`Db.discoverSiblings: pass { prefix: \"...\" } or { match: /.../ }`);\n }\n const matches = match instanceof RegExp ? (n) => match.test(n) : (n) => n.startsWith(prefix);\n\n const found = new Map();\n\n // 1. env-declared siblings (possibly on other servers) — SIB_ namespace\n for (const key of Object.keys(env)) {\n const m = /^DB_CONNECTION_STRING_SIB_(.+)$/.exec(key);\n if (!m || !env[key]) continue;\n const name = m[1].toLowerCase();\n if (matches(name)) {\n found.set(name, \"env\");\n }\n }\n\n // 2. same-server scan (PostgreSQL)\n const base = options.baseDb ?? context?.db;\n if (base) {\n const connectionString = String(base.config?.connectionString ?? \"\");\n if (connectionString.startsWith(\"postgresql\")) {\n const { rows } = await base.raw(\n \"SELECT datname FROM pg_database WHERE datistemplate = false\",\n );\n for (const { datname } of rows) {\n if (matches(datname) && !found.has(datname)) {\n found.set(datname, \"server\");\n }\n }\n } else {\n context?.logger?.warn?.(\n \"[Db] discoverSiblings: server scan supported for PostgreSQL only; using env-declared siblings\",\n );\n }\n }\n\n return [...found]\n .map(([name, origin]) => ({ name, origin }))\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Discover + connect: one handler per active sibling (cached, pooled —\n * see initSibling). Pass `includeMain: true` to get `[context.db, ...]`,\n * which is the usual shape for \"apply this DDL everywhere\" loops:\n *\n * const dbs = await Db.initAllSiblings(context, { prefix: \"src_\", includeMain: true });\n * await ensureSchemaEverywhere(dbs, spec, { logger: context.logger });\n *\n * @param {object} context\n * @param {{ prefix?: string, match?: RegExp, includeMain?: boolean, baseDb?: object, env?: object }} options\n * @returns {Promise<Function[]>} connected handlers\n */\n static async initAllSiblings(context, options = {}) {\n const discovered = await Db.discoverSiblings(context, options);\n const handlers = [];\n for (const { name } of discovered) {\n handlers.push(await Db.initSibling(context, name, options));\n }\n return options.includeMain && context.db ? [context.db, ...handlers] : handlers;\n }\n\n constructor(config) {\n if (!config || !config.connectionString) {\n throw new ParamError(\"Db: connectionString is required\");\n }\n\n this.knexInstance = null;\n this.isConnected = false;\n this.queriesLog = [];\n this._reconnectPromise = null;\n\n this.config = {\n testConnection: true,\n profile: false,\n pool: { min: 0, max: 10, idleTimeoutMillis: 30_000 },\n acquireConnectionTimeout: 10000,\n ssl: { rejectUnauthorized: false },\n logger: console,\n ...config,\n };\n\n this.logger = this.config.logger;\n\n const instance = this;\n const callableWrapper = function () {\n throw new Error(\"This should never be called directly\");\n };\n callableWrapper._instance = instance;\n\n return new Proxy(callableWrapper, {\n apply: (target, _thisArg, argumentsList) => {\n const inst = target._instance;\n if (!inst.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n return inst.wrapQueryBuilder(inst.knexInstance(...argumentsList));\n },\n get: (target, prop) => {\n if (prop === \"_instance\") {\n return target._instance;\n }\n\n const inst = target._instance;\n\n const ownMethods = [\n \"connect\",\n \"disconnect\",\n \"reconnect\",\n \"testConnection\",\n \"tableExists\",\n \"raw\",\n \"withConnectionRetry\",\n \"isConnectionError\",\n \"getQueryLog\",\n \"getKnex\",\n \"isConnectedToDb\",\n \"getErrorMessage\",\n \"detectClient\",\n \"attachProfiler\",\n ];\n\n if (prop in inst) {\n const value = inst[prop];\n if (typeof value === \"function\" && ownMethods.includes(prop)) {\n return value.bind(inst);\n }\n if (typeof value !== \"function\") {\n return value;\n }\n }\n\n if (inst.knexInstance) {\n const knexProp = inst.knexInstance[prop];\n if (typeof knexProp === \"function\") {\n if (prop === \"raw\") {\n return (...args) => inst.raw(...args);\n }\n return knexProp.bind(inst.knexInstance);\n }\n return knexProp;\n }\n\n if (prop in inst) {\n const method = inst[prop];\n if (typeof method === \"function\") {\n return method.bind(inst);\n }\n return method;\n }\n\n return undefined;\n },\n });\n }\n\n detectClient(connectionString) {\n if (connectionString.match(/^postgresql/)) {\n return \"pg\";\n }\n if (connectionString.match(/^mysql/)) {\n return \"mysql2\";\n }\n return null;\n }\n\n async connect() {\n if (this.isConnected && this.knexInstance) {\n this.logger.warn?.(\"[Db] Already connected\");\n return;\n }\n\n const client = this.detectClient(this.config.connectionString);\n if (!client) {\n throw new ParamError(\n \"Db: Cannot determine client type from connection string. Expected postgresql:// or mysql://\"\n );\n }\n\n try {\n const connectionConfig = {\n connectionString: this.config.connectionString,\n family: 4,\n };\n\n this.knexInstance = knex({\n client,\n connection: connectionConfig,\n pool: this.config.pool,\n acquireConnectionTimeout: this.config.acquireConnectionTimeout,\n ...(this.config.ssl && { ssl: this.config.ssl }),\n });\n\n if (this.config.profile) {\n this.attachProfiler();\n }\n\n if (this.config.testConnection) {\n await this.testConnection();\n }\n\n this.isConnected = true;\n this.logger.debug?.(formatDbConnectMessage(this.config.name, this.config.connectionString));\n } catch (error) {\n if (error instanceof ParamError) {\n throw error;\n }\n const errorMsg = this.getErrorMessage(error);\n throw new ParamError(`Db: Connection failed - ${errorMsg}`);\n }\n }\n\n async disconnect() {\n if (!this.knexInstance) {\n return;\n }\n\n try {\n await this.knexInstance.destroy();\n this.knexInstance = null;\n this.isConnected = false;\n this.queriesLog = [];\n this.logger.debug?.(formatDbDisconnectMessage(this.config.name, this.config.connectionString));\n } catch (error) {\n const errorMsg = this.getErrorMessage(error);\n this.logger.error?.(`[Db] Error disconnecting: ${errorMsg}`);\n throw error;\n }\n }\n\n /**\n * True when the error indicates a dead socket / pool that a fresh connect may fix.\n * Safe to call as `Db.prototype.isConnectionError(err)` or via a connected handle.\n */\n isConnectionError(error) {\n if (!error) {\n return false;\n }\n\n if (error instanceof AggregateError && Array.isArray(error.errors)) {\n return error.errors.some((e) => this.isConnectionError(e));\n }\n\n const code = error.code ?? error.errno;\n if (code != null && CONNECTION_ERROR_CODES.has(String(code))) {\n return true;\n }\n\n const msg = error instanceof Error ? error.message : String(error);\n return CONNECTION_ERROR_MESSAGE_RE.test(msg);\n }\n\n /**\n * Destroy the current knex pool and open a new one. Concurrent callers share one attempt.\n */\n async reconnect() {\n if (this._reconnectPromise) {\n await this._reconnectPromise;\n return;\n }\n\n this._reconnectPromise = (async () => {\n const old = this.knexInstance;\n this.isConnected = false;\n this.knexInstance = null;\n this.queriesLog = [];\n\n if (old) {\n try {\n await old.destroy();\n } catch (error) {\n this.logger.debug?.(\n `[Db] destroy during reconnect: ${this.getErrorMessage(error)}`\n );\n }\n }\n\n await this.connect();\n })();\n\n try {\n await this._reconnectPromise;\n } finally {\n this._reconnectPromise = null;\n }\n }\n\n async reconnectAfterConnectionError(error) {\n this.logger.warn?.(\n `[Db] Connection lost (${this.getErrorMessage(error)}) — reconnecting…`\n );\n await this.reconnect();\n }\n\n /**\n * Run `fn`; on a connection error, reconnect once (by default) and retry `fn`.\n * `fn` should look up `this.knexInstance` each call so the retry uses the new client.\n */\n async withConnectionRetry(fn, { retries = 1 } = {}) {\n let lastError;\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n if (!this.isConnectionError(error) || attempt >= retries) {\n throw error;\n }\n await this.reconnectAfterConnectionError(error);\n }\n }\n throw lastError;\n }\n\n /**\n * Wrap a knex QueryBuilder / Raw so that awaiting it retries once after reconnect\n * when the first attempt dies with a connection error.\n */\n wrapQueryBuilder(builder) {\n if (!builder || typeof builder.then !== \"function\" || builder.__dbReconnectWrapped) {\n return builder;\n }\n\n builder.__dbReconnectWrapped = true;\n const inst = this;\n const protoThen = Object.getPrototypeOf(builder)?.then;\n if (typeof protoThen !== \"function\") {\n return builder;\n }\n\n builder.then = function (onFulfilled, onRejected) {\n const run = async () => {\n try {\n return await protoThen.call(builder);\n } catch (error) {\n if (!inst.isConnectionError(error)) {\n throw error;\n }\n await inst.reconnectAfterConnectionError(error);\n\n if (typeof builder.clone === \"function\") {\n const retry = builder.clone();\n retry.client = inst.knexInstance.client;\n return await protoThen.call(retry);\n }\n\n if (typeof builder.toSQL === \"function\" && inst.knexInstance) {\n const sql = builder.toSQL();\n const statements = Array.isArray(sql) ? sql : [sql];\n let last;\n for (const stmt of statements) {\n last = await inst.knexInstance.raw(stmt.sql, stmt.bindings);\n }\n return last;\n }\n\n throw error;\n }\n };\n return run().then(onFulfilled, onRejected);\n };\n\n return builder;\n }\n\n /** knex.raw with auto-reconnect on dead connections. */\n raw(...args) {\n if (!this.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n return this.wrapQueryBuilder(this.knexInstance.raw(...args));\n }\n\n getErrorMessage(error) {\n if (error instanceof AggregateError) {\n const errors = error.errors || [];\n\n if (errors.length > 0) {\n const firstError = errors[0];\n const firstErrorMsg =\n firstError instanceof Error ? firstError.message : String(firstError);\n\n const allSimilar = errors.every((e) => {\n const msg = e instanceof Error ? e.message : String(e);\n const codeMatch = msg.match(/^(\\w+)\\s/);\n const firstCodeMatch = firstErrorMsg.match(/^(\\w+)\\s/);\n return codeMatch && firstCodeMatch && codeMatch[1] === firstCodeMatch[1];\n });\n\n if (allSimilar && errors.length > 1) {\n const addresses = errors\n .map((e) => {\n const msg = e instanceof Error ? e.message : String(e);\n const addrMatch = msg.match(/([:\\d.]+:\\d+)/);\n return addrMatch ? addrMatch[1] : null;\n })\n .filter(Boolean);\n\n if (addresses.length > 0) {\n const codeMatch = firstErrorMsg.match(/^(\\w+)\\s/);\n const code = codeMatch ? codeMatch[1] : \"Connection error\";\n return `${code} (tried: ${addresses.join(\", \")})`;\n }\n }\n\n const uniqueMessages = [\n ...new Set(\n errors.map((e) => (e instanceof Error ? e.message : String(e)))\n ),\n ];\n\n if (uniqueMessages.length === 1) {\n return uniqueMessages[0];\n }\n\n return uniqueMessages.join(\"; \");\n }\n\n return error.message || \"Multiple errors occurred\";\n }\n\n if (error instanceof Error) {\n const code = error.code;\n if (code) {\n return `${code}: ${error.message || String(error)}`;\n }\n return error.message || String(error);\n }\n\n if (typeof error === \"string\") {\n return error;\n }\n\n if (error && typeof error === \"object\" && \"message\" in error) {\n const msg = String(error.message);\n const code = error.code;\n if (code) {\n return `${code}: ${msg}`;\n }\n return msg;\n }\n\n return String(error) || \"Unknown error\";\n }\n\n async testConnection() {\n if (!this.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n\n try {\n const result = await this.knexInstance.raw(\"SELECT 2+3 AS result\");\n const isOk = result.rows?.[0]?.result === 5 || result[0]?.[0]?.result === 5;\n this.logger.debug?.(`[Db] Connection test: ${isOk ? \"OK\" : \"FAILED\"}`);\n return isOk;\n } catch (error) {\n const errorMsg = this.getErrorMessage(error);\n this.logger.error?.(`[Db] Connection test failed: ${errorMsg}`);\n throw new ParamError(`Db: Connection test failed - ${errorMsg}`);\n }\n }\n\n attachProfiler() {\n if (!this.knexInstance) {\n return;\n }\n\n this.queriesLog = [];\n this.knexInstance.queriesLog = this.queriesLog;\n\n // Knex emits fresh Object.assign(...) copies on \"query\" and \"query-response\",\n // so __startTime on the query object does not survive — track per-connection FIFO.\n const startsByConnection = new Map();\n\n const pushStart = (query) => {\n const uid = query?.__knexUid ?? \"\";\n if (!startsByConnection.has(uid)) {\n startsByConnection.set(uid, []);\n }\n startsByConnection.get(uid).push(process.hrtime());\n };\n\n const popStart = (query) => {\n const uid = query?.__knexUid ?? \"\";\n const stack = startsByConnection.get(uid);\n if (!stack?.length) {\n return null;\n }\n return stack.pop();\n };\n\n const isTimeoutError = (error) =>\n error?.name === \"KnexTimeoutError\" ||\n /query timeout|timeout exceeded/i.test(String(error?.message ?? \"\"));\n\n const logQueryEntry = (query, start, { error } = {}) => {\n let executionTimeMs;\n if (start) {\n const [seconds, nanoseconds] = process.hrtime(start);\n executionTimeMs = (seconds * 1000 + nanoseconds / 1e6).toFixed(2);\n } else if (error?.timeout != null) {\n executionTimeMs = Number(error.timeout).toFixed(2);\n } else {\n executionTimeMs = \"?\";\n }\n\n let status = \"ok\";\n if (error) {\n status = isTimeoutError(error) ? \"timeout\" : \"error\";\n }\n\n const logEntry = {\n sql: query?.sql,\n bindings: query?.bindings || [],\n executionTimeMs,\n status,\n };\n if (error) {\n logEntry.error = error.message ?? String(error);\n }\n\n this.queriesLog.push(logEntry);\n const suffix = error ? ` | ${status}: ${logEntry.error}` : \"\";\n this.logger.debug?.(\n `[Db] Query: ${query?.sql} | Duration: ${executionTimeMs}ms${suffix}`,\n );\n };\n\n const handleQueryError = (error, query) => {\n const start = popStart(query);\n logQueryEntry(query, start, { error });\n this.logger.error?.(`[Db] Query failed: ${query?.sql}`, error);\n };\n\n this.knexInstance.on(\"query\", (query) => {\n pushStart(query);\n });\n\n this.knexInstance.on(\"query-response\", (_response, query) => {\n const start = popStart(query);\n if (!start) {\n return;\n }\n logQueryEntry(query, start);\n });\n\n this.knexInstance.on(\"query-error\", (error, query) => {\n handleQueryError(error, query);\n });\n\n // Builder queries emit \"query\" / \"query-error\" on the builder, not always on client.\n const hookBuilder = (builder) => {\n if (!builder || builder.__dbProfilerHooked) {\n return;\n }\n builder.__dbProfilerHooked = true;\n builder.on(\"query\", pushStart);\n builder.on(\"query-error\", handleQueryError);\n };\n\n const client = this.knexInstance.client;\n if (client && !client.__dbProfilerPatched) {\n client.__dbProfilerPatched = true;\n if (typeof client.queryBuilder === \"function\") {\n const origQueryBuilder = client.queryBuilder.bind(client);\n client.queryBuilder = (...args) => {\n const builder = origQueryBuilder(...args);\n hookBuilder(builder);\n return builder;\n };\n }\n }\n }\n\n getQueryLog() {\n return [...this.queriesLog];\n }\n\n async tableExists(tableName) {\n if (!this.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n\n try {\n return await this.withConnectionRetry(() =>\n this.knexInstance.schema.hasTable(tableName)\n );\n } catch (error) {\n this.logger.error?.(`[Db] Error checking table existence: ${error.message}`);\n throw error;\n }\n }\n\n getKnex() {\n if (!this.knexInstance) {\n throw new Error(\"Db: Not connected. Call connect() first.\");\n }\n return this.knexInstance;\n }\n\n isConnectedToDb() {\n return this.isConnected && this.knexInstance !== null;\n }\n}\n\nfunction capitalizeFirstLetter(str) {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * The main database handler, for initSibling's fallbacks: the base handle when\n * one was passed, otherwise `context.db` (initialized on demand, so an\n * initSibling-only script still works).\n */\nasync function resolveMainHandler(context, options = {}) {\n if (options.baseDb) {\n return options.baseDb;\n }\n if (!context.db) {\n context.db = await Db.init(context);\n }\n return context.db;\n}\n\n/**\n * Does `databaseName` exist on the base server? Checked via an existing handle\n * when available (no extra connection), else a short-lived one. Non-PostgreSQL\n * bases skip the check (returns true — connection will tell).\n */\nasync function databaseExistsOnServer(baseHandle, baseConnectionString, databaseName) {\n if (!String(baseConnectionString).startsWith(\"postgresql\")) {\n return true;\n }\n const query = \"SELECT 1 FROM pg_database WHERE datname = ?\";\n if (baseHandle) {\n const { rows } = await baseHandle.raw(query, [databaseName]);\n return rows.length > 0;\n }\n const shortLived = knex({\n client: \"pg\",\n connection: { connectionString: baseConnectionString },\n pool: { min: 0, max: 1 },\n });\n try {\n const { rows } = await shortLived.raw(query, [databaseName]);\n return rows.length > 0;\n } finally {\n await shortLived.destroy();\n }\n}\n\n/**\n * Database name → camelCase param suffix: \"src_bright\" → \"SrcBright\",\n * so the override param becomes dbConnectionStringSibSrcBright\n * (env DB_CONNECTION_STRING_SIB_SRC_BRIGHT).\n */\nfunction camelizeDbName(name) {\n return name\n .split(/[_-]+/)\n .filter(Boolean)\n .map(capitalizeFirstLetter)\n .join(\"\");\n}\n\n/** camelCase param name → its SNAKE_CASE env var (for error messages only). */\nfunction toEnvKey(key) {\n return key.replace(/([A-Z])/g, \"_$1\").toUpperCase();\n}\n\n/**\n * Replace the database name in a connection string, keeping everything else\n * (credentials, host, port, query options like sslmode) intact.\n * Exported for reuse (e.g. database-splitting tools) and tests.\n */\nexport function replaceDatabaseName(connectionString, databaseName) {\n let url;\n try {\n url = new URL(connectionString);\n } catch {\n throw new ParamError(\n `Db: cannot parse connection string to derive a sibling database from it`\n );\n }\n url.pathname = `/${databaseName}`;\n return url.toString();\n}\n\nfunction resolveDbDisplayName(dbName, connectionParam, argsEnv, mergedName) {\n if (dbName) {\n return dbName;\n }\n if (mergedName) {\n return mergedName;\n }\n if (\n connectionParam?.startsWith(\"dbConnectionString\") &&\n connectionParam.length > \"dbConnectionString\".length\n ) {\n return connectionParam.slice(\"dbConnectionString\".length).toLowerCase();\n }\n if (connectionParam === \"dbConnectionString\" && argsEnv) {\n return argsEnv;\n }\n return undefined;\n}\n\nfunction formatDbConnectMessage(name, connectionString) {\n const endpointSuffix = formatConnectionEndpointSuffix(connectionString);\n if (name) {\n return `[Db] Connected to database \"${name}\"${endpointSuffix}`;\n }\n return `[Db] Connected${endpointSuffix}`;\n}\n\nfunction formatDbDisconnectMessage(name, connectionString) {\n const endpointSuffix = formatConnectionEndpointSuffix(connectionString);\n if (name) {\n return `[Db] Disconnected from database \"${name}\"${endpointSuffix}`;\n }\n return `[Db] Disconnected${endpointSuffix}`;\n}\n\nfunction formatDbInstanceMessage(action, name) {\n if (name) {\n return `[Db] instance \"${name}\" ${action}`;\n }\n return `[Db] instance ${action}`;\n}\n\nfunction formatConnectionEndpointSuffix(connectionString) {\n const endpoint = formatConnectionEndpoint(connectionString);\n return endpoint ? ` (${endpoint})` : \"\";\n}\n\nfunction formatConnectionEndpoint(connectionString) {\n try {\n const url = new URL(connectionString);\n const host = url.hostname;\n if (!host) {\n return null;\n }\n\n let port = url.port;\n if (!port) {\n if (url.protocol === \"postgresql:\") {\n port = \"5432\";\n } else if (url.protocol === \"mysql:\") {\n port = \"3306\";\n }\n }\n\n return port ? `${host}:${port}` : host;\n } catch {\n return null;\n }\n}\n\nasync function dbConnect(context, config) {\n try {\n const db = new Db(config);\n\n context.registerCleanup(async () => {\n await db.disconnect();\n context.logger.debug?.(formatDbInstanceMessage(\"disconnected\", config.name));\n });\n\n await db.connect();\n\n context.logger.debug?.(formatDbInstanceMessage(\"initialized\", config.name));\n\n return db;\n } catch (error) {\n if (error instanceof ParamError) {\n throw error;\n }\n const errorMsg = error instanceof Error ? error.message : String(error);\n throw new ParamError(`[Db] connect error: ${errorMsg}`);\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n","/**\n * Declarative, idempotent DDL — \"make sure this schema exists\" helpers.\n *\n * Components describe the schema they need (tables, columns, indexes,\n * extensions) once, as data; `ensureSchema` diffs that spec against a live\n * database and applies only what's missing:\n *\n * - table missing → CREATE TABLE with all columns + indexes\n * - table there, column not → ALTER TABLE ADD COLUMN (never drops/changes)\n * - index/unique missing → CREATE [UNIQUE] INDEX IF NOT EXISTS\n * - extension missing → CREATE EXTENSION IF NOT EXISTS\n *\n * Because the input is a spec (not imperative code), the same call can be\n * repeated over any number of databases — the main one and every sibling —\n * and each gets exactly the DDL it lacks. See Db.initSibling /\n * Db.discoverSiblings for getting those handles.\n *\n * Spec shape:\n *\n * {\n * extensions: [\"uuid-ossp\"],\n * tables: {\n * my_table: {\n * columns: {\n * id: (t, db) => t.uuid(\"id\").primary().defaultTo(db.raw(\"uuid_generate_v4()\")),\n * name: (t) => t.text(\"name\").notNullable(),\n * },\n * indexes: [\n * { columns: [\"name\"], name: \"my_table_name_idx\" },\n * { columns: [\"a\", \"b\"], name: \"my_table_ab_uniq\", unique: true },\n * ],\n * },\n * },\n * }\n *\n * Every function accepts `{ dryRun: true }` and then only *reports* what it\n * would do (returned in the report, logged when a logger is given) without\n * touching the database.\n */\n\n/** @typedef {{ columns: Record<string, Function>, indexes?: Array<{ columns: string[], name: string, unique?: boolean }> }} TableSpec */\n/** @typedef {{ extensions?: string[], tables?: Record<string, TableSpec> }} SchemaSpec */\n\nconst dbLabel = (db) => db?.config?.name ?? \"db\";\n\n/**\n * CREATE EXTENSION IF NOT EXISTS (PostgreSQL). No-op report entry on dryRun.\n *\n * @param {Function} db - a connected Db/knex handle\n * @param {string} name - extension name, e.g. \"uuid-ossp\"\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<{ action: string }>}\n */\nexport async function ensureExtension(db, name, options = {}) {\n const action = `CREATE EXTENSION IF NOT EXISTS \"${name}\"`;\n if (!options.dryRun) {\n await db.raw(action);\n }\n options.logger?.silly?.(`[ensure] ${dbLabel(db)}: ${action}${options.dryRun ? \" (dryRun)\" : \"\"}`);\n return { action };\n}\n\n/**\n * Create a table when missing; when it exists, add any missing columns and\n * indexes from the spec. Never drops or alters existing columns.\n *\n * @param {Function} db\n * @param {string} tableName\n * @param {TableSpec} spec\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<string[]>} human-readable list of applied (or planned) DDL\n */\nexport async function ensureTable(db, tableName, spec, options = {}) {\n const { dryRun = false, logger } = options;\n const actions = [];\n const exists = await db.tableExists(tableName);\n\n if (!exists) {\n actions.push(`CREATE TABLE ${tableName} (${Object.keys(spec.columns).length} columns)`);\n if (!dryRun) {\n await db.schema.createTable(tableName, (t) => {\n for (const define of Object.values(spec.columns)) {\n define(t, db);\n }\n });\n }\n } else {\n // Column-level ensure: add whatever the spec has that the table lacks.\n const missing = [];\n for (const column of Object.keys(spec.columns)) {\n if (!(await db.schema.hasColumn(tableName, column))) {\n missing.push(column);\n }\n }\n if (missing.length > 0) {\n actions.push(`ALTER TABLE ${tableName} ADD COLUMN ${missing.join(\", \")}`);\n if (!dryRun) {\n await db.schema.alterTable(tableName, (t) => {\n for (const column of missing) {\n spec.columns[column](t, db);\n }\n });\n }\n }\n }\n\n for (const index of spec.indexes ?? []) {\n const indexActions = await ensureIndex(db, tableName, index, options);\n actions.push(...indexActions);\n }\n\n for (const action of actions) {\n logger?.silly?.(`[ensure] ${dbLabel(db)}: ${action}${dryRun ? \" (dryRun)\" : \"\"}`);\n }\n return actions;\n}\n\n/**\n * Ensure a (unique) index exists. On PostgreSQL this is a real existence check\n * (`CREATE [UNIQUE] INDEX IF NOT EXISTS`); other dialects fall back to\n * attempt-and-tolerate-\"already exists\".\n *\n * @param {Function} db\n * @param {string} tableName\n * @param {{ columns: string[], name: string, unique?: boolean }} index\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<string[]>}\n */\nexport async function ensureIndex(db, tableName, index, options = {}) {\n const { dryRun = false } = options;\n const kind = index.unique ? \"UNIQUE INDEX\" : \"INDEX\";\n const cols = index.columns.map((c) => `\"${c}\"`).join(\", \");\n const isPg = String(db?.config?.connectionString ?? \"\").startsWith(\"postgresql\");\n\n if (isPg) {\n const sql = `CREATE ${kind} IF NOT EXISTS \"${index.name}\" ON \"${tableName}\" (${cols})`;\n const { rows } = await db.raw(`SELECT 1 FROM pg_indexes WHERE indexname = ?`, [index.name]);\n if (rows.length > 0) return [];\n if (!dryRun) await db.raw(sql);\n return [sql];\n }\n\n // Non-pg fallback: try, and treat \"already exists\" as success.\n const sql = `CREATE ${kind} ${index.name} ON ${tableName} (${cols})`;\n if (!dryRun) {\n try {\n await db.raw(sql);\n } catch (error) {\n if (!/already exists|duplicate/i.test(error?.message ?? \"\")) throw error;\n return [];\n }\n }\n return [sql];\n}\n\n/**\n * Apply a full schema spec to ONE database. Returns the list of applied\n * (or, with dryRun, planned) DDL statements — empty when everything is\n * already in place.\n *\n * @param {Function} db\n * @param {SchemaSpec} spec\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<{ database: string, actions: string[] }>}\n */\nexport async function ensureSchema(db, spec, options = {}) {\n const actions = [];\n for (const extension of spec.extensions ?? []) {\n // Extensions are cheap no-ops when present; report only in dryRun.\n const { action } = await ensureExtension(db, extension, options);\n if (options.dryRun) actions.push(action);\n }\n for (const [tableName, tableSpec] of Object.entries(spec.tables ?? {})) {\n actions.push(...(await ensureTable(db, tableName, tableSpec, options)));\n }\n return { database: dbLabel(db), actions };\n}\n\n/**\n * Apply a schema spec to MANY databases (e.g. the main DB plus every sibling).\n * Convenience loop over {@link ensureSchema} with a combined report and\n * per-database info logging.\n *\n * @param {Function[]} dbs - connected handles (see Db.initAllSiblings)\n * @param {SchemaSpec} spec\n * @param {{ dryRun?: boolean, logger?: object }} [options]\n * @returns {Promise<Array<{ database: string, actions: string[] }>>}\n */\nexport async function ensureSchemaEverywhere(dbs, spec, options = {}) {\n const reports = [];\n for (const db of dbs) {\n const report = await ensureSchema(db, spec, options);\n if (report.actions.length > 0) {\n options.logger?.info?.(\n `[ensure] ${report.database}: ${options.dryRun ? \"would apply\" : \"applied\"} ${report.actions.length} DDL statement(s)`,\n );\n }\n reports.push(report);\n }\n return reports;\n}\n"],"mappings":"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","names":["sql"]}
package/dist/deploy.cjs CHANGED
@@ -671,6 +671,8 @@ module.exports = {
671
671
  merge_logs: true,
672
672
  time: true,
673
673
  env: {
674
+ // cli-toolkit Args: ENV selects env-suffixed defaults (DB_NAME_PRODUCTION, BUCKET_PRODUCTION, \u2026)
675
+ ENV: "production",
674
676
  NODE_ENV: "production",
675
677
  },
676
678
  },