From 685ec8b999a64d07bcd7002fb07a3586df2e9184 Mon Sep 17 00:00:00 2001 From: hasna Date: Wed, 29 Jul 2026 14:13:52 +0300 Subject: [PATCH] refactor(storage)!: replace the local|hybrid|remote deployment mode with a sqlite|postgresql backend Deployment location was never a property of the data layer. `StorageMode` named three places something could run; nothing in this repo ever branched on it. Its only two readers were `getStorageStatus()` and its own test, so the enum bought vocabulary and no behaviour. The real defect was underneath it. `normalizeStorageMode` returned `undefined` for any value it did not recognise, and `getStorageMode` then fell through to `getStorageDatabaseUrl() ? "hybrid" : "local"`. Measured on origin/main: HASNA_HOOKS_STORAGE_MODE=self_hosted -> rc=0, "Mode: local" HASNA_HOOKS_STORAGE_MODE=mysql -> rc=0, "Mode: local" A typo and the fleet's own canonical mode word were both silently normalised into a mode the operator did not ask for, and reported back as if configured. Now: - `StorageBackend = "sqlite" | "postgresql"` replaces `StorageMode`. `local` collapses to sqlite; hybrid, remote, self_hosted, self-hosted and cloud all collapse to postgresql, because they only ever differed in who operated the server. - `HASNA_HOOKS_STORAGE_BACKEND` (fallback `HOOKS_STORAGE_BACKEND`) selects it. `sqlite3`, `postgres` and `pg` are accepted aliases. Any other value throws and names the accepted set. - `HASNA_HOOKS_STORAGE_MODE` and `HOOKS_STORAGE_MODE` are no longer read. Setting either throws, naming the replacement variable and the backend that value maps to, rather than being ignored. Deleting the words while keeping the silent fall-through would have removed the symptom and kept the hole. - Inference is unchanged: unset plus a database URL yields postgresql (previously reported `hybrid`); unset with no URL yields sqlite (`local`). Breaking for API consumers: `StorageStatus.mode` becomes `.backend`, the CLI prints `Backend:`, and `StorageMode` / `getStorageMode` / `HOOKS_STORAGE_MODE_ENV` / `HOOKS_STORAGE_MODE_FALLBACK_ENV` / `STORAGE_MODE_ENV` are unexported. A workspace scan found no importer of `@hasna/hooks` outside this repo, and the rename makes a stale reading of the field a compile error rather than a silent mismatch, since the values changed meaning too. Hook evaluation is untouched: no hook and no part of the prompt path reads the backend. No client-side PostgresStore is introduced or retained. `PgAdapterAsync` remains reachable only from the operator-invoked `hooks storage push|pull|sync` commands; the live data layer is bun:sqlite only. --- CHANGELOG.md | 9 ++++ README.md | 36 ++++++++++--- src/cli/index.tsx | 2 +- src/db/pg-migrations.ts | 2 +- src/db/storage-sync.ts | 110 ++++++++++++++++++++++++++++++++++------ src/index.ts | 12 +++-- src/storage.test.ts | 99 ++++++++++++++++++++++++++++++------ src/storage.ts | 12 +++-- 8 files changed, 232 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c451fde..04d6e92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING: deployment modes are gone; hooks storage is a two-value data-backend switch.** `StorageMode = "local" | "hybrid" | "remote"` described *where* something ran, which was never a property of the data layer, and nothing in the codebase ever branched on it — it was reported by `hooks storage status` and the `storage_status` MCP tool and otherwise decorative. It is replaced by `StorageBackend = "sqlite" | "postgresql"`. + - `HASNA_HOOKS_STORAGE_MODE` and `HOOKS_STORAGE_MODE` are retired and are **no longer read**. Setting either now raises an error naming the replacement variable and the backend to use, instead of being quietly ignored: `local` became `sqlite`, and `hybrid` / `remote` / `self_hosted` / `self-hosted` / `cloud` all became `postgresql`. + - New `HASNA_HOOKS_STORAGE_BACKEND` (fallback `HOOKS_STORAGE_BACKEND`) accepts `sqlite` or `postgresql` (`sqlite3`, `postgres` and `pg` are accepted aliases). **An unrecognised value now throws.** Previously any unknown value — including a typo — fell through `normalizeStorageMode` to `undefined` and then silently to `local`, so a misconfigured mode looked like a working local one. That silent normalisation was the actual defect; the vocabulary was its symptom. + - Backend inference is unchanged: with the variable unset, a configured `HASNA_HOOKS_DATABASE_URL` / `HOOKS_DATABASE_URL` yields `postgresql` (previously reported as `hybrid`) and its absence yields `sqlite` (previously `local`). + - `StorageStatus.mode` is renamed to `StorageStatus.backend`, and `hooks storage status` prints `Backend:` in place of `Mode:`. Removed from the package's public exports: `StorageMode`, `getStorageMode`, `HOOKS_STORAGE_MODE_ENV`, `HOOKS_STORAGE_MODE_FALLBACK_ENV`, `STORAGE_MODE_ENV`. Added: `StorageBackend`, `getStorageBackend`, `STORAGE_BACKENDS`, `HOOKS_STORAGE_BACKEND_ENV`, `HOOKS_STORAGE_BACKEND_FALLBACK_ENV`, `STORAGE_BACKEND_ENV`, `RETIRED_STORAGE_MODE_ENV`. + - Hook evaluation is untouched: no hook, and no part of the prompt path, reads the backend. `getStorageBackend()` is reached only from `getStorageStatus()`. + ### Fixed - **`pre-bash` / `worktree-guard` destructive-shell guard no longer lets a filesystem-root wipe through.** `rm -rf /*` and `rm -rf "$(cmd)"/*` both returned `{"continue":true}` before this change; only `rm -rf /` blocked, and only incidentally, because `~/.hasna` sits under it. Two complementary rules close the class: diff --git a/README.md b/README.md index 6d00266..8d4e948 100644 --- a/README.md +++ b/README.md @@ -95,9 +95,9 @@ hooks install knowledge-context --target codewith --apply-codewith --codewith-co Hooks stores data locally by default in `~/.hasna/hooks/` and uses SQLite directly for hook event history. The package owns its database schema and migrations; it does not depend on the deprecated shared runtime or its CLI. -The repo includes its own PostgreSQL migration definitions for optional remote -storage deployments. Use the `hooks log` commands to inspect local hook event -data. +The repo includes its own PostgreSQL migration definitions for the optional +`hooks storage push|pull|sync` commands. Use the `hooks log` commands to inspect +local hook event data. ```bash hooks storage status --json @@ -107,14 +107,34 @@ hooks storage sync --json ``` Configure database storage with `HASNA_HOOKS_DATABASE_URL` or fallback -`HOOKS_DATABASE_URL`. Optional storage mode env vars are -`HASNA_HOOKS_STORAGE_MODE` and `HOOKS_STORAGE_MODE`, with `local`, `hybrid`, or -`remote` values. +`HOOKS_DATABASE_URL`. + +### Storage backend + +Hooks storage has one setting with two values: **which data backend**, not where +anything is deployed. + +| `HASNA_HOOKS_STORAGE_BACKEND` (fallback `HOOKS_STORAGE_BACKEND`) | meaning | +| --- | --- | +| `sqlite` | the on-box SQLite file in `~/.hasna/hooks/` (default) | +| `postgresql` | the PostgreSQL database named by `HASNA_HOOKS_DATABASE_URL` | + +Leave it unset and the backend is inferred exactly as before: `postgresql` when a +database URL is configured, `sqlite` otherwise. An unrecognised value is an +error, not a silent fall back to SQLite. + +The former deployment-mode variables `HASNA_HOOKS_STORAGE_MODE` and +`HOOKS_STORAGE_MODE`, and their `local` / `hybrid` / `remote` / `self-hosted` / +`cloud` values, are **retired**. They are not read; setting one raises an error +naming the replacement variable and the backend to use (`local` became `sqlite`, +everything else became `postgresql`). Deployment location was never a property of +the data layer, so it is no longer expressed as one. ## Runtime model -This package is an npm/local CLI, MCP server, and static dashboard package. It -does not require a deployed cloud or self-hosted runtime to install or run hooks. +This package is an npm CLI, MCP server, and static dashboard package. Installing +and running hooks needs nothing deployed anywhere — the SQLite backend is the +default and requires no server. ## Data Directory diff --git a/src/cli/index.tsx b/src/cli/index.tsx index cfaffcf..909fad6 100644 --- a/src/cli/index.tsx +++ b/src/cli/index.tsx @@ -1227,7 +1227,7 @@ storageCmd } console.log(chalk.bold("\n Storage Status\n")); console.log(` Configured: ${status.configured ? chalk.green(`yes (${status.activeEnv})`) : chalk.red("no")}`); - console.log(` Mode: ${status.mode}`); + console.log(` Backend: ${status.backend}`); console.log(` Tables: ${status.tables.join(", ")}`); console.log(` Sync rows: ${status.sync.length}`); }); diff --git a/src/db/pg-migrations.ts b/src/db/pg-migrations.ts index c01f351..26b960a 100644 --- a/src/db/pg-migrations.ts +++ b/src/db/pg-migrations.ts @@ -1,5 +1,5 @@ /** - * PostgreSQL migrations for open-hooks remote storage and sync. + * PostgreSQL migrations for the open-hooks PostgreSQL storage backend and sync. * * Equivalent to the SQLite schema in schema.ts, migrations/, and index.ts, * translated for PostgreSQL. diff --git a/src/db/storage-sync.ts b/src/db/storage-sync.ts index 2182d26..ede0f19 100644 --- a/src/db/storage-sync.ts +++ b/src/db/storage-sync.ts @@ -15,7 +15,16 @@ export const HOOKS_STORAGE_TABLES = STORAGE_TABLES; type StorageTable = (typeof STORAGE_TABLES)[number]; type Row = Record; -export type StorageMode = "local" | "hybrid" | "remote"; +/** + * The storage backend is a two-value data-backend switch, NOT a deployment mode. + * + * `local | hybrid | remote` (and the wider fleet's `self_hosted | cloud`) described *where* + * something ran, which is not a property of the data layer. They are retired: local collapses + * to `sqlite`, and every server-backed placement collapses to `postgresql`. + */ +export const STORAGE_BACKENDS = ["sqlite", "postgresql"] as const; + +export type StorageBackend = (typeof STORAGE_BACKENDS)[number]; export interface StorageEnv { name: string; @@ -36,14 +45,21 @@ export interface SyncMeta { export const HOOKS_STORAGE_ENV = "HASNA_HOOKS_DATABASE_URL"; export const HOOKS_STORAGE_FALLBACK_ENV = "HOOKS_DATABASE_URL"; -export const HOOKS_STORAGE_MODE_ENV = "HASNA_HOOKS_STORAGE_MODE"; -export const HOOKS_STORAGE_MODE_FALLBACK_ENV = "HOOKS_STORAGE_MODE"; +export const HOOKS_STORAGE_BACKEND_ENV = "HASNA_HOOKS_STORAGE_BACKEND"; +export const HOOKS_STORAGE_BACKEND_FALLBACK_ENV = "HOOKS_STORAGE_BACKEND"; export const STORAGE_DATABASE_ENV = [HOOKS_STORAGE_ENV, HOOKS_STORAGE_FALLBACK_ENV] as const; -export const STORAGE_MODE_ENV = [HOOKS_STORAGE_MODE_ENV, HOOKS_STORAGE_MODE_FALLBACK_ENV] as const; +export const STORAGE_BACKEND_ENV = [HOOKS_STORAGE_BACKEND_ENV, HOOKS_STORAGE_BACKEND_FALLBACK_ENV] as const; + +/** + * Deployment-mode env vars that no longer exist. Reading one is an error rather than a no-op: + * an operator who set `HASNA_HOOKS_STORAGE_MODE=hybrid` believed they had configured something, + * and silently ignoring it is how a config change appears to work and does not. + */ +export const RETIRED_STORAGE_MODE_ENV = ["HASNA_HOOKS_STORAGE_MODE", "HOOKS_STORAGE_MODE"] as const; export interface StorageStatus { configured: boolean; - mode: StorageMode; + backend: StorageBackend; env: typeof STORAGE_DATABASE_ENV; activeEnv: string | null; service: "hooks"; @@ -63,10 +79,67 @@ function readEnv(name: string): string | undefined { return value || undefined; } -function normalizeStorageMode(value: string | undefined): StorageMode | undefined { - const normalized = value?.trim().toLowerCase(); - if (normalized === "local" || normalized === "hybrid" || normalized === "remote") return normalized; - return undefined; +/** + * Retired deployment-mode values mapped to the backend that replaced them. `local` was the + * on-box SQLite file; every other placement — hybrid, remote, self-hosted, cloud — was a + * server holding the data in PostgreSQL. + */ +const RETIRED_MODE_REPLACEMENT: Record = { + local: "sqlite", + hybrid: "postgresql", + remote: "postgresql", + self_hosted: "postgresql", + "self-hosted": "postgresql", + selfhosted: "postgresql", + cloud: "postgresql", +}; + +const BACKEND_ALIASES: Record = { + sqlite: "sqlite", + sqlite3: "sqlite", + postgresql: "postgresql", + postgres: "postgresql", + pg: "postgresql", +}; + +function assertNoRetiredModeEnv(): void { + for (const name of RETIRED_STORAGE_MODE_ENV) { + const value = readEnv(name); + if (!value) continue; + const replacement = RETIRED_MODE_REPLACEMENT[value.trim().toLowerCase()]; + const mapping = replacement + ? `${value} maps to ${replacement}` + : `use one of ${STORAGE_BACKENDS.join(", ")}`; + throw new Error( + `${name} is a retired deployment-mode variable and is no longer read. ` + + `Hooks storage is a data-backend switch, not a deployment mode: ` + + `set ${HOOKS_STORAGE_BACKEND_ENV} to ${STORAGE_BACKENDS.join(" or ")} instead (${mapping}), ` + + `then unset ${name}.`, + ); + } +} + +function normalizeStorageBackend(value: string | undefined, envName: string): StorageBackend | undefined { + if (value === undefined) return undefined; + const normalized = value.trim().toLowerCase(); + if (normalized === "") return undefined; + + const backend = BACKEND_ALIASES[normalized]; + if (backend) return backend; + + const replacement = RETIRED_MODE_REPLACEMENT[normalized]; + if (replacement) { + throw new Error( + `${envName}=${value} names a retired deployment mode. ` + + `local/hybrid/remote/self_hosted/cloud were removed: hooks storage now selects a data ` + + `backend only. Set ${envName}=${replacement} instead.`, + ); + } + + throw new Error( + `${envName}=${value} is not a known hooks storage backend. ` + + `Set ${HOOKS_STORAGE_BACKEND_ENV} to one of ${STORAGE_BACKENDS.join(", ")}.`, + ); } export function getStorageDatabaseEnvName(): (typeof STORAGE_DATABASE_ENV)[number] | null { @@ -86,11 +159,18 @@ export function getStorageDatabaseUrl(): string | null { return env ? readEnv(env.name) ?? null : null; } -export function getStorageMode(): StorageMode { - const mode = normalizeStorageMode(readEnv(HOOKS_STORAGE_MODE_ENV)) - ?? normalizeStorageMode(readEnv(HOOKS_STORAGE_MODE_FALLBACK_ENV)); - if (mode) return mode; - return getStorageDatabaseUrl() ? "hybrid" : "local"; +/** + * Which data backend hooks storage talks to. Explicit configuration wins; otherwise the + * presence of a database URL is the answer, exactly as before. + * + * Throws — never silently falls back — on an unknown value or a retired deployment-mode name. + */ +export function getStorageBackend(): StorageBackend { + assertNoRetiredModeEnv(); + const backend = normalizeStorageBackend(readEnv(HOOKS_STORAGE_BACKEND_ENV), HOOKS_STORAGE_BACKEND_ENV) + ?? normalizeStorageBackend(readEnv(HOOKS_STORAGE_BACKEND_FALLBACK_ENV), HOOKS_STORAGE_BACKEND_FALLBACK_ENV); + if (backend) return backend; + return getStorageDatabaseUrl() ? "postgresql" : "sqlite"; } export async function getStoragePg(): Promise { @@ -154,7 +234,7 @@ export function getStorageStatus(): StorageStatus { const activeEnv = getStorageDatabaseEnv(); return { configured: Boolean(activeEnv), - mode: getStorageMode(), + backend: getStorageBackend(), env: STORAGE_DATABASE_ENV, activeEnv: activeEnv?.name ?? null, service: "hooks", diff --git a/src/index.ts b/src/index.ts index feb7cb5..3ceecea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -190,16 +190,18 @@ export { export { HOOKS_STORAGE_ENV, HOOKS_STORAGE_FALLBACK_ENV, - HOOKS_STORAGE_MODE_ENV, - HOOKS_STORAGE_MODE_FALLBACK_ENV, + HOOKS_STORAGE_BACKEND_ENV, + HOOKS_STORAGE_BACKEND_FALLBACK_ENV, HOOKS_STORAGE_TABLES, + RETIRED_STORAGE_MODE_ENV, + STORAGE_BACKENDS, + STORAGE_BACKEND_ENV, STORAGE_DATABASE_ENV, - STORAGE_MODE_ENV, STORAGE_TABLES, + getStorageBackend, getStorageDatabaseEnv, getStorageDatabaseEnvName, getStorageDatabaseUrl, - getStorageMode, getStoragePg, getStorageStatus, getSyncMetaAll, @@ -210,4 +212,4 @@ export { storagePush, storageSync, } from "./storage.js"; -export type { StorageEnv, StorageMode, StorageStatus, SyncMeta, SyncResult } from "./storage.js"; +export type { StorageBackend, StorageEnv, StorageStatus, SyncMeta, SyncResult } from "./storage.js"; diff --git a/src/storage.test.ts b/src/storage.test.ts index 7de9594..e135a38 100644 --- a/src/storage.test.ts +++ b/src/storage.test.ts @@ -3,15 +3,18 @@ import { existsSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { closeDb } from "./db/index.js"; +import * as storageModule from "./storage.js"; import { + HOOKS_STORAGE_BACKEND_ENV, + HOOKS_STORAGE_BACKEND_FALLBACK_ENV, HOOKS_STORAGE_ENV, HOOKS_STORAGE_FALLBACK_ENV, - HOOKS_STORAGE_MODE_ENV, - HOOKS_STORAGE_MODE_FALLBACK_ENV, + RETIRED_STORAGE_MODE_ENV, + STORAGE_BACKENDS, STORAGE_TABLES, + getStorageBackend, getStorageDatabaseEnv, getStorageDatabaseUrl, - getStorageMode, getStorageStatus, parseStorageTables, resolveTables, @@ -20,8 +23,9 @@ import { const ENV_KEYS = [ HOOKS_STORAGE_ENV, HOOKS_STORAGE_FALLBACK_ENV, - HOOKS_STORAGE_MODE_ENV, - HOOKS_STORAGE_MODE_FALLBACK_ENV, + HOOKS_STORAGE_BACKEND_ENV, + HOOKS_STORAGE_BACKEND_FALLBACK_ENV, + ...RETIRED_STORAGE_MODE_ENV, "HASNA_HOOKS_DB_PATH", ] as const; @@ -31,27 +35,91 @@ afterEach(() => { }); describe("hooks storage config", () => { - test("resolves canonical database env, fallback env, and storage mode", () => { + test("resolves canonical database env and fallback env", () => { for (const key of ENV_KEYS) delete process.env[key]; expect(getStorageDatabaseEnv()).toBeNull(); expect(getStorageDatabaseUrl()).toBeNull(); - expect(getStorageMode()).toBe("local"); process.env[HOOKS_STORAGE_FALLBACK_ENV] = "postgres://fallback/hooks"; expect(getStorageDatabaseEnv()?.name).toBe(HOOKS_STORAGE_FALLBACK_ENV); expect(getStorageDatabaseUrl()).toBe("postgres://fallback/hooks"); - expect(getStorageMode()).toBe("hybrid"); process.env[HOOKS_STORAGE_ENV] = "postgres://primary/hooks"; expect(getStorageDatabaseEnv()?.name).toBe(HOOKS_STORAGE_ENV); expect(getStorageDatabaseUrl()).toBe("postgres://primary/hooks"); + }); + + test("the storage backend is a two-value switch, not a three-way deployment mode", () => { + expect([...STORAGE_BACKENDS]).toEqual(["sqlite", "postgresql"]); + }); + + test("backend defaults to sqlite, and to postgresql once a database url is configured", () => { + for (const key of ENV_KEYS) delete process.env[key]; + expect(getStorageBackend()).toBe("sqlite"); + + process.env[HOOKS_STORAGE_FALLBACK_ENV] = "postgres://fallback/hooks"; + expect(getStorageBackend()).toBe("postgresql"); + }); + + test("an explicit backend wins over the inferred default, case-insensitively", () => { + process.env[HOOKS_STORAGE_BACKEND_ENV] = "PostgreSQL"; + expect(getStorageBackend()).toBe("postgresql"); + + delete process.env[HOOKS_STORAGE_BACKEND_ENV]; + process.env[HOOKS_STORAGE_BACKEND_FALLBACK_ENV] = " sqlite "; + process.env[HOOKS_STORAGE_ENV] = "postgres://primary/hooks"; + expect(getStorageBackend()).toBe("sqlite"); + }); + + test("`postgres` is accepted as an alias for the canonical `postgresql`", () => { + process.env[HOOKS_STORAGE_BACKEND_ENV] = "postgres"; + expect(getStorageBackend()).toBe("postgresql"); + }); + + test("an unknown backend value throws instead of silently falling back", () => { + process.env[HOOKS_STORAGE_BACKEND_ENV] = "invalid"; + process.env[HOOKS_STORAGE_BACKEND_FALLBACK_ENV] = "sqlite"; + expect(() => getStorageBackend()).toThrow(/HASNA_HOOKS_STORAGE_BACKEND/); + expect(() => getStorageBackend()).toThrow(/sqlite/); + expect(() => getStorageBackend()).toThrow(/postgresql/); + }); + + test.each(["local", "hybrid", "remote", "self_hosted", "self-hosted", "cloud"])( + "the retired deployment mode %p throws and names its backend replacement", + (retired) => { + process.env[HOOKS_STORAGE_BACKEND_ENV] = retired; + expect(() => getStorageBackend()).toThrow(/deployment mode/i); + expect(() => getStorageBackend()).toThrow(/HASNA_HOOKS_STORAGE_BACKEND/); + const expected = retired === "local" ? "sqlite" : "postgresql"; + expect(() => getStorageBackend()).toThrow(new RegExp(expected)); + }, + ); - process.env[HOOKS_STORAGE_MODE_ENV] = "remote"; - expect(getStorageMode()).toBe("remote"); + test.each(["HASNA_HOOKS_STORAGE_MODE", "HOOKS_STORAGE_MODE"])( + "the retired env var %s throws and names the replacement env var", + (retiredEnv) => { + process.env[retiredEnv] = "hybrid"; + expect(() => getStorageBackend()).toThrow(new RegExp(retiredEnv)); + expect(() => getStorageBackend()).toThrow(/HASNA_HOOKS_STORAGE_BACKEND/); + expect(() => getStorageStatus()).toThrow(/HASNA_HOOKS_STORAGE_BACKEND/); + }, + ); - process.env[HOOKS_STORAGE_MODE_ENV] = "invalid"; - process.env[HOOKS_STORAGE_MODE_FALLBACK_ENV] = "local"; - expect(getStorageMode()).toBe("local"); + test("a retired env var throws even when it carries an otherwise valid backend value", () => { + process.env.HASNA_HOOKS_STORAGE_MODE = "sqlite"; + expect(() => getStorageBackend()).toThrow(/HASNA_HOOKS_STORAGE_MODE/); + }); + + test("the deployment-mode API is gone from the public surface", () => { + const removed = [ + "getStorageMode", + "HOOKS_STORAGE_MODE_ENV", + "HOOKS_STORAGE_MODE_FALLBACK_ENV", + "STORAGE_MODE_ENV", + ]; + for (const name of removed) { + expect(Object.keys(storageModule)).not.toContain(name); + } }); test("exposes and validates storage tables", () => { @@ -63,7 +131,7 @@ describe("hooks storage config", () => { expect(() => resolveTables(["missing"])).toThrow("Unknown hooks sync table"); }); - test("storage status initializes local sync metadata without remote config", () => { + test("storage status reports the sqlite backend and no deployment mode", () => { const dir = mkdtempSync(join(tmpdir(), "hooks-storage-")); const dbPath = join(dir, "hooks.db"); process.env.HASNA_HOOKS_DB_PATH = dbPath; @@ -72,11 +140,12 @@ describe("hooks storage config", () => { const status = getStorageStatus(); expect(status).toMatchObject({ configured: false, - mode: "local", + backend: "sqlite", service: "hooks", activeEnv: null, sync: [], }); + expect(Object.keys(status)).not.toContain("mode"); expect(status.tables).toEqual(STORAGE_TABLES); expect(existsSync(dbPath)).toBe(true); } finally { diff --git a/src/storage.ts b/src/storage.ts index 929e27e..36f5a2a 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1,16 +1,18 @@ export { HOOKS_STORAGE_ENV, HOOKS_STORAGE_FALLBACK_ENV, - HOOKS_STORAGE_MODE_ENV, - HOOKS_STORAGE_MODE_FALLBACK_ENV, + HOOKS_STORAGE_BACKEND_ENV, + HOOKS_STORAGE_BACKEND_FALLBACK_ENV, HOOKS_STORAGE_TABLES, + RETIRED_STORAGE_MODE_ENV, + STORAGE_BACKENDS, + STORAGE_BACKEND_ENV, STORAGE_DATABASE_ENV, - STORAGE_MODE_ENV, STORAGE_TABLES, + getStorageBackend, getStorageDatabaseEnv, getStorageDatabaseEnvName, getStorageDatabaseUrl, - getStorageMode, getStoragePg, getStorageStatus, getSyncMetaAll, @@ -21,6 +23,6 @@ export { storagePush, storageSync, } from "./db/storage-sync.js"; -export type { StorageEnv, StorageMode, StorageStatus, SyncMeta, SyncResult } from "./db/storage-sync.js"; +export type { StorageBackend, StorageEnv, StorageStatus, SyncMeta, SyncResult } from "./db/storage-sync.js"; export { PgAdapterAsync } from "./db/remote-storage.js"; export { PG_MIGRATIONS } from "./db/pg-migrations.js";