@loomwork/plugin-sqlite 0.0.0-stage → 0.1.1
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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +62 -2
- package/dist/SqliteArchiveStore.d.ts +22 -0
- package/dist/SqliteArchiveStore.d.ts.map +1 -0
- package/dist/SqliteArchiveStore.js +129 -0
- package/dist/SqliteArchiveStore.js.map +1 -0
- package/dist/SqliteBranchRegistry.d.ts +26 -0
- package/dist/SqliteBranchRegistry.d.ts.map +1 -0
- package/dist/SqliteBranchRegistry.js +154 -0
- package/dist/SqliteBranchRegistry.js.map +1 -0
- package/dist/SqliteContentStore.d.ts +55 -0
- package/dist/SqliteContentStore.d.ts.map +1 -0
- package/dist/SqliteContentStore.js +150 -0
- package/dist/SqliteContentStore.js.map +1 -0
- package/dist/SqliteDurableStateStore.d.ts +26 -0
- package/dist/SqliteDurableStateStore.d.ts.map +1 -0
- package/dist/SqliteDurableStateStore.js +81 -0
- package/dist/SqliteDurableStateStore.js.map +1 -0
- package/dist/SqliteEventStore.d.ts +55 -0
- package/dist/SqliteEventStore.d.ts.map +1 -0
- package/dist/SqliteEventStore.js +297 -0
- package/dist/SqliteEventStore.js.map +1 -0
- package/dist/SqliteLeaseStore.d.ts +25 -0
- package/dist/SqliteLeaseStore.d.ts.map +1 -0
- package/dist/SqliteLeaseStore.js +107 -0
- package/dist/SqliteLeaseStore.js.map +1 -0
- package/dist/SqliteMemoryStore.d.ts +52 -0
- package/dist/SqliteMemoryStore.d.ts.map +1 -0
- package/dist/SqliteMemoryStore.js +149 -0
- package/dist/SqliteMemoryStore.js.map +1 -0
- package/dist/SqliteReadModelStore.d.ts +38 -0
- package/dist/SqliteReadModelStore.d.ts.map +1 -0
- package/dist/SqliteReadModelStore.js +292 -0
- package/dist/SqliteReadModelStore.js.map +1 -0
- package/dist/SqliteSnapshotStore.d.ts +31 -0
- package/dist/SqliteSnapshotStore.d.ts.map +1 -0
- package/dist/SqliteSnapshotStore.js +101 -0
- package/dist/SqliteSnapshotStore.js.map +1 -0
- package/dist/SqliteTimerStore.d.ts +19 -0
- package/dist/SqliteTimerStore.d.ts.map +1 -0
- package/dist/SqliteTimerStore.js +106 -0
- package/dist/SqliteTimerStore.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/preset.d.ts +31 -0
- package/dist/preset.d.ts.map +1 -0
- package/dist/preset.js +51 -0
- package/dist/preset.js.map +1 -0
- package/dist/schema.d.ts +3 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +103 -0
- package/dist/schema.js.map +1 -0
- package/dist/snapshotSchema.d.ts +3 -0
- package/dist/snapshotSchema.d.ts.map +1 -0
- package/dist/snapshotSchema.js +19 -0
- package/dist/snapshotSchema.js.map +1 -0
- package/package.json +62 -3
- package/src/SqliteArchiveStore.ts +189 -0
- package/src/SqliteBranchRegistry.ts +199 -0
- package/src/SqliteContentStore.ts +195 -0
- package/src/SqliteDurableStateStore.ts +101 -0
- package/src/SqliteEventStore.ts +414 -0
- package/src/SqliteLeaseStore.ts +165 -0
- package/src/SqliteMemoryStore.ts +160 -0
- package/src/SqliteReadModelStore.ts +378 -0
- package/src/SqliteSnapshotStore.ts +157 -0
- package/src/SqliteTimerStore.ts +124 -0
- package/src/index.ts +13 -0
- package/src/preset.ts +66 -0
- package/src/schema.ts +100 -0
- package/src/snapshotSchema.ts +17 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteSnapshotStore.js","sourceRoot":"","sources":["../src/SqliteSnapshotStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TimerRecord, TimerStore } from "@loomwork/event-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* SQLite-backed {@link TimerStore} (T1.3). Point it at the same DB file as the
|
|
4
|
+
* SqliteEventStore so a single backup captures events + timers — the full
|
|
5
|
+
* kernel state. Own idempotent migration, same pattern as SqliteBranchRegistry.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SqliteTimerStore implements TimerStore {
|
|
8
|
+
private readonly db;
|
|
9
|
+
constructor(dbPath: string);
|
|
10
|
+
schedule(timer: TimerRecord): Promise<void>;
|
|
11
|
+
due(now: number, limit?: number, options?: {
|
|
12
|
+
redrive?: boolean;
|
|
13
|
+
inbox?: boolean;
|
|
14
|
+
refused?: boolean;
|
|
15
|
+
}): Promise<TimerRecord[]>;
|
|
16
|
+
complete(executionId: string, wakeAt: number): Promise<void>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=SqliteTimerStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteTimerStore.d.ts","sourceRoot":"","sources":["../src/SqliteTimerStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAUvE;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,MAAM,EAAE,MAAM;IA6CpB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B3C,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,SAAM,EACX,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAClE,OAAO,CAAC,WAAW,EAAE,CAAC;IAkBnB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SqliteTimerStore = void 0;
|
|
7
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
8
|
+
/**
|
|
9
|
+
* SQLite-backed {@link TimerStore} (T1.3). Point it at the same DB file as the
|
|
10
|
+
* SqliteEventStore so a single backup captures events + timers — the full
|
|
11
|
+
* kernel state. Own idempotent migration, same pattern as SqliteBranchRegistry.
|
|
12
|
+
*/
|
|
13
|
+
class SqliteTimerStore {
|
|
14
|
+
db;
|
|
15
|
+
constructor(dbPath) {
|
|
16
|
+
this.db = new better_sqlite3_1.default(dbPath);
|
|
17
|
+
try {
|
|
18
|
+
this.db.pragma("journal_mode = WAL");
|
|
19
|
+
this.db.pragma("synchronous = NORMAL");
|
|
20
|
+
this.db.exec(`
|
|
21
|
+
CREATE TABLE IF NOT EXISTS timers (
|
|
22
|
+
execution_id TEXT NOT NULL,
|
|
23
|
+
wake_at INTEGER NOT NULL,
|
|
24
|
+
PRIMARY KEY (execution_id, wake_at)
|
|
25
|
+
);
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_timers_wake ON timers (wake_at ASC);
|
|
27
|
+
`);
|
|
28
|
+
// ADR 0118 D4 — `CREATE TABLE IF NOT EXISTS` never adds a column to a table an older version
|
|
29
|
+
// created, so the re-drive column is added by a guarded ALTER: only when `table_info` lacks it
|
|
30
|
+
// (SQLite has no `ADD COLUMN IF NOT EXISTS`, and a second ALTER would throw). The check and
|
|
31
|
+
// the ALTER run under ONE write lock (`BEGIN IMMEDIATE`): two processes opening the same
|
|
32
|
+
// file at once would otherwise both see the column missing and the loser's ALTER would throw
|
|
33
|
+
// `duplicate column name`, failing its boot. The error is also tolerated (belt and braces: a
|
|
34
|
+
// peer on a SQLite build whose locking differs still ends with the column present).
|
|
35
|
+
this.db
|
|
36
|
+
.transaction(() => {
|
|
37
|
+
const columns = this.db.pragma("table_info(timers)");
|
|
38
|
+
// `redrive` (ADR 0118 D4) and `refused` (ADR 0130 D10), both nullable TEXT (JSON).
|
|
39
|
+
for (const column of ["redrive", "refused"]) {
|
|
40
|
+
if (columns.some((c) => c.name === column))
|
|
41
|
+
continue;
|
|
42
|
+
try {
|
|
43
|
+
this.db.exec(`ALTER TABLE timers ADD COLUMN ${column} TEXT`);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (!/duplicate column name/i.test(String(err.message)))
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
.immediate();
|
|
52
|
+
// ADR 0130 D10 - a PARTIAL index over the (rare) refusal rows only: a scan for them
|
|
53
|
+
// (`due` with refusal rows wanted) never walks the plain timers, and a plain row pays nothing.
|
|
54
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_timers_refused ON timers (wake_at ASC) WHERE refused IS NOT NULL");
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
this.db.close(); // don't leak the file handle if setup fails on a bad/locked file
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async schedule(timer) {
|
|
62
|
+
this.db
|
|
63
|
+
.prepare(
|
|
64
|
+
// Upsert (ADR 0118 D4, ADR 0130 D10): a flagged row is never downgraded, a plain one gains
|
|
65
|
+
// the flag, and a same-key `refused` upsert keeps the MAX head / MAX holds (never last-writer-wins).
|
|
66
|
+
`INSERT INTO timers (execution_id, wake_at, redrive, refused) VALUES (?, ?, ?, ?)
|
|
67
|
+
ON CONFLICT (execution_id, wake_at)
|
|
68
|
+
DO UPDATE SET redrive = COALESCE(excluded.redrive, timers.redrive),
|
|
69
|
+
refused = CASE
|
|
70
|
+
WHEN excluded.refused IS NULL THEN timers.refused
|
|
71
|
+
WHEN timers.refused IS NULL THEN excluded.refused
|
|
72
|
+
WHEN json_extract(excluded.refused, '$.holds') IS NULL
|
|
73
|
+
AND json_extract(timers.refused, '$.holds') IS NULL
|
|
74
|
+
THEN json_object('head', max(json_extract(excluded.refused, '$.head'),
|
|
75
|
+
json_extract(timers.refused, '$.head')))
|
|
76
|
+
ELSE json_object('head', max(json_extract(excluded.refused, '$.head'),
|
|
77
|
+
json_extract(timers.refused, '$.head')),
|
|
78
|
+
'holds', max(COALESCE(json_extract(excluded.refused, '$.holds'), 0),
|
|
79
|
+
COALESCE(json_extract(timers.refused, '$.holds'), 0)))
|
|
80
|
+
END`)
|
|
81
|
+
.run(timer.executionId, timer.wakeAt, timer.redrive !== undefined ? JSON.stringify(timer.redrive) : null, timer.refused !== undefined ? JSON.stringify(timer.refused) : null);
|
|
82
|
+
}
|
|
83
|
+
async due(now, limit = 100, options) {
|
|
84
|
+
const rows = this.db
|
|
85
|
+
.prepare(`SELECT execution_id, wake_at, redrive, refused FROM timers WHERE wake_at <= ?${options?.redrive === false ? " AND redrive IS NULL" : ""}${options?.refused === false ? " AND refused IS NULL" : ""}${options?.inbox === false ? " AND (wake_at <> -1 OR redrive IS NOT NULL)" : ""} ORDER BY wake_at ASC LIMIT ?`)
|
|
86
|
+
.all(now, limit);
|
|
87
|
+
return rows.map((r) => ({
|
|
88
|
+
executionId: r.execution_id,
|
|
89
|
+
wakeAt: r.wake_at,
|
|
90
|
+
...(r.redrive !== null && { redrive: JSON.parse(r.redrive) }),
|
|
91
|
+
...(r.refused !== null && {
|
|
92
|
+
refused: JSON.parse(r.refused),
|
|
93
|
+
}),
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
async complete(executionId, wakeAt) {
|
|
97
|
+
this.db
|
|
98
|
+
.prepare("DELETE FROM timers WHERE execution_id = ? AND wake_at = ?")
|
|
99
|
+
.run(executionId, wakeAt);
|
|
100
|
+
}
|
|
101
|
+
async close() {
|
|
102
|
+
this.db.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.SqliteTimerStore = SqliteTimerStore;
|
|
106
|
+
//# sourceMappingURL=SqliteTimerStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteTimerStore.js","sourceRoot":"","sources":["../src/SqliteTimerStore.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { SqliteEventStore } from "./SqliteEventStore";
|
|
2
|
+
export { SqliteBranchRegistry } from "./SqliteBranchRegistry";
|
|
3
|
+
export { SqliteLeaseStore } from "./SqliteLeaseStore";
|
|
4
|
+
export { SqliteTimerStore } from "./SqliteTimerStore";
|
|
5
|
+
export { SqliteSnapshotStore } from "./SqliteSnapshotStore";
|
|
6
|
+
export type { SqliteSnapshotStoreOptions } from "./SqliteSnapshotStore";
|
|
7
|
+
export { SqliteArchiveStore } from "./SqliteArchiveStore";
|
|
8
|
+
export { SqliteDurableStateStore } from "./SqliteDurableStateStore";
|
|
9
|
+
export { SqliteReadModelStore } from "./SqliteReadModelStore";
|
|
10
|
+
export { SqliteContentStore, type SqliteContentStoreOptions } from "./SqliteContentStore";
|
|
11
|
+
export { SqliteMemoryStore } from "./SqliteMemoryStore";
|
|
12
|
+
export { sqliteStorage, type SqliteStorageOptions } from "./preset";
|
|
13
|
+
//# 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,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sqliteStorage = exports.SqliteMemoryStore = exports.SqliteContentStore = exports.SqliteReadModelStore = exports.SqliteDurableStateStore = exports.SqliteArchiveStore = exports.SqliteSnapshotStore = exports.SqliteTimerStore = exports.SqliteLeaseStore = exports.SqliteBranchRegistry = exports.SqliteEventStore = void 0;
|
|
4
|
+
var SqliteEventStore_1 = require("./SqliteEventStore");
|
|
5
|
+
Object.defineProperty(exports, "SqliteEventStore", { enumerable: true, get: function () { return SqliteEventStore_1.SqliteEventStore; } });
|
|
6
|
+
var SqliteBranchRegistry_1 = require("./SqliteBranchRegistry");
|
|
7
|
+
Object.defineProperty(exports, "SqliteBranchRegistry", { enumerable: true, get: function () { return SqliteBranchRegistry_1.SqliteBranchRegistry; } });
|
|
8
|
+
var SqliteLeaseStore_1 = require("./SqliteLeaseStore");
|
|
9
|
+
Object.defineProperty(exports, "SqliteLeaseStore", { enumerable: true, get: function () { return SqliteLeaseStore_1.SqliteLeaseStore; } });
|
|
10
|
+
var SqliteTimerStore_1 = require("./SqliteTimerStore");
|
|
11
|
+
Object.defineProperty(exports, "SqliteTimerStore", { enumerable: true, get: function () { return SqliteTimerStore_1.SqliteTimerStore; } });
|
|
12
|
+
var SqliteSnapshotStore_1 = require("./SqliteSnapshotStore");
|
|
13
|
+
Object.defineProperty(exports, "SqliteSnapshotStore", { enumerable: true, get: function () { return SqliteSnapshotStore_1.SqliteSnapshotStore; } });
|
|
14
|
+
var SqliteArchiveStore_1 = require("./SqliteArchiveStore");
|
|
15
|
+
Object.defineProperty(exports, "SqliteArchiveStore", { enumerable: true, get: function () { return SqliteArchiveStore_1.SqliteArchiveStore; } });
|
|
16
|
+
var SqliteDurableStateStore_1 = require("./SqliteDurableStateStore");
|
|
17
|
+
Object.defineProperty(exports, "SqliteDurableStateStore", { enumerable: true, get: function () { return SqliteDurableStateStore_1.SqliteDurableStateStore; } });
|
|
18
|
+
var SqliteReadModelStore_1 = require("./SqliteReadModelStore");
|
|
19
|
+
Object.defineProperty(exports, "SqliteReadModelStore", { enumerable: true, get: function () { return SqliteReadModelStore_1.SqliteReadModelStore; } });
|
|
20
|
+
var SqliteContentStore_1 = require("./SqliteContentStore");
|
|
21
|
+
Object.defineProperty(exports, "SqliteContentStore", { enumerable: true, get: function () { return SqliteContentStore_1.SqliteContentStore; } });
|
|
22
|
+
var SqliteMemoryStore_1 = require("./SqliteMemoryStore");
|
|
23
|
+
Object.defineProperty(exports, "SqliteMemoryStore", { enumerable: true, get: function () { return SqliteMemoryStore_1.SqliteMemoryStore; } });
|
|
24
|
+
var preset_1 = require("./preset");
|
|
25
|
+
Object.defineProperty(exports, "sqliteStorage", { enumerable: true, get: function () { return preset_1.sqliteStorage; } });
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAE5B,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,qEAAoE;AAA3D,kIAAA,uBAAuB,OAAA;AAChC,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,2DAA0F;AAAjF,wHAAA,kBAAkB,OAAA;AAC3B,yDAAwD;AAA/C,sHAAA,iBAAiB,OAAA;AAE1B,mCAAoE;AAA3D,uGAAA,aAAa,OAAA"}
|
package/dist/preset.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { LeaseStore } from "@loomwork/event-runtime";
|
|
2
|
+
import type { CustomStorage } from "@loomwork/plugin-kit";
|
|
3
|
+
import type { SnapshotCodecName } from "@loomwork/snapshot-runtime";
|
|
4
|
+
export interface SqliteStorageOptions {
|
|
5
|
+
/** Directory holding the `.db` files. Defaults to `.loom`. */
|
|
6
|
+
dir?: string;
|
|
7
|
+
/** Also provision a branch registry (its own DB file to avoid write contention). */
|
|
8
|
+
branching?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* ADR 0141 - opt in to zstd-compressed snapshot states (default `"none"`: JSON text, as before).
|
|
11
|
+
* Needs Node >= 22.15 (throws at construction otherwise). Old snapshots stay readable; see
|
|
12
|
+
* `SqliteSnapshotStoreOptions.codec`.
|
|
13
|
+
*/
|
|
14
|
+
snapshotCodec?: SnapshotCodecName;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A `defineConfig({ storage })` preset for embedded SQLite. Returns a `custom`
|
|
18
|
+
* storage descriptor with the SQLite stores constructed — so `@loomwork/app` never
|
|
19
|
+
* needs to know SQLite exists (ADR 0043: 1P/3P storage backends are symmetric,
|
|
20
|
+
* each supplied as a preset, none privileged with a `kind`).
|
|
21
|
+
*
|
|
22
|
+
* `leaseStore` (ADR 0130 D11) is the heartbeat-lease store CO-LOCATED with the event store: the
|
|
23
|
+
* same `events.db`, so the lease and the log share one failure domain. It is an accessor on
|
|
24
|
+
* the factory's result, not part of `CustomStorage`, so there is no `EventStore` SPI change;
|
|
25
|
+
* `defineConfig` does not read it yet. It is opened lazily on first access (its own connection) and
|
|
26
|
+
* closed by `eventStore.close()`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sqliteStorage(options?: SqliteStorageOptions): CustomStorage & {
|
|
29
|
+
leaseStore: LeaseStore;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=preset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAOpE,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,oBAAyB,GACjC,aAAa,GAAG;IAAE,UAAU,EAAE,UAAU,CAAA;CAAE,CA2B5C"}
|
package/dist/preset.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sqliteStorage = sqliteStorage;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const SqliteArchiveStore_1 = require("./SqliteArchiveStore");
|
|
7
|
+
const SqliteBranchRegistry_1 = require("./SqliteBranchRegistry");
|
|
8
|
+
const SqliteEventStore_1 = require("./SqliteEventStore");
|
|
9
|
+
const SqliteLeaseStore_1 = require("./SqliteLeaseStore");
|
|
10
|
+
const SqliteSnapshotStore_1 = require("./SqliteSnapshotStore");
|
|
11
|
+
/**
|
|
12
|
+
* A `defineConfig({ storage })` preset for embedded SQLite. Returns a `custom`
|
|
13
|
+
* storage descriptor with the SQLite stores constructed — so `@loomwork/app` never
|
|
14
|
+
* needs to know SQLite exists (ADR 0043: 1P/3P storage backends are symmetric,
|
|
15
|
+
* each supplied as a preset, none privileged with a `kind`).
|
|
16
|
+
*
|
|
17
|
+
* `leaseStore` (ADR 0130 D11) is the heartbeat-lease store CO-LOCATED with the event store: the
|
|
18
|
+
* same `events.db`, so the lease and the log share one failure domain. It is an accessor on
|
|
19
|
+
* the factory's result, not part of `CustomStorage`, so there is no `EventStore` SPI change;
|
|
20
|
+
* `defineConfig` does not read it yet. It is opened lazily on first access (its own connection) and
|
|
21
|
+
* closed by `eventStore.close()`.
|
|
22
|
+
*/
|
|
23
|
+
function sqliteStorage(options = {}) {
|
|
24
|
+
const dir = options.dir ?? ".loom";
|
|
25
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
26
|
+
const eventStore = new SqliteEventStore_1.SqliteEventStore((0, node_path_1.join)(dir, "events.db"));
|
|
27
|
+
// Lazy: a second events.db connection + migrate is opened only when something reads
|
|
28
|
+
// `leaseStore`, and it is closed together with the event store (as the Postgres preset does).
|
|
29
|
+
let lease;
|
|
30
|
+
const closeEvents = eventStore.close.bind(eventStore);
|
|
31
|
+
eventStore.close = async () => {
|
|
32
|
+
await closeEvents();
|
|
33
|
+
await lease?.close();
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
kind: "custom",
|
|
37
|
+
eventStore,
|
|
38
|
+
snapshotStore: new SqliteSnapshotStore_1.SqliteSnapshotStore((0, node_path_1.join)(dir, "snapshots.db"), {
|
|
39
|
+
...(options.snapshotCodec !== undefined && { codec: options.snapshotCodec }),
|
|
40
|
+
}),
|
|
41
|
+
archiveStore: new SqliteArchiveStore_1.SqliteArchiveStore((0, node_path_1.join)(dir, "archive.db")),
|
|
42
|
+
get leaseStore() {
|
|
43
|
+
lease ??= new SqliteLeaseStore_1.SqliteLeaseStore((0, node_path_1.join)(dir, "events.db"));
|
|
44
|
+
return lease;
|
|
45
|
+
},
|
|
46
|
+
...(options.branching === true && {
|
|
47
|
+
branchRegistry: new SqliteBranchRegistry_1.SqliteBranchRegistry((0, node_path_1.join)(dir, "branches.db")),
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=preset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset.js","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":";;AAoCA,sCA6BC;AAjED,qCAAoC;AACpC,yCAAiC;AAIjC,6DAA0D;AAC1D,iEAA8D;AAC9D,yDAAsD;AACtD,yDAAsD;AACtD,+DAA4D;AAe5D;;;;;;;;;;;GAWG;AACH,SAAgB,aAAa,CAC3B,UAAgC,EAAE;IAElC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC;IACnC,IAAA,mBAAS,EAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,mCAAgB,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC;IAChE,oFAAoF;IACpF,8FAA8F;IAC9F,IAAI,KAAmC,CAAC;IACxC,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtD,UAAU,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE;QAC5B,MAAM,WAAW,EAAE,CAAC;QACpB,MAAM,KAAK,EAAE,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC;IACF,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU;QACV,aAAa,EAAE,IAAI,yCAAmB,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,cAAc,CAAC,EAAE;YAChE,GAAG,CAAC,OAAO,CAAC,aAAa,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;SAC7E,CAAC;QACF,YAAY,EAAE,IAAI,uCAAkB,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAC7D,IAAI,UAAU;YACZ,KAAK,KAAK,IAAI,mCAAgB,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC;YACvD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI;YAChC,cAAc,EAAE,IAAI,2CAAoB,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,aAAa,CAAC,CAAC;SACnE,CAAC;KACH,CAAC;AACJ,CAAC"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,wBAAgB,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CA6DnD"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migrate = migrate;
|
|
4
|
+
function migrate(db) {
|
|
5
|
+
db.exec(`
|
|
6
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
7
|
+
id TEXT NOT NULL,
|
|
8
|
+
workflow_id TEXT NOT NULL,
|
|
9
|
+
execution_id TEXT NOT NULL,
|
|
10
|
+
event_type TEXT NOT NULL,
|
|
11
|
+
sequence INTEGER NOT NULL,
|
|
12
|
+
timestamp INTEGER NOT NULL,
|
|
13
|
+
payload TEXT NOT NULL,
|
|
14
|
+
metadata TEXT,
|
|
15
|
+
PRIMARY KEY (id),
|
|
16
|
+
UNIQUE (execution_id, sequence)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_events_exec
|
|
20
|
+
ON events (execution_id, sequence ASC);
|
|
21
|
+
|
|
22
|
+
CREATE TABLE IF NOT EXISTS executions (
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
workflow_id TEXT NOT NULL,
|
|
25
|
+
workflow_version TEXT NOT NULL,
|
|
26
|
+
status TEXT NOT NULL,
|
|
27
|
+
created_at INTEGER NOT NULL,
|
|
28
|
+
updated_at INTEGER NOT NULL
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
-- Branch registry: git-style movable pointers (refs) into the event-log
|
|
32
|
+
-- topology. A ref names an execution head; promote() repoints a ref onto a
|
|
33
|
+
-- winning execution ("pick-a-winner"). No merge — see BranchRegistry.
|
|
34
|
+
CREATE TABLE IF NOT EXISTS refs (
|
|
35
|
+
name TEXT PRIMARY KEY,
|
|
36
|
+
execution_id TEXT NOT NULL,
|
|
37
|
+
tip INTEGER NOT NULL,
|
|
38
|
+
parent_execution_id TEXT,
|
|
39
|
+
fork_sequence INTEGER,
|
|
40
|
+
created_at INTEGER NOT NULL,
|
|
41
|
+
updated_at INTEGER NOT NULL
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
-- Immutable fork DAG: each forked execution records its parent and the
|
|
45
|
+
-- sequence it forked at. One row per child execution.
|
|
46
|
+
-- #380 — durable ownership of a RUNNING execution; the primary key IS the exclusion.
|
|
47
|
+
CREATE TABLE IF NOT EXISTS leases (
|
|
48
|
+
execution_id TEXT NOT NULL PRIMARY KEY,
|
|
49
|
+
owner TEXT NOT NULL,
|
|
50
|
+
expires_at INTEGER NOT NULL
|
|
51
|
+
);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_leases_expiry ON leases (expires_at ASC);
|
|
53
|
+
|
|
54
|
+
CREATE TABLE IF NOT EXISTS lineage (
|
|
55
|
+
execution_id TEXT PRIMARY KEY,
|
|
56
|
+
parent_execution_id TEXT NOT NULL,
|
|
57
|
+
fork_sequence INTEGER NOT NULL,
|
|
58
|
+
created_at INTEGER NOT NULL
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_lineage_parent
|
|
62
|
+
ON lineage (parent_execution_id);
|
|
63
|
+
`);
|
|
64
|
+
migrateIncarnation(db);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* ADR 0129 — the store-assigned incarnation of an execution, kept in STORE METADATA only (never
|
|
68
|
+
* in an envelope, so replay stays byte-identical). It lives on the sequence-0 row and is filled
|
|
69
|
+
* by a trigger from a one-row counter, so EVERY way a sequence 0 can land (append, appendBatch,
|
|
70
|
+
* copyPrefix, appendIfUnchanged, another process) assigns one. The counter is never reset, so a
|
|
71
|
+
* deleted-and-recreated id always gets a larger value. Logs created before this column existed
|
|
72
|
+
* keep NULL (no backfill): their anchor stays the legacy `timestamp:id`. All steps are
|
|
73
|
+
* idempotent and tolerate two processes migrating at once.
|
|
74
|
+
*/
|
|
75
|
+
function migrateIncarnation(db) {
|
|
76
|
+
const columns = db.prepare("PRAGMA table_info(events)").all();
|
|
77
|
+
if (!columns.some((c) => c.name === "incarnation")) {
|
|
78
|
+
try {
|
|
79
|
+
db.exec("ALTER TABLE events ADD COLUMN incarnation INTEGER");
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
// A concurrent migrator added it between our check and our ALTER.
|
|
83
|
+
if (!/duplicate column name/i.test(String(err.message)))
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
db.exec(`
|
|
88
|
+
CREATE TABLE IF NOT EXISTS incarnation_counter (
|
|
89
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
90
|
+
n INTEGER NOT NULL
|
|
91
|
+
);
|
|
92
|
+
INSERT OR IGNORE INTO incarnation_counter (id, n) VALUES (1, 0);
|
|
93
|
+
CREATE TRIGGER IF NOT EXISTS trg_events_incarnation
|
|
94
|
+
AFTER INSERT ON events
|
|
95
|
+
WHEN NEW.sequence = 0
|
|
96
|
+
BEGIN
|
|
97
|
+
UPDATE incarnation_counter SET n = n + 1 WHERE id = 1;
|
|
98
|
+
UPDATE events SET incarnation = (SELECT n FROM incarnation_counter WHERE id = 1)
|
|
99
|
+
WHERE id = NEW.id;
|
|
100
|
+
END;
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":";;AAEA,0BA6DC;AA7DD,SAAgB,OAAO,CAAC,EAAqB;IAC3C,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DP,CAAC,CAAC;IACH,kBAAkB,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,kBAAkB,CAAC,EAAqB;IAC/C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAuB,2BAA2B,CAAC,CAAC,GAAG,EAAE,CAAC;IACpF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,EAAE,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,kEAAkE;YAClE,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;gBAAE,MAAM,GAAG,CAAC;QAChF,CAAC;IACH,CAAC;IACD,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;GAcP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotSchema.d.ts","sourceRoot":"","sources":["../src/snapshotSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,wBAAgB,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAcnD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migrate = migrate;
|
|
4
|
+
function migrate(db) {
|
|
5
|
+
db.exec(`
|
|
6
|
+
CREATE TABLE IF NOT EXISTS snapshots (
|
|
7
|
+
id TEXT PRIMARY KEY,
|
|
8
|
+
execution_id TEXT NOT NULL,
|
|
9
|
+
workflow_id TEXT NOT NULL,
|
|
10
|
+
sequence INTEGER NOT NULL,
|
|
11
|
+
state TEXT NOT NULL,
|
|
12
|
+
created_at INTEGER NOT NULL
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_exec
|
|
16
|
+
ON snapshots (execution_id, sequence DESC);
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=snapshotSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotSchema.js","sourceRoot":"","sources":["../src/snapshotSchema.ts"],"names":[],"mappings":";;AAEA,0BAcC;AAdD,SAAgB,OAAO,CAAC,EAAqB;IAC3C,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;GAYP,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loomwork/plugin-sqlite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "SQLite store plugin for Loom — embedded EventStore, SnapshotStore, BranchRegistry, TimerStore, DurableStateStore, ReadModelStore, ContentStore and MemoryStore over better-sqlite3, behind the same interfaces as the in-memory reference. The zero-server durable backend; keeps the native driver out of the kernel packages.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"better-sqlite3": "^11.5.0",
|
|
16
|
+
"@loomwork/core": "^0.1.1",
|
|
17
|
+
"@loomwork/event-runtime": "^0.1.1",
|
|
18
|
+
"@loomwork/snapshot-runtime": "^0.1.1",
|
|
19
|
+
"@loomwork/plugin-durable-state": "^0.1.1",
|
|
20
|
+
"@loomwork/plugin-read-model": "^0.1.1",
|
|
21
|
+
"@loomwork/plugin-content": "^0.1.1",
|
|
22
|
+
"@loomwork/plugin-kit": "^0.1.1",
|
|
23
|
+
"@loomwork/memory": "^0.1.1"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
27
|
+
"@types/node": "^22.0.0",
|
|
28
|
+
"typescript": "^5.6.3",
|
|
29
|
+
"vitest": "^2.1.8",
|
|
30
|
+
"@loomwork/conformance": "0.1.1"
|
|
31
|
+
},
|
|
32
|
+
"license": "Apache-2.0",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/chhetrisushil/loom.git",
|
|
36
|
+
"directory": "plugins/sqlite"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"src",
|
|
41
|
+
"!src/**/*.test.ts",
|
|
42
|
+
"!src/**/*.test.tsx",
|
|
43
|
+
"!src/**/__tests__",
|
|
44
|
+
"!src/**/__fixtures__",
|
|
45
|
+
"!src/**/__snapshots__",
|
|
46
|
+
"!src/**/__golden__",
|
|
47
|
+
"LICENSE",
|
|
48
|
+
"NOTICE"
|
|
49
|
+
],
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public",
|
|
52
|
+
"tag": "next"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=20.0.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsc --build",
|
|
59
|
+
"dev": "tsc --build --watch",
|
|
60
|
+
"test": "vitest run",
|
|
61
|
+
"test:watch": "vitest",
|
|
62
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
63
|
+
"typecheck:tests": "tsc -p tsconfig.test.json"
|
|
64
|
+
}
|
|
6
65
|
}
|