@hasna/mementos 0.14.47 → 0.14.49

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.
Files changed (38) hide show
  1. package/Dockerfile.package +32 -0
  2. package/bun.lock +405 -0
  3. package/dist/cli/index.js +141 -70
  4. package/dist/db/pg-migrations.d.ts.map +1 -1
  5. package/dist/generated/storage-kit/health.d.ts +20 -0
  6. package/dist/generated/storage-kit/health.d.ts.map +1 -0
  7. package/dist/generated/storage-kit/index.d.ts +8 -0
  8. package/dist/generated/storage-kit/index.d.ts.map +1 -0
  9. package/dist/generated/storage-kit/migrations.d.ts +48 -0
  10. package/dist/generated/storage-kit/migrations.d.ts.map +1 -0
  11. package/dist/generated/storage-kit/mode.d.ts +48 -0
  12. package/dist/generated/storage-kit/mode.d.ts.map +1 -0
  13. package/dist/generated/storage-kit/pool.d.ts +34 -0
  14. package/dist/generated/storage-kit/pool.d.ts.map +1 -0
  15. package/dist/generated/storage-kit/query.d.ts +36 -0
  16. package/dist/generated/storage-kit/query.d.ts.map +1 -0
  17. package/dist/generated/storage-kit/tls.d.ts +26 -0
  18. package/dist/generated/storage-kit/tls.d.ts.map +1 -0
  19. package/dist/index.js +96 -64
  20. package/dist/mcp/index.js +135 -64
  21. package/dist/pg-sync-worker.d.ts +2 -0
  22. package/dist/pg-sync-worker.d.ts.map +1 -0
  23. package/dist/pg-sync-worker.js +47 -0
  24. package/dist/sdk/index.d.ts +27 -0
  25. package/dist/sdk/index.d.ts.map +1 -1
  26. package/dist/sdk/index.js +25 -6
  27. package/dist/server/auth.d.ts +11 -0
  28. package/dist/server/auth.d.ts.map +1 -0
  29. package/dist/server/index.d.ts.map +1 -1
  30. package/dist/server/index.js +362 -129
  31. package/dist/server/openapi.d.ts +2 -0
  32. package/dist/server/openapi.d.ts.map +1 -0
  33. package/dist/storage.d.ts +36 -3
  34. package/dist/storage.d.ts.map +1 -1
  35. package/dist/storage.js +99 -64
  36. package/docker-entrypoint.sh +46 -0
  37. package/hasna.contract.json +16 -0
  38. package/package.json +7 -3
package/dist/cli/index.js CHANGED
@@ -2104,6 +2104,8 @@ import { Database } from "bun:sqlite";
2104
2104
  import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
2105
2105
  import { homedir as homedir2 } from "os";
2106
2106
  import { join as join2 } from "path";
2107
+ import { fileURLToPath } from "url";
2108
+ import { Worker } from "worker_threads";
2107
2109
  import pg from "pg";
2108
2110
  function normalizeParams(params) {
2109
2111
  const flat = params.length === 1 && Array.isArray(params[0]) ? params[0] : params;
@@ -2166,13 +2168,15 @@ class SqliteAdapter {
2166
2168
  function translateSql(sql) {
2167
2169
  let parameterIndex = 0;
2168
2170
  let translated = sql.replace(/\?/g, () => `$${++parameterIndex}`);
2169
- translated = translated.replace(/datetime\s*\(\s*'now'\s*\)/gi, "NOW()");
2171
+ const ISO_FMT = `'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'`;
2172
+ translated = translated.replace(/datetime\s*\(\s*'now'\s*\)/gi, `to_char(now() AT TIME ZONE 'UTC', ${ISO_FMT})`);
2170
2173
  translated = translated.replace(/datetime\s*\(\s*'now'\s*,\s*'(-?\d+)\s+(minutes?|hours?|days?|seconds?)'\s*\)/gi, (_match, amount, unit) => {
2171
2174
  const parsed = parseInt(String(amount), 10);
2172
2175
  const absolute = Math.abs(parsed);
2173
2176
  const normalizedUnit = String(unit).toLowerCase().replace(/s$/, "");
2174
2177
  const pluralUnit = absolute === 1 ? normalizedUnit : `${normalizedUnit}s`;
2175
- return parsed < 0 ? `NOW() - INTERVAL '${absolute} ${pluralUnit}'` : `NOW() + INTERVAL '${absolute} ${pluralUnit}'`;
2178
+ const op = parsed < 0 ? "-" : "+";
2179
+ return `to_char((now() ${op} INTERVAL '${absolute} ${pluralUnit}') AT TIME ZONE 'UTC', ${ISO_FMT})`;
2176
2180
  });
2177
2181
  translated = translated.replace(/lower\s*\(\s*hex\s*\(\s*randomblob\s*\(\s*\d+\s*\)\s*\)\s*\)/gi, "gen_random_uuid()::text");
2178
2182
  translated = translated.replace(/\bIFNULL\s*\(/gi, "COALESCE(");
@@ -2229,56 +2233,24 @@ function makePool(connectionString) {
2229
2233
  class PgAdapter {
2230
2234
  pool;
2231
2235
  constructor(input) {
2232
- this.pool = typeof input === "string" ? makePool(input) : input;
2233
- }
2234
- runSync(fn) {
2235
- let result;
2236
- let error;
2237
- let done = false;
2238
- fn().then((value) => {
2239
- result = value;
2240
- done = true;
2241
- }).catch((caught) => {
2242
- error = caught;
2243
- done = true;
2244
- });
2245
- const deadline = Date.now() + 30000;
2246
- while (!done && Date.now() < deadline) {
2247
- Bun.sleepSync(1);
2248
- }
2249
- if (error) {
2250
- throw error;
2251
- }
2252
- if (!done) {
2253
- throw new Error("PostgreSQL query timed out after 30s");
2254
- }
2255
- return result;
2236
+ this.pool = typeof input === "string" ? new PgSyncPool(input) : input;
2256
2237
  }
2257
2238
  run(sql, ...params) {
2258
- return this.runSync(async () => {
2259
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
2260
- return {
2261
- changes: result.rowCount ?? 0,
2262
- lastInsertRowid: result.rows?.[0]?.id ?? 0
2263
- };
2264
- });
2239
+ const result = this.pool.query(translateSql(sql), normalizeParams(params));
2240
+ return {
2241
+ changes: result.rowCount ?? 0,
2242
+ lastInsertRowid: result.rows?.[0]?.id ?? 0
2243
+ };
2265
2244
  }
2266
2245
  get(sql, ...params) {
2267
- return this.runSync(async () => {
2268
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
2269
- return result.rows[0] ?? null;
2270
- });
2246
+ const result = this.pool.query(translateSql(sql), normalizeParams(params));
2247
+ return result.rows[0] ?? null;
2271
2248
  }
2272
2249
  all(sql, ...params) {
2273
- return this.runSync(async () => {
2274
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
2275
- return result.rows;
2276
- });
2250
+ return this.pool.query(translateSql(sql), normalizeParams(params)).rows;
2277
2251
  }
2278
2252
  exec(sql) {
2279
- this.runSync(async () => {
2280
- await this.pool.query(sql);
2281
- });
2253
+ this.pool.query(sql, []);
2282
2254
  }
2283
2255
  prepare(sql) {
2284
2256
  return {
@@ -2292,28 +2264,20 @@ class PgAdapter {
2292
2264
  return this.prepare(sql);
2293
2265
  }
2294
2266
  close() {
2295
- this.runSync(async () => {
2296
- await this.pool.end();
2297
- });
2267
+ this.pool.end();
2298
2268
  }
2299
2269
  transaction(fn) {
2300
- return this.runSync(async () => {
2301
- const client = await this.pool.connect();
2302
- const originalQuery = this.pool.query.bind(this.pool);
2270
+ this.pool.query("BEGIN", []);
2271
+ try {
2272
+ const value = fn();
2273
+ this.pool.query("COMMIT", []);
2274
+ return value;
2275
+ } catch (error) {
2303
2276
  try {
2304
- await client.query("BEGIN");
2305
- this.pool.query = client.query.bind(client);
2306
- const value = fn();
2307
- await client.query("COMMIT");
2308
- return value;
2309
- } catch (error) {
2310
- await client.query("ROLLBACK");
2311
- throw error;
2312
- } finally {
2313
- this.pool.query = originalQuery;
2314
- client.release();
2315
- }
2316
- });
2277
+ this.pool.query("ROLLBACK", []);
2278
+ } catch {}
2279
+ throw error;
2280
+ }
2317
2281
  }
2318
2282
  get raw() {
2319
2283
  return this.pool;
@@ -2595,7 +2559,7 @@ function getSyncMetaAll(db) {
2595
2559
  ensureSyncMetaTable(db);
2596
2560
  return db.all("SELECT table_name, last_synced_at, last_synced_row_count, direction FROM _sync_meta ORDER BY table_name");
2597
2561
  }
2598
- var MEMENTOS_STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES, MODE_ENV_NAMES, warnedDeprecatedModes, SYNC_EXCLUDED_TABLE_PATTERNS, SYNC_META_TABLE_SQL = `
2562
+ var PgSyncPool, MEMENTOS_STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES, MODE_ENV_NAMES, warnedDeprecatedModes, SYNC_EXCLUDED_TABLE_PATTERNS, SYNC_META_TABLE_SQL = `
2599
2563
  CREATE TABLE IF NOT EXISTS _sync_meta (
2600
2564
  table_name TEXT PRIMARY KEY,
2601
2565
  last_synced_at TEXT,
@@ -2603,6 +2567,74 @@ CREATE TABLE IF NOT EXISTS _sync_meta (
2603
2567
  direction TEXT DEFAULT 'push'
2604
2568
  )`;
2605
2569
  var init_storage = __esm(() => {
2570
+ PgSyncPool = class PgSyncPool {
2571
+ worker;
2572
+ status;
2573
+ data;
2574
+ closed = false;
2575
+ lastError = null;
2576
+ static DATA_BYTES = 128 * 1024 * 1024;
2577
+ static QUERY_TIMEOUT_MS = 60000;
2578
+ static resolveWorkerPath() {
2579
+ const ext = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
2580
+ const here = fileURLToPath(new URL(".", import.meta.url));
2581
+ const candidates = [
2582
+ join2(here, `pg-sync-worker${ext}`),
2583
+ join2(here, "..", `pg-sync-worker${ext}`),
2584
+ join2(here, "..", "..", `pg-sync-worker${ext}`)
2585
+ ];
2586
+ for (const candidate of candidates) {
2587
+ if (existsSync2(candidate))
2588
+ return candidate;
2589
+ }
2590
+ return candidates[0];
2591
+ }
2592
+ constructor(connectionString) {
2593
+ const control = new SharedArrayBuffer(8);
2594
+ const dataSab = new SharedArrayBuffer(PgSyncPool.DATA_BYTES);
2595
+ this.status = new Int32Array(control);
2596
+ this.data = new Uint8Array(dataSab);
2597
+ this.worker = new Worker(PgSyncPool.resolveWorkerPath(), {
2598
+ workerData: {
2599
+ dsn: stripSslParams(connectionString),
2600
+ ssl: sslConfigFor(connectionString),
2601
+ control,
2602
+ data: dataSab
2603
+ }
2604
+ });
2605
+ this.worker.unref();
2606
+ this.worker.on("error", (err) => {
2607
+ this.lastError = err;
2608
+ });
2609
+ }
2610
+ query(sql, params) {
2611
+ if (this.closed)
2612
+ throw new Error("PgSyncPool is closed");
2613
+ if (this.lastError)
2614
+ throw this.lastError;
2615
+ Atomics.store(this.status, 0, 0);
2616
+ this.worker.postMessage({ sql, params });
2617
+ const waitResult = Atomics.wait(this.status, 0, 0, PgSyncPool.QUERY_TIMEOUT_MS);
2618
+ const code = Atomics.load(this.status, 0);
2619
+ if (code === 0 || waitResult === "timed-out") {
2620
+ if (this.lastError)
2621
+ throw this.lastError;
2622
+ throw new Error("PostgreSQL query timed out after 60s");
2623
+ }
2624
+ const len = Atomics.load(this.status, 1);
2625
+ const payload = JSON.parse(new TextDecoder().decode(this.data.subarray(0, len)));
2626
+ if (code === 2) {
2627
+ throw new Error(payload.message ?? "PostgreSQL error");
2628
+ }
2629
+ return payload;
2630
+ }
2631
+ end() {
2632
+ if (this.closed)
2633
+ return;
2634
+ this.closed = true;
2635
+ this.worker.terminate();
2636
+ }
2637
+ };
2606
2638
  MEMENTOS_STORAGE_TABLES = [
2607
2639
  "projects",
2608
2640
  "agents",
@@ -4988,10 +5020,10 @@ __export(exports_helpers, {
4988
5020
  import chalk from "chalk";
4989
5021
  import { readFileSync as readFileSync2 } from "fs";
4990
5022
  import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
4991
- import { fileURLToPath } from "url";
5023
+ import { fileURLToPath as fileURLToPath2 } from "url";
4992
5024
  function getPackageVersion() {
4993
5025
  try {
4994
- const pkgPath = join4(dirname2(fileURLToPath(import.meta.url)), "..", "..", "package.json");
5026
+ const pkgPath = join4(dirname2(fileURLToPath2(import.meta.url)), "..", "..", "package.json");
4995
5027
  const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
4996
5028
  return pkg.version || "0.0.0";
4997
5029
  } catch {
@@ -10781,6 +10813,45 @@ var init_pg_migrations = __esm(() => {
10781
10813
  machine_id TEXT,
10782
10814
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
10783
10815
  );
10816
+ `,
10817
+ `
10818
+ CREATE TABLE IF NOT EXISTS tasks (
10819
+ id TEXT PRIMARY KEY,
10820
+ subject TEXT NOT NULL,
10821
+ description TEXT DEFAULT '',
10822
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','in_progress','completed','failed','cancelled')),
10823
+ priority TEXT NOT NULL DEFAULT 'medium' CHECK(priority IN ('critical','high','medium','low')),
10824
+ tags TEXT NOT NULL DEFAULT '[]',
10825
+ assigned_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
10826
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
10827
+ session_id TEXT,
10828
+ parent_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
10829
+ metadata TEXT NOT NULL DEFAULT '{}',
10830
+ progress REAL NOT NULL DEFAULT 0 CHECK(progress >= 0 AND progress <= 1),
10831
+ due_at TEXT,
10832
+ started_at TEXT,
10833
+ completed_at TEXT,
10834
+ failed_at TEXT,
10835
+ error TEXT,
10836
+ created_at TEXT NOT NULL DEFAULT to_char((now() AT TIME ZONE 'UTC'), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
10837
+ updated_at TEXT NOT NULL DEFAULT to_char((now() AT TIME ZONE 'UTC'), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
10838
+ );
10839
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
10840
+ CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
10841
+ CREATE INDEX IF NOT EXISTS idx_tasks_agent ON tasks(assigned_agent_id);
10842
+ CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
10843
+ CREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_id);
10844
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id);
10845
+
10846
+ CREATE TABLE IF NOT EXISTS task_comments (
10847
+ id TEXT PRIMARY KEY,
10848
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
10849
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
10850
+ body TEXT NOT NULL,
10851
+ created_at TEXT NOT NULL DEFAULT to_char((now() AT TIME ZONE 'UTC'), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
10852
+ );
10853
+ CREATE INDEX IF NOT EXISTS idx_task_comments_task ON task_comments(task_id);
10854
+ CREATE INDEX IF NOT EXISTS idx_task_comments_agent ON task_comments(agent_id);
10784
10855
  `
10785
10856
  ];
10786
10857
  });
@@ -59335,7 +59406,7 @@ function collectValues(value, previous) {
59335
59406
  init_database();
59336
59407
  import { readFileSync as readFileSync8 } from "fs";
59337
59408
  import { dirname as dirname7, join as join11 } from "path";
59338
- import { fileURLToPath as fileURLToPath3 } from "url";
59409
+ import { fileURLToPath as fileURLToPath4 } from "url";
59339
59410
 
59340
59411
  // src/db/machines.ts
59341
59412
  init_database();
@@ -64521,7 +64592,7 @@ import {
64521
64592
  } from "fs";
64522
64593
  import { dirname as dirname6, join as join8 } from "path";
64523
64594
  import { homedir as homedir5 } from "os";
64524
- import { fileURLToPath as fileURLToPath2 } from "url";
64595
+ import { fileURLToPath as fileURLToPath3 } from "url";
64525
64596
  function registerInitCommand(program2) {
64526
64597
  program2.command("init").description("One-command setup: register MCP, install stop hook, configure auto-start").action(async () => {
64527
64598
  const { platform: platform2 } = process;
@@ -64605,7 +64676,7 @@ function registerInitCommand(program2) {
64605
64676
  mkdirSync6(hooksDir, { recursive: true });
64606
64677
  }
64607
64678
  if (!existsSync9(hookDest)) {
64608
- const packageDir = dirname6(dirname6(fileURLToPath2(import.meta.url)));
64679
+ const packageDir = dirname6(dirname6(fileURLToPath3(import.meta.url)));
64609
64680
  const candidatePaths = [
64610
64681
  join8(packageDir, "scripts", "hooks", "claude-stop-hook.ts"),
64611
64682
  join8(packageDir, "..", "scripts", "hooks", "claude-stop-hook.ts"),
@@ -66102,7 +66173,7 @@ function makeBrainsCommand() {
66102
66173
  // src/cli/index.tsx
66103
66174
  function getPackageVersion2() {
66104
66175
  try {
66105
- const pkgPath = join11(dirname7(fileURLToPath3(import.meta.url)), "..", "..", "package.json");
66176
+ const pkgPath = join11(dirname7(fileURLToPath4(import.meta.url)), "..", "..", "package.json");
66106
66177
  const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
66107
66178
  return pkg.version || "0.0.0";
66108
66179
  } catch {
@@ -1 +1 @@
1
- {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EAutBjC,CAAC"}
1
+ {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EAkwBjC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { TypedQueryClient } from "./query.js";
2
+ import { type Migration, type MigrationRunnerOptions } from "./migrations.js";
3
+ export interface HealthResult {
4
+ ok: boolean;
5
+ /** Round-trip latency of the probe query, in milliseconds. */
6
+ latencyMs: number;
7
+ error?: string;
8
+ }
9
+ /** Cheap reachability probe: `SELECT 1`. Never throws — reports `ok: false`. */
10
+ export declare function checkHealth(client: TypedQueryClient): Promise<HealthResult>;
11
+ export interface ReadyResult extends HealthResult {
12
+ /** Migration ids that are defined but not yet applied. */
13
+ pendingMigrations: string[];
14
+ }
15
+ /**
16
+ * Readiness probe: reachable AND fully migrated. Reports `ok: false` with the
17
+ * list of pending migration ids when the schema is behind.
18
+ */
19
+ export declare function checkReady(client: TypedQueryClient, migrations: readonly Migration[], options?: MigrationRunnerOptions): Promise<ReadyResult>;
20
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../src/generated/storage-kit/health.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE/F,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,gFAAgF;AAChF,wBAAsB,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAYjF;AAED,MAAM,WAAW,WAAY,SAAQ,YAAY;IAC/C,0DAA0D;IAC1D,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,WAAW,CAAC,CAetB"}
@@ -0,0 +1,8 @@
1
+ export declare const KIT_VERSION = "0.4.1";
2
+ export * from "./mode.js";
3
+ export * from "./tls.js";
4
+ export * from "./query.js";
5
+ export * from "./pool.js";
6
+ export * from "./migrations.js";
7
+ export * from "./health.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/generated/storage-kit/index.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { TypedQueryClient } from "./query.js";
2
+ /** Default ledger table name. Override per app if a legacy name exists. */
3
+ export declare const DEFAULT_MIGRATION_LEDGER_TABLE = "schema_migrations";
4
+ export interface Migration {
5
+ readonly id: string;
6
+ readonly sql: string;
7
+ readonly checksum: string;
8
+ }
9
+ export type MigrationState = "already_applied" | "pending";
10
+ export interface MigrationPlanItem {
11
+ readonly migration: Migration;
12
+ readonly state: MigrationState;
13
+ }
14
+ export interface AppliedMigration {
15
+ readonly id: string;
16
+ readonly checksum: string;
17
+ readonly appliedAt: string;
18
+ }
19
+ export interface MigrationResult {
20
+ readonly dryRun: boolean;
21
+ readonly applied: AppliedMigration[];
22
+ readonly plan: MigrationPlanItem[];
23
+ }
24
+ /** Stable sha256 checksum for a migration's SQL text. */
25
+ export declare function checksumSql(sql: string): string;
26
+ /** Freeze a migration definition, computing its checksum from the SQL. */
27
+ export declare function defineMigration(id: string, sql: string): Migration;
28
+ export interface MigrationRunnerOptions {
29
+ ledgerTable?: string;
30
+ }
31
+ export declare class MigrationLedger {
32
+ private readonly client;
33
+ private readonly migrations;
34
+ private readonly ledgerTable;
35
+ constructor(client: TypedQueryClient, migrations: readonly Migration[], options?: MigrationRunnerOptions);
36
+ ensureLedger(): Promise<void>;
37
+ listApplied(): Promise<AppliedMigration[]>;
38
+ private readApplied;
39
+ /** Compute the migration plan and guard against drift/downgrade. */
40
+ private buildPlan;
41
+ /** Apply all pending migrations. With `dryRun`, report the plan only. */
42
+ migrate(opts?: {
43
+ dryRun?: boolean;
44
+ }): Promise<MigrationResult>;
45
+ }
46
+ /** Convenience: build a ledger and run all pending migrations. */
47
+ export declare function createMigrationLedger(client: TypedQueryClient, migrations: readonly Migration[], options?: MigrationRunnerOptions): MigrationLedger;
48
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../src/generated/storage-kit/migrations.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,2EAA2E;AAC3E,eAAO,MAAM,8BAA8B,sBAAsB,CAAC;AAElE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC;CACpC;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAElE;AAQD,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,eAAe;IAIxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAGlB,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,SAAS,SAAS,EAAE,EACjD,OAAO,GAAE,sBAA2B;IAUhC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B,WAAW,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAKlC,WAAW;IAWzB,oEAAoE;IACpE,OAAO,CAAC,SAAS;IAsBjB,yEAAyE;IACnE,OAAO,CAAC,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,eAAe,CAAC;CAiBzE;AAED,kEAAkE;AAClE,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,OAAO,GAAE,sBAA2B,GACnC,eAAe,CAEjB"}
@@ -0,0 +1,48 @@
1
+ export declare const STORAGE_MODES: readonly ["local", "cloud"];
2
+ export type StorageMode = (typeof STORAGE_MODES)[number];
3
+ export declare const DEPRECATED_STORAGE_MODE_ALIASES: readonly ["remote", "hybrid", "self_hosted"];
4
+ export type Env = Record<string, string | undefined>;
5
+ export interface StorageModeNormalization {
6
+ mode: StorageMode;
7
+ /** The deprecated alias that was normalized to `cloud`, if any. */
8
+ deprecatedAlias: string | null;
9
+ }
10
+ /**
11
+ * Normalize a raw storage-mode string to the `local | cloud` runtime enum.
12
+ * Accepts deprecated aliases (`remote`, `hybrid`, `self_hosted`) and maps them
13
+ * to `cloud`. Throws on any other value.
14
+ */
15
+ export declare function normalizeStorageMode(value: string): StorageModeNormalization;
16
+ /** Upper-snake env token for an app name, e.g. `todos` -> `TODOS`. */
17
+ export declare function envToken(name: string): string;
18
+ export interface StorageEnvKeys {
19
+ /** `HASNA_<NAME>_STORAGE_MODE` then the optional `<NAME>_STORAGE_MODE` alias. */
20
+ modeKeys: string[];
21
+ /** `HASNA_<NAME>_DATABASE_URL` then the optional `<NAME>_DATABASE_URL` alias. */
22
+ databaseUrlKeys: string[];
23
+ }
24
+ /** Resolve the canonical env-key spec for an app's storage config. */
25
+ export declare function storageEnvKeys(name: string): StorageEnvKeys;
26
+ export interface StorageModeResolution {
27
+ mode: StorageMode;
28
+ /** Env key the mode came from, or `"default"`. */
29
+ source: string;
30
+ deprecatedAlias: string | null;
31
+ databaseUrlPresent: boolean;
32
+ /** Env key the database URL came from, or `null`. */
33
+ databaseUrlSource: string | null;
34
+ warning: string | null;
35
+ }
36
+ /**
37
+ * Resolve an app's storage mode from the environment per the contract env spec.
38
+ * Precedence: `HASNA_<NAME>_STORAGE_MODE`, then `<NAME>_STORAGE_MODE`, else
39
+ * `local`. Never reads secret values — only detects DATABASE_URL presence.
40
+ */
41
+ export declare function resolveStorageMode(name: string, env?: Env): StorageModeResolution;
42
+ /**
43
+ * Resolve the database URL value for an app, honoring the canonical then alias
44
+ * env keys. Returns `null` when unset. The caller is responsible for never
45
+ * logging the returned value.
46
+ */
47
+ export declare function resolveDatabaseUrl(name: string, env?: Env): string | null;
48
+ //# sourceMappingURL=mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../../../src/generated/storage-kit/mode.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,aAAa,6BAA8B,CAAC;AACzD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,+BAA+B,8CAIlC,CAAC;AAEX,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,WAAW,CAAC;IAClB,mEAAmE;IACnE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB,CAQ5E;AAED,sEAAsE;AACtE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iFAAiF;IACjF,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAM3D;AAUD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,WAAW,CAAC;IAClB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qDAAqD;IACrD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,GAAiB,GAAG,qBAAqB,CAwC9F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,GAAiB,GAAG,MAAM,GAAG,IAAI,CAItF"}
@@ -0,0 +1,34 @@
1
+ import type { Pool } from "pg";
2
+ import { type TlsResolveOptions } from "./tls.js";
3
+ import { type PoolQueryClient } from "./query.js";
4
+ export interface CreatePgPoolOptions extends TlsResolveOptions {
5
+ connectionString: string;
6
+ /** Max clients in the pool. Defaults to pg's default (10). */
7
+ max?: number;
8
+ /** Idle client timeout (ms). */
9
+ idleTimeoutMillis?: number;
10
+ /** Connection acquisition timeout (ms). */
11
+ connectionTimeoutMillis?: number;
12
+ /** Application name reported to Postgres (shows in pg_stat_activity). */
13
+ applicationName?: string;
14
+ }
15
+ /** Build a `pg.Pool` with fleet-standard TLS handling. */
16
+ export declare function createPgPool(options: CreatePgPoolOptions): Pool;
17
+ export interface CreateCloudPoolFromEnvOptions extends TlsResolveOptions {
18
+ max?: number;
19
+ idleTimeoutMillis?: number;
20
+ connectionTimeoutMillis?: number;
21
+ applicationName?: string;
22
+ }
23
+ export interface CloudPoolFromEnv {
24
+ client: PoolQueryClient;
25
+ connectionSource: string;
26
+ }
27
+ /**
28
+ * Resolve mode + database URL from the environment and build a cloud pool.
29
+ *
30
+ * Throws when the resolved mode is not `cloud` (PURE REMOTE has no Postgres in
31
+ * `local` mode) or when the database URL is missing. Never logs the URL.
32
+ */
33
+ export declare function createCloudPoolFromEnv(appName: string, options?: CreateCloudPoolFromEnvOptions): CloudPoolFromEnv;
34
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../../src/generated/storage-kit/pool.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,IAAI,CAAC;AAE3C,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,gBAAgB,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAe/D;AAED,MAAM,WAAW,6BAA8B,SAAQ,iBAAiB;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,6BAAkC,GAC1C,gBAAgB,CAgClB"}
@@ -0,0 +1,36 @@
1
+ import type { Pool, QueryResultRow } from "pg";
2
+ export interface QueryResult<T extends QueryResultRow> {
3
+ rows: T[];
4
+ rowCount: number;
5
+ }
6
+ /**
7
+ * Minimal executor contract. `pg.Pool` and `pg.PoolClient` both satisfy the
8
+ * `query` method; the wrapper builds the rest on top so tests can substitute a
9
+ * lightweight shim without pulling in a live Postgres.
10
+ */
11
+ export interface PgExecutor {
12
+ query<T extends QueryResultRow>(sql: string, params?: readonly unknown[]): Promise<{
13
+ rows: T[];
14
+ rowCount: number | null;
15
+ }>;
16
+ }
17
+ export interface TypedQueryClient {
18
+ query<T extends QueryResultRow>(sql: string, params?: readonly unknown[]): Promise<QueryResult<T>>;
19
+ many<T extends QueryResultRow>(sql: string, params?: readonly unknown[]): Promise<T[]>;
20
+ /** First row or `null`. Restored here after open-knowledge dropped it. */
21
+ get<T extends QueryResultRow>(sql: string, params?: readonly unknown[]): Promise<T | null>;
22
+ /** Exactly one row; throws if zero or more than one row is returned. */
23
+ one<T extends QueryResultRow>(sql: string, params?: readonly unknown[]): Promise<T>;
24
+ execute(sql: string, params?: readonly unknown[]): Promise<void>;
25
+ }
26
+ /** Wrap any `PgExecutor` (a Pool, a PoolClient, or a test shim) with the typed vocabulary. */
27
+ export declare function wrapExecutor(executor: PgExecutor): TypedQueryClient;
28
+ export interface PoolQueryClient extends TypedQueryClient {
29
+ readonly pool: Pool;
30
+ /** Run a callback inside a `BEGIN`/`COMMIT` transaction on a dedicated client. */
31
+ transaction<T>(fn: (client: TypedQueryClient) => Promise<T>): Promise<T>;
32
+ close(): Promise<void>;
33
+ }
34
+ /** Build a `PoolQueryClient` around a live `pg.Pool`. */
35
+ export declare function createQueryClient(pool: Pool): PoolQueryClient;
36
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/generated/storage-kit/query.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,IAAI,EAAc,cAAc,EAAE,MAAM,IAAI,CAAC;AAE3D,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,cAAc;IACnD,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,CAAC,SAAS,cAAc,EAC5B,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAC1B,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,CAAC,SAAS,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACnG,IAAI,CAAC,CAAC,SAAS,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACvF,0EAA0E;IAC1E,GAAG,CAAC,CAAC,SAAS,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3F,wEAAwE;IACxE,GAAG,CAAC,CAAC,SAAS,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,GAAG,gBAAgB,CAyBnE;AAED,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,kFAAkF;IAClF,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,yDAAyD;AACzD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,eAAe,CA2B7D"}
@@ -0,0 +1,26 @@
1
+ /** The `ssl` field shape accepted by `pg.Pool` / `pg.Client`. */
2
+ export type PgSslConfig = boolean | {
3
+ rejectUnauthorized: boolean;
4
+ ca?: string;
5
+ };
6
+ export interface TlsResolveOptions {
7
+ /** Inline CA bundle (PEM). Wins over every other CA source. */
8
+ ca?: string;
9
+ /** Path to a CA bundle PEM file, e.g. the Amazon RDS global bundle. */
10
+ caCertPath?: string;
11
+ /** Environment used to discover PGSSLROOTCERT / NODE_EXTRA_CA_CERTS. */
12
+ env?: Record<string, string | undefined>;
13
+ }
14
+ export type SslMode = "disable" | "prefer" | "require" | "verify-ca" | "verify-full";
15
+ /**
16
+ * Extract the effective `sslmode` from a Postgres connection string. Honors the
17
+ * `sslmode` query param and the legacy `ssl=true` boolean. Returns `disable`
18
+ * when TLS is not requested.
19
+ */
20
+ export declare function sslModeFromConnectionString(connectionString: string): SslMode;
21
+ /**
22
+ * Resolve the `pg` ssl config for a connection string. See the module header
23
+ * for the full mode table. Returns `undefined` when TLS should be off.
24
+ */
25
+ export declare function resolveTlsConfig(connectionString: string, options?: TlsResolveOptions): PgSslConfig | undefined;
26
+ //# sourceMappingURL=tls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tls.d.ts","sourceRoot":"","sources":["../../../src/generated/storage-kit/tls.ts"],"names":[],"mappings":"AAkCA,iEAAiE;AACjE,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG;IAAE,kBAAkB,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;AAErF;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAwB7E;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,iBAAsB,GAC9B,WAAW,GAAG,SAAS,CA2BzB"}