@promptowl/contextnest-community 1.5.0 → 1.6.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.
@@ -1,3 +1,7 @@
1
+ import {
2
+ ANON_USER_ID
3
+ } from "./chunk-SLTQACJW.js";
4
+
1
5
  // src/config.ts
2
6
  import { join, dirname } from "path";
3
7
  import { existsSync } from "fs";
@@ -38,6 +42,68 @@ var config = {
38
42
  get DATABASE_PATH() {
39
43
  return process.env.DATABASE_PATH || join(dataRoot(), "community.db");
40
44
  },
45
+ /**
46
+ * Root directory for nest vault files. Defaults to `<DATA_ROOT>/nests`.
47
+ * Override (e.g. a GCS volume mount on Cloud Run) to store nest files
48
+ * separately from the SQLite DB. See `src/shared/paths.ts`.
49
+ */
50
+ get NEST_STORAGE_ROOT() {
51
+ return process.env.NEST_STORAGE_ROOT || join(dataRoot(), "nests");
52
+ },
53
+ /**
54
+ * Which database backend to use.
55
+ * "sqlite" — default; the on-disk file at DATABASE_PATH (existing behaviour).
56
+ * "postgres" — Cloud SQL / PostgreSQL, for durable Cloud Run deployments.
57
+ * Explicit DB_DRIVER wins; otherwise we infer "postgres" when a connection
58
+ * is configured (DATABASE_URL or CLOUD_SQL_CONNECTION_NAME), else "sqlite".
59
+ * So existing deployments with no new env vars keep using SQLite untouched.
60
+ */
61
+ get DB_DRIVER() {
62
+ const v = (process.env.DB_DRIVER || "").trim().toLowerCase();
63
+ if (v === "postgres" || v === "postgresql" || v === "pg") return "postgres";
64
+ if (v === "sqlite") return "sqlite";
65
+ return process.env.DATABASE_URL || process.env.CLOUD_SQL_CONNECTION_NAME ? "postgres" : "sqlite";
66
+ },
67
+ /** Postgres connection string, e.g. postgres://user:pass@host:5432/db (TCP). */
68
+ get DATABASE_URL() {
69
+ return process.env.DATABASE_URL || "";
70
+ },
71
+ /**
72
+ * Cloud SQL instance connection name (project:region:instance). When set,
73
+ * the server connects over the unix socket at /cloudsql/<name> created by
74
+ * the Cloud SQL Auth Proxy (the recommended Cloud Run setup). Combine with
75
+ * DB_USER / DB_PASSWORD / DB_NAME.
76
+ */
77
+ get CLOUD_SQL_CONNECTION_NAME() {
78
+ return process.env.CLOUD_SQL_CONNECTION_NAME || "";
79
+ },
80
+ get DB_HOST() {
81
+ return process.env.DB_HOST || "";
82
+ },
83
+ get DB_PORT() {
84
+ return parseInt(process.env.DB_PORT || "5432", 10);
85
+ },
86
+ get DB_USER() {
87
+ return process.env.DB_USER || "";
88
+ },
89
+ get DB_PASSWORD() {
90
+ return process.env.DB_PASSWORD || "";
91
+ },
92
+ get DB_NAME() {
93
+ return process.env.DB_NAME || "";
94
+ },
95
+ /** Max Postgres pool connections. Keep modest — Cloud SQL tiers cap connections. */
96
+ get DB_POOL_MAX() {
97
+ return parseInt(process.env.DB_POOL_MAX || "10", 10);
98
+ },
99
+ /** Enable TLS for Postgres TCP connections (not needed over the unix socket). */
100
+ get DB_SSL() {
101
+ return process.env.DB_SSL === "true";
102
+ },
103
+ /** Optional path to a CA cert (PEM) for verify-ca/verify-full TLS. */
104
+ get DB_SSL_CA() {
105
+ return process.env.DB_SSL_CA || "";
106
+ },
41
107
  get PROMPTOWL_API_URL() {
42
108
  return process.env.PROMPTOWL_API_URL || "https://app.promptowl.ai";
43
109
  },
@@ -146,16 +212,12 @@ var config = {
146
212
 
147
213
  // src/db/client.ts
148
214
  import Database from "better-sqlite3";
149
- import { mkdirSync } from "fs";
215
+ import { mkdirSync, readFileSync } from "fs";
150
216
  import { dirname as dirname2 } from "path";
151
217
 
152
- // src/shared/constants.ts
153
- var ANON_USER_ID = "00000000-0000-0000-0000-000000000000";
154
- var ANON_EMAIL = "admin@localhost";
155
-
156
218
  // src/db/migrations.ts
157
- function runMigrations(db2) {
158
- db2.exec(`
219
+ function runMigrations(db) {
220
+ db.exec(`
159
221
  CREATE TABLE IF NOT EXISTS users (
160
222
  id TEXT PRIMARY KEY,
161
223
  email TEXT UNIQUE NOT NULL,
@@ -249,7 +311,7 @@ function runMigrations(db2) {
249
311
  sent INTEGER NOT NULL DEFAULT 0
250
312
  );
251
313
  `);
252
- db2.exec(`
314
+ db.exec(`
253
315
  -- Steward assignments (mirrors PromptOwl ContextSteward model)
254
316
  -- scope+target combination determines what the steward governs
255
317
  -- Resolution priority: document(1) > tag(2) > nest(3)
@@ -322,47 +384,47 @@ function runMigrations(db2) {
322
384
  PRIMARY KEY (nest_id, node_id)
323
385
  );
324
386
  `);
325
- const nestCols = db2.prepare("PRAGMA table_info(nests)").all().map((c) => c.name);
387
+ const nestCols = db.prepare("PRAGMA table_info(nests)").all().map((c) => c.name);
326
388
  if (!nestCols.includes("stewardship_enabled")) {
327
- db2.exec("ALTER TABLE nests ADD COLUMN stewardship_enabled INTEGER NOT NULL DEFAULT 0");
389
+ db.exec("ALTER TABLE nests ADD COLUMN stewardship_enabled INTEGER NOT NULL DEFAULT 0");
328
390
  }
329
391
  if (!nestCols.includes("is_imported")) {
330
- db2.exec("ALTER TABLE nests ADD COLUMN is_imported INTEGER NOT NULL DEFAULT 0");
392
+ db.exec("ALTER TABLE nests ADD COLUMN is_imported INTEGER NOT NULL DEFAULT 0");
331
393
  }
332
394
  if (!nestCols.includes("allow_self_approve")) {
333
- db2.exec("ALTER TABLE nests ADD COLUMN allow_self_approve INTEGER NOT NULL DEFAULT 0");
395
+ db.exec("ALTER TABLE nests ADD COLUMN allow_self_approve INTEGER NOT NULL DEFAULT 0");
334
396
  }
335
- const userCols = db2.prepare("PRAGMA table_info(users)").all().map((c) => c.name);
397
+ const userCols = db.prepare("PRAGMA table_info(users)").all().map((c) => c.name);
336
398
  if (!userCols.includes("is_admin")) {
337
- db2.exec("ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0");
399
+ db.exec("ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0");
338
400
  }
339
401
  if (!userCols.includes("is_invited")) {
340
- db2.exec("ALTER TABLE users ADD COLUMN is_invited INTEGER NOT NULL DEFAULT 0");
402
+ db.exec("ALTER TABLE users ADD COLUMN is_invited INTEGER NOT NULL DEFAULT 0");
341
403
  }
342
- const stewardCols = db2.prepare("PRAGMA table_info(stewards)").all().map((c) => c.name);
404
+ const stewardCols = db.prepare("PRAGMA table_info(stewards)").all().map((c) => c.name);
343
405
  if (stewardCols.length > 0) {
344
406
  if (!stewardCols.includes("can_approve")) {
345
- db2.exec(
407
+ db.exec(
346
408
  "ALTER TABLE stewards ADD COLUMN can_approve INTEGER NOT NULL DEFAULT 1"
347
409
  );
348
410
  }
349
411
  if (!stewardCols.includes("can_reject")) {
350
- db2.exec(
412
+ db.exec(
351
413
  "ALTER TABLE stewards ADD COLUMN can_reject INTEGER NOT NULL DEFAULT 1"
352
414
  );
353
415
  }
354
416
  }
355
- db2.exec(`
417
+ db.exec(`
356
418
  CREATE TABLE IF NOT EXISTS schema_migrations (
357
419
  id TEXT PRIMARY KEY,
358
420
  applied_at TEXT NOT NULL DEFAULT (datetime('now'))
359
421
  );
360
422
  `);
361
- const hasMigration = (id) => !!db2.prepare("SELECT id FROM schema_migrations WHERE id = ?").get(id);
362
- const recordMigration = (id) => db2.prepare("INSERT OR IGNORE INTO schema_migrations (id) VALUES (?)").run(id);
423
+ const hasMigration = (id) => !!db.prepare("SELECT id FROM schema_migrations WHERE id = ?").get(id);
424
+ const recordMigration = (id) => db.prepare("INSERT OR IGNORE INTO schema_migrations (id) VALUES (?)").run(id);
363
425
  if (!hasMigration("002_steward_parity")) {
364
- db2.transaction(() => {
365
- db2.exec(`
426
+ db.transaction(() => {
427
+ db.exec(`
366
428
  CREATE TABLE stewards_new (
367
429
  id TEXT PRIMARY KEY,
368
430
  nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
@@ -402,7 +464,7 @@ function runMigrations(db2) {
402
464
  CREATE INDEX idx_stewards_email ON stewards(user_email);
403
465
  CREATE INDEX idx_stewards_scope ON stewards(nest_id, scope);
404
466
  `);
405
- db2.exec(`
467
+ db.exec(`
406
468
  CREATE UNIQUE INDEX idx_stewards_uniq_nest
407
469
  ON stewards(nest_id, user_email)
408
470
  WHERE scope = 'nest' AND is_active = 1;
@@ -423,7 +485,7 @@ function runMigrations(db2) {
423
485
  ON stewards(nest_id, node_pattern)
424
486
  WHERE scope = 'document' AND is_active = 1;
425
487
  `);
426
- db2.exec(`
488
+ db.exec(`
427
489
  CREATE TABLE IF NOT EXISTS node_tag_index (
428
490
  nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
429
491
  node_id TEXT NOT NULL,
@@ -433,7 +495,7 @@ function runMigrations(db2) {
433
495
  CREATE INDEX IF NOT EXISTS idx_node_tag_by_tag
434
496
  ON node_tag_index(nest_id, tag_name);
435
497
  `);
436
- const rows = db2.prepare(
498
+ const rows = db.prepare(
437
499
  `SELECT nv.nest_id, nv.node_id, nv.tags_json
438
500
  FROM node_versions nv
439
501
  JOIN (
@@ -445,7 +507,7 @@ function runMigrations(db2) {
445
507
  AND latest.node_id = nv.node_id
446
508
  AND latest.v = nv.version`
447
509
  ).all();
448
- const insertTag = db2.prepare(
510
+ const insertTag = db.prepare(
449
511
  "INSERT OR IGNORE INTO node_tag_index (nest_id, node_id, tag_name) VALUES (?, ?, ?)"
450
512
  );
451
513
  for (const row of rows) {
@@ -467,8 +529,8 @@ function runMigrations(db2) {
467
529
  })();
468
530
  }
469
531
  if (!hasMigration("003_sessions_and_single_api_key")) {
470
- db2.transaction(() => {
471
- db2.exec(`
532
+ db.transaction(() => {
533
+ db.exec(`
472
534
  CREATE TABLE IF NOT EXISTS sessions (
473
535
  id TEXT PRIMARY KEY,
474
536
  user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@@ -480,7 +542,7 @@ function runMigrations(db2) {
480
542
  CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
481
543
  CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);
482
544
  `);
483
- db2.exec(`
545
+ db.exec(`
484
546
  DELETE FROM api_keys
485
547
  WHERE id IN (
486
548
  SELECT id FROM (
@@ -498,7 +560,7 @@ function runMigrations(db2) {
498
560
  WHERE rn > 1
499
561
  );
500
562
  `);
501
- db2.exec(`
563
+ db.exec(`
502
564
  CREATE UNIQUE INDEX IF NOT EXISTS idx_api_keys_user_unique
503
565
  ON api_keys(user_id);
504
566
  `);
@@ -506,25 +568,25 @@ function runMigrations(db2) {
506
568
  })();
507
569
  }
508
570
  if (!hasMigration("004_license_cache_owner_email")) {
509
- db2.transaction(() => {
510
- const cols = db2.prepare("PRAGMA table_info(license_cache)").all().map((c) => c.name);
571
+ db.transaction(() => {
572
+ const cols = db.prepare("PRAGMA table_info(license_cache)").all().map((c) => c.name);
511
573
  if (!cols.includes("owner_email")) {
512
- db2.exec("ALTER TABLE license_cache ADD COLUMN owner_email TEXT");
574
+ db.exec("ALTER TABLE license_cache ADD COLUMN owner_email TEXT");
513
575
  }
514
576
  recordMigration("004_license_cache_owner_email");
515
577
  })();
516
578
  }
517
579
  if (!hasMigration("005_anon_nest_public_default")) {
518
- db2.transaction(() => {
519
- db2.prepare(
580
+ db.transaction(() => {
581
+ db.prepare(
520
582
  "UPDATE nests SET visibility = 'public' WHERE user_id = ? AND visibility = 'private'"
521
583
  ).run(ANON_USER_ID);
522
584
  recordMigration("005_anon_nest_public_default");
523
585
  })();
524
586
  }
525
587
  if (!hasMigration("006_node_versions_published_status")) {
526
- db2.transaction(() => {
527
- db2.exec(`
588
+ db.transaction(() => {
589
+ db.exec(`
528
590
  CREATE TABLE node_versions_new (
529
591
  id INTEGER PRIMARY KEY AUTOINCREMENT,
530
592
  nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
@@ -552,15 +614,15 @@ function runMigrations(db2) {
552
614
  })();
553
615
  }
554
616
  if (!hasMigration("007_drop_steward_capability_flags")) {
555
- const stewardCols2 = db2.prepare("PRAGMA table_info(stewards)").all().map((c) => c.name);
617
+ const stewardCols2 = db.prepare("PRAGMA table_info(stewards)").all().map((c) => c.name);
556
618
  const hasLegacyCols = stewardCols2.includes("can_approve") || stewardCols2.includes("can_reject");
557
619
  if (hasLegacyCols) {
558
- db2.transaction(() => {
620
+ db.transaction(() => {
559
621
  if (stewardCols2.includes("can_approve")) {
560
- db2.exec("ALTER TABLE stewards DROP COLUMN can_approve");
622
+ db.exec("ALTER TABLE stewards DROP COLUMN can_approve");
561
623
  }
562
624
  if (stewardCols2.includes("can_reject")) {
563
- db2.exec("ALTER TABLE stewards DROP COLUMN can_reject");
625
+ db.exec("ALTER TABLE stewards DROP COLUMN can_reject");
564
626
  }
565
627
  recordMigration("007_drop_steward_capability_flags");
566
628
  })();
@@ -569,13 +631,13 @@ function runMigrations(db2) {
569
631
  }
570
632
  }
571
633
  if (!hasMigration("008_drop_steward_folder_scope")) {
572
- db2.transaction(() => {
573
- db2.exec("DELETE FROM stewards WHERE scope = 'folder'");
574
- db2.exec("DROP INDEX IF EXISTS idx_stewards_uniq_folder");
575
- db2.exec("DROP INDEX IF EXISTS idx_stewards_folder_lookup");
576
- const tbl = db2.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='stewards'").get();
634
+ db.transaction(() => {
635
+ db.exec("DELETE FROM stewards WHERE scope = 'folder'");
636
+ db.exec("DROP INDEX IF EXISTS idx_stewards_uniq_folder");
637
+ db.exec("DROP INDEX IF EXISTS idx_stewards_folder_lookup");
638
+ const tbl = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='stewards'").get();
577
639
  if (tbl?.sql && tbl.sql.includes("'folder'")) {
578
- db2.exec(`
640
+ db.exec(`
579
641
  CREATE TABLE stewards_new (
580
642
  id TEXT PRIMARY KEY,
581
643
  nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
@@ -624,8 +686,8 @@ function runMigrations(db2) {
624
686
  })();
625
687
  }
626
688
  if (!hasMigration("009_lowercase_emails")) {
627
- db2.transaction(() => {
628
- const collisions = db2.prepare(
689
+ db.transaction(() => {
690
+ const collisions = db.prepare(
629
691
  `SELECT GROUP_CONCAT(email, ', ') AS emails
630
692
  FROM users GROUP BY LOWER(email) HAVING COUNT(*) > 1`
631
693
  ).all();
@@ -634,20 +696,20 @@ function runMigrations(db2) {
634
696
  `[migration 009] case-collision user rows NOT auto-merged: ${c.emails} \u2014 reconcile manually (pick the row the person logs into; reset its password; ensure collaborator/steward grants point at that user_id).`
635
697
  );
636
698
  }
637
- db2.exec(
699
+ db.exec(
638
700
  `UPDATE users SET email = LOWER(email)
639
701
  WHERE email <> LOWER(email)
640
702
  AND LOWER(email) NOT IN (
641
703
  SELECT LOWER(email) FROM users GROUP BY LOWER(email) HAVING COUNT(*) > 1
642
704
  )`
643
705
  );
644
- const remaining = db2.prepare(
706
+ const remaining = db.prepare(
645
707
  `SELECT COUNT(*) AS c FROM (
646
708
  SELECT 1 FROM users GROUP BY LOWER(email) HAVING COUNT(*) > 1
647
709
  )`
648
710
  ).get().c;
649
711
  if (remaining === 0) {
650
- db2.exec(
712
+ db.exec(
651
713
  "CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_nocase ON users(email COLLATE NOCASE)"
652
714
  );
653
715
  } else {
@@ -659,8 +721,8 @@ function runMigrations(db2) {
659
721
  recordMigration("009_lowercase_emails");
660
722
  }
661
723
  if (!hasMigration("010_sso_used_jti")) {
662
- db2.transaction(() => {
663
- db2.exec(`
724
+ db.transaction(() => {
725
+ db.exec(`
664
726
  CREATE TABLE IF NOT EXISTS sso_used_jti (
665
727
  jti TEXT PRIMARY KEY,
666
728
  used_at TEXT NOT NULL DEFAULT (datetime('now')),
@@ -673,8 +735,8 @@ function runMigrations(db2) {
673
735
  recordMigration("010_sso_used_jti");
674
736
  }
675
737
  if (!hasMigration("011_comments")) {
676
- db2.transaction(() => {
677
- db2.exec(`
738
+ db.transaction(() => {
739
+ db.exec(`
678
740
  CREATE TABLE IF NOT EXISTS comments (
679
741
  id TEXT PRIMARY KEY,
680
742
  nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
@@ -704,23 +766,150 @@ function runMigrations(db2) {
704
766
  }
705
767
  }
706
768
 
769
+ // src/db/adapter.sqlite.ts
770
+ var SqliteAdapter = class {
771
+ constructor(db) {
772
+ this.db = db;
773
+ }
774
+ db;
775
+ dialect = "sqlite";
776
+ // better-sqlite3 is one synchronous connection. Once transaction bodies are
777
+ // async (they may `await` between statements), two concurrent bodies could
778
+ // interleave their BEGIN/COMMIT on the shared connection and corrupt
779
+ // atomicity. This promise chain serialises transactions in-process,
780
+ // restoring the run-to-completion guarantee better-sqlite3's own
781
+ // `.transaction()` gave us synchronously.
782
+ txChain = Promise.resolve();
783
+ async get(sql, params = []) {
784
+ return this.db.prepare(sql).get(...params);
785
+ }
786
+ async all(sql, params = []) {
787
+ return this.db.prepare(sql).all(...params);
788
+ }
789
+ async run(sql, params = []) {
790
+ const info = this.db.prepare(sql).run(...params);
791
+ return { changes: info.changes };
792
+ }
793
+ async exec(sql) {
794
+ this.db.exec(sql);
795
+ }
796
+ async transaction(fn) {
797
+ const run = async () => {
798
+ this.db.exec("BEGIN IMMEDIATE");
799
+ try {
800
+ const result2 = await fn(this);
801
+ this.db.exec("COMMIT");
802
+ return result2;
803
+ } catch (err) {
804
+ try {
805
+ this.db.exec("ROLLBACK");
806
+ } catch {
807
+ }
808
+ throw err;
809
+ }
810
+ };
811
+ const result = this.txChain.then(run, run);
812
+ this.txChain = result.then(
813
+ () => void 0,
814
+ () => void 0
815
+ );
816
+ return result;
817
+ }
818
+ async close() {
819
+ this.db.close();
820
+ }
821
+ };
822
+
707
823
  // src/db/client.ts
708
- var db = null;
824
+ var adapter = null;
825
+ function buildPgConfig() {
826
+ const base = {
827
+ max: config.DB_POOL_MAX,
828
+ idleTimeoutMillis: 3e4,
829
+ connectionTimeoutMillis: 1e4,
830
+ keepAlive: true
831
+ };
832
+ if (config.CLOUD_SQL_CONNECTION_NAME) {
833
+ return {
834
+ ...base,
835
+ host: `/cloudsql/${config.CLOUD_SQL_CONNECTION_NAME}`,
836
+ user: config.DB_USER || void 0,
837
+ password: config.DB_PASSWORD || void 0,
838
+ database: config.DB_NAME || void 0
839
+ };
840
+ }
841
+ if (config.DATABASE_URL) {
842
+ return { ...base, connectionString: config.DATABASE_URL, ssl: pgSsl() };
843
+ }
844
+ return {
845
+ ...base,
846
+ host: config.DB_HOST || "localhost",
847
+ port: config.DB_PORT,
848
+ user: config.DB_USER || void 0,
849
+ password: config.DB_PASSWORD || void 0,
850
+ database: config.DB_NAME || void 0,
851
+ ssl: pgSsl()
852
+ };
853
+ }
854
+ function pgSsl() {
855
+ if (!config.DB_SSL) return false;
856
+ const ssl = {
857
+ rejectUnauthorized: true
858
+ };
859
+ if (config.DB_SSL_CA) ssl.ca = readFileSync(config.DB_SSL_CA, "utf8");
860
+ return ssl;
861
+ }
862
+ function openSqlite() {
863
+ mkdirSync(dirname2(config.DATABASE_PATH), { recursive: true });
864
+ const raw = new Database(config.DATABASE_PATH);
865
+ raw.pragma("journal_mode = WAL");
866
+ raw.pragma("foreign_keys = ON");
867
+ raw.pragma("busy_timeout = 5000");
868
+ runMigrations(raw);
869
+ return new SqliteAdapter(raw);
870
+ }
871
+ async function initDb() {
872
+ if (adapter) return adapter;
873
+ if (config.DB_DRIVER === "postgres") {
874
+ const { Pool } = await import("pg");
875
+ const { PostgresAdapter } = await import("./adapter.postgres-YOODX2BI.js");
876
+ const { runPostgresMigrations } = await import("./migrations.postgres-NVJAGBSF.js");
877
+ const pool = new Pool(buildPgConfig());
878
+ adapter = new PostgresAdapter(pool);
879
+ await runPostgresMigrations(adapter);
880
+ } else {
881
+ adapter = openSqlite();
882
+ }
883
+ return adapter;
884
+ }
709
885
  function getDb() {
710
- if (!db) {
711
- mkdirSync(dirname2(config.DATABASE_PATH), { recursive: true });
712
- db = new Database(config.DATABASE_PATH);
713
- db.pragma("journal_mode = WAL");
714
- db.pragma("foreign_keys = ON");
715
- db.pragma("busy_timeout = 5000");
716
- runMigrations(db);
886
+ if (!adapter) {
887
+ if (config.DB_DRIVER === "postgres") {
888
+ throw new Error(
889
+ "Postgres backend not initialised \u2014 call (and await) initDb() at startup before getDb()."
890
+ );
891
+ }
892
+ adapter = openSqlite();
717
893
  }
718
- return db;
894
+ return adapter;
895
+ }
896
+
897
+ // src/db/sql.ts
898
+ function nowExpr(db) {
899
+ return db.dialect === "sqlite" ? "datetime('now')" : "to_char((now() AT TIME ZONE 'utc'), 'YYYY-MM-DD HH24:MI:SS')";
900
+ }
901
+ function insertOrIgnore(db, insertSql) {
902
+ return db.dialect === "sqlite" ? insertSql.replace(/^\s*INSERT\s+INTO/i, "INSERT OR IGNORE INTO") : `${insertSql} ON CONFLICT DO NOTHING`;
903
+ }
904
+ function insertOrReplace(db, insertSql, conflictCols, set) {
905
+ return db.dialect === "sqlite" ? insertSql.replace(/^\s*INSERT\s+INTO/i, "INSERT OR REPLACE INTO") : `${insertSql} ON CONFLICT (${conflictCols.join(", ")}) DO UPDATE SET ${set}`;
719
906
  }
720
907
 
721
908
  export {
722
909
  config,
723
- ANON_USER_ID,
724
- ANON_EMAIL,
725
- getDb
910
+ initDb,
911
+ getDb,
912
+ nowExpr,
913
+ insertOrIgnore,
914
+ insertOrReplace
726
915
  };