@bluewombat/persist-sqlite 0.1.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/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # persist-sqlite
2
+
3
+ Persistence Port for WorkLedger: one SQLite file, `node:sqlite`. The ledger
4
+ never imports this package. Glue does.
5
+
6
+ ```ts
7
+ import { openWorkLedger } from "@bluewombat/work-ledger";
8
+ import { openSqlitePersist } from "@bluewombat/persist-sqlite";
9
+
10
+ const persist = await openSqlitePersist({ path: "/absolute/path/to/ledger.sqlite" });
11
+ const ledger = openWorkLedger({ persist });
12
+ ```
13
+
14
+ `path` must be absolute. The adapter creates the parent directory if missing.
15
+
16
+ From a Project, name it instead and let Host load it:
17
+
18
+ ```json
19
+ { "persist": "@bluewombat/persist-sqlite" }
20
+ ```
21
+
22
+ Host calls `openPersist({ ledgerRoot })`; the file is `<ledgerRoot>/ledger.sqlite`,
23
+ beside Host's own Cursor and journal. Install the package next to mason.
24
+
25
+ ## Stack
26
+
27
+ Repository stack — TypeScript, Node 24.20, Biome, `node:test`. See [docs/DEVELOPMENT.md](../../../docs/DEVELOPMENT.md).
28
+
29
+ ## Commands
30
+
31
+ | Task | Command |
32
+ | --------- | ---------------- |
33
+ | Lint | `npm run lint` |
34
+ | Format | `npm run format` |
35
+ | Typecheck | `npm run tsc` |
36
+ | Test | `npm test` |
37
+ | Build | `npm run build` |
@@ -0,0 +1,4 @@
1
+ export { LEDGER_FILE, openPersist } from "./open-persist.js";
2
+ export type { SqlitePersistOptions } from "./open-sqlite-persist.js";
3
+ export { openSqlitePersist } from "./open-sqlite-persist.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { LEDGER_FILE, openPersist } from "./open-persist.js";
2
+ export { openSqlitePersist } from "./open-sqlite-persist.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { PersistencePort } from "@bluewombat/work-ledger";
2
+ /** The one file the ledger lives in, under Host's ledger directory. */
3
+ export declare const LEDGER_FILE = "ledger.sqlite";
4
+ /**
5
+ * The face Host loads by name (`"persist": "@bluewombat/persist-sqlite"`):
6
+ * the ledger directory in, a Port out. Everything goes in `ledger.sqlite`
7
+ * there; Host's own files (Cursor, journal, lock) sit beside it untouched.
8
+ */
9
+ export declare function openPersist(context: {
10
+ ledgerRoot: string;
11
+ }): Promise<PersistencePort>;
12
+ //# sourceMappingURL=open-persist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-persist.d.ts","sourceRoot":"","sources":["../src/open-persist.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/D,uEAAuE;AACvE,eAAO,MAAM,WAAW,kBAAkB,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAErF"}
@@ -0,0 +1,13 @@
1
+ import { join } from "node:path";
2
+ import { openSqlitePersist } from "./open-sqlite-persist.js";
3
+ /** The one file the ledger lives in, under Host's ledger directory. */
4
+ export const LEDGER_FILE = "ledger.sqlite";
5
+ /**
6
+ * The face Host loads by name (`"persist": "@bluewombat/persist-sqlite"`):
7
+ * the ledger directory in, a Port out. Everything goes in `ledger.sqlite`
8
+ * there; Host's own files (Cursor, journal, lock) sit beside it untouched.
9
+ */
10
+ export function openPersist(context) {
11
+ return openSqlitePersist({ path: join(context.ledgerRoot, LEDGER_FILE) });
12
+ }
13
+ //# sourceMappingURL=open-persist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-persist.js","sourceRoot":"","sources":["../src/open-persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,uEAAuE;AACvE,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,OAA+B;IACzD,OAAO,iBAAiB,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { PersistencePort } from "@bluewombat/work-ledger";
2
+ export type SqlitePersistOptions = {
3
+ path: string;
4
+ };
5
+ /**
6
+ * SQLite Persistence Port. One row per key. A transaction wraps each save.
7
+ * Does not know FeatureStandard transitions.
8
+ */
9
+ export declare function openSqlitePersist(options: SqlitePersistOptions): Promise<PersistencePort>;
10
+ //# sourceMappingURL=open-sqlite-persist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-sqlite-persist.d.ts","sourceRoot":"","sources":["../src/open-sqlite-persist.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAqCF;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CAuD/F"}
@@ -0,0 +1,93 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import { dirname, isAbsolute } from "node:path";
3
+ import { DatabaseSync } from "node:sqlite";
4
+ function isAggregate(value) {
5
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
6
+ return false;
7
+ }
8
+ const record = value;
9
+ return (typeof record.state === "string" &&
10
+ record.intention !== null &&
11
+ typeof record.intention === "object");
12
+ }
13
+ function toSummary(aggregate) {
14
+ const summary = {
15
+ key: aggregate.intention.key,
16
+ project: aggregate.intention.project,
17
+ state: aggregate.state,
18
+ priority: aggregate.intention.priority,
19
+ received_at: aggregate.received_at,
20
+ };
21
+ if (aggregate.bail !== undefined) {
22
+ summary.bail_expires_at = aggregate.bail.expires_at;
23
+ }
24
+ return summary;
25
+ }
26
+ function parsePayload(raw) {
27
+ try {
28
+ const parsed = JSON.parse(raw);
29
+ return isAggregate(parsed) ? parsed : undefined;
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ /**
36
+ * SQLite Persistence Port. One row per key. A transaction wraps each save.
37
+ * Does not know FeatureStandard transitions.
38
+ */
39
+ export async function openSqlitePersist(options) {
40
+ if (!isAbsolute(options.path)) {
41
+ throw new Error(`SQLite persist path must be an absolute file path, got "${options.path}".`);
42
+ }
43
+ await mkdir(dirname(options.path), { recursive: true });
44
+ const db = new DatabaseSync(options.path);
45
+ db.exec(`
46
+ CREATE TABLE IF NOT EXISTS features (
47
+ key TEXT PRIMARY KEY NOT NULL,
48
+ payload TEXT NOT NULL
49
+ );
50
+ `);
51
+ const loadStmt = db.prepare("SELECT payload FROM features WHERE key = ?");
52
+ const saveStmt = db.prepare("INSERT OR REPLACE INTO features (key, payload) VALUES (?, ?)");
53
+ const listStmt = db.prepare("SELECT payload FROM features");
54
+ return {
55
+ async load(key) {
56
+ const row = loadStmt.get(key);
57
+ if (row === undefined) {
58
+ return undefined;
59
+ }
60
+ return parsePayload(row.payload);
61
+ },
62
+ async save(key, aggregate) {
63
+ try {
64
+ db.exec("BEGIN IMMEDIATE");
65
+ saveStmt.run(key, JSON.stringify(aggregate));
66
+ db.exec("COMMIT");
67
+ return { ok: true };
68
+ }
69
+ catch (error) {
70
+ try {
71
+ db.exec("ROLLBACK");
72
+ }
73
+ catch {
74
+ // Rollback can fail if BEGIN never succeeded.
75
+ }
76
+ const detail = error instanceof Error ? error.message : String(error);
77
+ return { ok: false, detail };
78
+ }
79
+ },
80
+ async listSummaries() {
81
+ const rows = listStmt.all();
82
+ const summaries = [];
83
+ for (const row of rows) {
84
+ const aggregate = parsePayload(row.payload);
85
+ if (aggregate !== undefined) {
86
+ summaries.push(toSummary(aggregate));
87
+ }
88
+ }
89
+ return summaries;
90
+ },
91
+ };
92
+ }
93
+ //# sourceMappingURL=open-sqlite-persist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-sqlite-persist.js","sourceRoot":"","sources":["../src/open-sqlite-persist.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@bluewombat/persist-sqlite",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "WorkLedger persistence: one SQLite database via node:sqlite.",
6
+ "keywords": [
7
+ "mason",
8
+ "bluewombat",
9
+ "persistence",
10
+ "sqlite"
11
+ ],
12
+ "license": "MIT",
13
+ "homepage": "https://github.com/tilap/bluewombat/tree/main/packages/plugins/persist-sqlite#readme",
14
+ "bugs": "https://github.com/tilap/bluewombat/issues",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/tilap/bluewombat.git",
18
+ "directory": "packages/plugins/persist-sqlite"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ }
25
+ },
26
+ "types": "./dist/index.d.ts",
27
+ "files": [
28
+ "dist",
29
+ "README.md"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "engines": {
35
+ "node": ">=24.20.0"
36
+ },
37
+ "scripts": {
38
+ "lint": "biome check .",
39
+ "format": "biome check --write .",
40
+ "tsc": "tsc --noEmit",
41
+ "test": "tsx --test $(find src -name '*.test.ts')",
42
+ "build": "tsc -p tsconfig.build.json"
43
+ },
44
+ "dependencies": {
45
+ "@bluewombat/work-ledger": "^0.1.0"
46
+ }
47
+ }