@rotorsoft/act-sqlite 1.4.0 → 1.4.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/dist/.tsbuildinfo +1 -1
- package/dist/@types/sqlite-store.d.ts +3 -3
- package/dist/@types/sqlite-store.d.ts.map +1 -1
- package/dist/index.cjs +74 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +74 -69
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -87,23 +87,23 @@ var SqliteStore = class {
|
|
|
87
87
|
async commit(stream, msgs, meta, expectedVersion) {
|
|
88
88
|
const tx = await this.client.transaction("write");
|
|
89
89
|
try {
|
|
90
|
-
const
|
|
90
|
+
const version_row = await tx.execute({
|
|
91
91
|
sql: "SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?",
|
|
92
92
|
args: [stream]
|
|
93
93
|
});
|
|
94
|
-
const
|
|
95
|
-
if (typeof expectedVersion === "number" &&
|
|
94
|
+
const current_version = Number(version_row.rows[0].v);
|
|
95
|
+
if (typeof expectedVersion === "number" && current_version !== expectedVersion) {
|
|
96
96
|
const { ConcurrencyError } = await import("@rotorsoft/act");
|
|
97
97
|
throw new ConcurrencyError(
|
|
98
98
|
stream,
|
|
99
|
-
|
|
99
|
+
current_version,
|
|
100
100
|
msgs,
|
|
101
101
|
expectedVersion
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
105
105
|
const committed = [];
|
|
106
|
-
let version =
|
|
106
|
+
let version = current_version + 1;
|
|
107
107
|
for (const { name, data } of msgs) {
|
|
108
108
|
const result = await tx.execute({
|
|
109
109
|
sql: "INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, ?, ?, ?, ?, ?)",
|
|
@@ -246,10 +246,10 @@ var SqliteStore = class {
|
|
|
246
246
|
const tx = await this.client.transaction("write");
|
|
247
247
|
try {
|
|
248
248
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
249
|
-
const
|
|
249
|
+
const lane_clause = lane !== void 0 ? " AND lane = ?" : "";
|
|
250
250
|
const result = await tx.execute({
|
|
251
251
|
sql: `SELECT stream, source, at, priority, lane FROM streams
|
|
252
|
-
WHERE blocked = 0 AND (leased_until IS NULL OR leased_until <= ?)${
|
|
252
|
+
WHERE blocked = 0 AND (leased_until IS NULL OR leased_until <= ?)${lane_clause}
|
|
253
253
|
ORDER BY priority DESC, at ASC`,
|
|
254
254
|
args: lane !== void 0 ? [now, lane] : [now]
|
|
255
255
|
});
|
|
@@ -258,21 +258,21 @@ var SqliteStore = class {
|
|
|
258
258
|
const stream = row.stream;
|
|
259
259
|
const source = row.source;
|
|
260
260
|
const at = Number(row.at);
|
|
261
|
-
let
|
|
261
|
+
let has_events;
|
|
262
262
|
if (source) {
|
|
263
263
|
const check = await tx.execute({
|
|
264
264
|
sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' AND stream LIKE ? LIMIT 1`,
|
|
265
265
|
args: [at, streamPatternToLike(source)]
|
|
266
266
|
});
|
|
267
|
-
|
|
267
|
+
has_events = check.rows.length > 0;
|
|
268
268
|
} else {
|
|
269
269
|
const check = await tx.execute({
|
|
270
270
|
sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' LIMIT 1`,
|
|
271
271
|
args: [at]
|
|
272
272
|
});
|
|
273
|
-
|
|
273
|
+
has_events = check.rows.length > 0;
|
|
274
274
|
}
|
|
275
|
-
if (
|
|
275
|
+
if (has_events) {
|
|
276
276
|
candidates.push({
|
|
277
277
|
stream,
|
|
278
278
|
source: source ?? void 0,
|
|
@@ -359,7 +359,7 @@ var SqliteStore = class {
|
|
|
359
359
|
* plus positional args. Returns `"1"` (always true) when empty so
|
|
360
360
|
* callers can compose it unconditionally.
|
|
361
361
|
*/
|
|
362
|
-
|
|
362
|
+
_filter_clause(filter) {
|
|
363
363
|
const conditions = [];
|
|
364
364
|
const args = [];
|
|
365
365
|
if (filter.stream !== void 0) {
|
|
@@ -393,7 +393,7 @@ var SqliteStore = class {
|
|
|
393
393
|
}
|
|
394
394
|
// --- reset: transactional, accepts names or filter ---
|
|
395
395
|
async reset(input) {
|
|
396
|
-
const
|
|
396
|
+
const set_clause = `SET at = -1, retry = 0, blocked = 0, error = '',
|
|
397
397
|
leased_by = NULL, leased_until = NULL`;
|
|
398
398
|
const tx = await this.client.transaction("write");
|
|
399
399
|
try {
|
|
@@ -401,15 +401,15 @@ var SqliteStore = class {
|
|
|
401
401
|
if (Array.isArray(input)) {
|
|
402
402
|
for (const stream of input) {
|
|
403
403
|
const r = await tx.execute({
|
|
404
|
-
sql: `UPDATE streams ${
|
|
404
|
+
sql: `UPDATE streams ${set_clause} WHERE stream = ?`,
|
|
405
405
|
args: [stream]
|
|
406
406
|
});
|
|
407
407
|
count += r.rowsAffected;
|
|
408
408
|
}
|
|
409
409
|
} else {
|
|
410
|
-
const { clause, args } = this.
|
|
410
|
+
const { clause, args } = this._filter_clause(input);
|
|
411
411
|
const r = await tx.execute({
|
|
412
|
-
sql: `UPDATE streams ${
|
|
412
|
+
sql: `UPDATE streams ${set_clause} WHERE ${clause}`,
|
|
413
413
|
args
|
|
414
414
|
});
|
|
415
415
|
count = r.rowsAffected;
|
|
@@ -425,7 +425,7 @@ var SqliteStore = class {
|
|
|
425
425
|
// `retry = -1` so claim's post-bump returns retry=0 (first attempt),
|
|
426
426
|
// matching the InMemoryStore convention.
|
|
427
427
|
async unblock(input) {
|
|
428
|
-
const
|
|
428
|
+
const set_clause = `SET retry = -1, blocked = 0, error = '',
|
|
429
429
|
leased_by = NULL, leased_until = NULL`;
|
|
430
430
|
const tx = await this.client.transaction("write");
|
|
431
431
|
try {
|
|
@@ -433,19 +433,19 @@ var SqliteStore = class {
|
|
|
433
433
|
if (Array.isArray(input)) {
|
|
434
434
|
for (const stream of input) {
|
|
435
435
|
const r = await tx.execute({
|
|
436
|
-
sql: `UPDATE streams ${
|
|
436
|
+
sql: `UPDATE streams ${set_clause}
|
|
437
437
|
WHERE stream = ? AND blocked = 1`,
|
|
438
438
|
args: [stream]
|
|
439
439
|
});
|
|
440
440
|
count += r.rowsAffected;
|
|
441
441
|
}
|
|
442
442
|
} else {
|
|
443
|
-
const { clause, args } = this.
|
|
443
|
+
const { clause, args } = this._filter_clause({
|
|
444
444
|
...input,
|
|
445
445
|
blocked: true
|
|
446
446
|
});
|
|
447
447
|
const r = await tx.execute({
|
|
448
|
-
sql: `UPDATE streams ${
|
|
448
|
+
sql: `UPDATE streams ${set_clause} WHERE ${clause}`,
|
|
449
449
|
args
|
|
450
450
|
});
|
|
451
451
|
count = r.rowsAffected;
|
|
@@ -544,11 +544,11 @@ var SqliteStore = class {
|
|
|
544
544
|
*/
|
|
545
545
|
async query_stats(input, options) {
|
|
546
546
|
const exclude = options?.exclude ?? [];
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
const
|
|
547
|
+
const want_tail = options?.tail ?? false;
|
|
548
|
+
const want_count = options?.count ?? false;
|
|
549
|
+
const want_names = options?.names ?? false;
|
|
550
550
|
const before = options?.before;
|
|
551
|
-
const
|
|
551
|
+
const full_scan = want_count || want_names;
|
|
552
552
|
if (Array.isArray(input) && input.length === 0) {
|
|
553
553
|
return /* @__PURE__ */ new Map();
|
|
554
554
|
}
|
|
@@ -576,38 +576,43 @@ var SqliteStore = class {
|
|
|
576
576
|
where.push(`e.id < ?`);
|
|
577
577
|
args.push(before);
|
|
578
578
|
}
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
return
|
|
582
|
-
|
|
583
|
-
|
|
579
|
+
const from_clause = `events e`;
|
|
580
|
+
const where_clause = `WHERE ${where.length ? where.join(" AND ") : "1=1"}`;
|
|
581
|
+
return full_scan ? this._query_stats_full_scan(
|
|
582
|
+
from_clause,
|
|
583
|
+
where_clause,
|
|
584
584
|
args,
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
) : this.
|
|
585
|
+
want_tail,
|
|
586
|
+
want_count,
|
|
587
|
+
want_names
|
|
588
|
+
) : this._query_stats_heads_only(
|
|
589
|
+
from_clause,
|
|
590
|
+
where_clause,
|
|
591
|
+
args,
|
|
592
|
+
want_tail
|
|
593
|
+
);
|
|
589
594
|
}
|
|
590
595
|
/**
|
|
591
596
|
* Cheap path — head (and optional tail) via ROW_NUMBER() over the
|
|
592
597
|
* `(stream, version)` unique index. Parallel queries when tail set.
|
|
593
598
|
*/
|
|
594
|
-
async
|
|
599
|
+
async _query_stats_heads_only(from_clause, where_clause, args, want_tail) {
|
|
595
600
|
const cols = `e.id, e.stream, e.version, e.name, e.data, e.created, e.meta`;
|
|
596
|
-
const
|
|
601
|
+
const head_sql = `SELECT * FROM (
|
|
597
602
|
SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version DESC) AS rn
|
|
598
|
-
FROM ${
|
|
599
|
-
${
|
|
603
|
+
FROM ${from_clause}
|
|
604
|
+
${where_clause}
|
|
600
605
|
) WHERE rn = 1`;
|
|
601
|
-
const
|
|
606
|
+
const tail_sql = want_tail ? `SELECT * FROM (
|
|
602
607
|
SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version ASC) AS rn
|
|
603
|
-
FROM ${
|
|
604
|
-
${
|
|
608
|
+
FROM ${from_clause}
|
|
609
|
+
${where_clause}
|
|
605
610
|
) WHERE rn = 1` : null;
|
|
606
611
|
const [headRes, tailRes] = await Promise.all([
|
|
607
|
-
this.client.execute({ sql:
|
|
608
|
-
|
|
612
|
+
this.client.execute({ sql: head_sql, args }),
|
|
613
|
+
tail_sql ? this.client.execute({ sql: tail_sql, args }) : null
|
|
609
614
|
]);
|
|
610
|
-
const
|
|
615
|
+
const to_committed = (row) => ({
|
|
611
616
|
id: Number(row.id),
|
|
612
617
|
stream: row.stream,
|
|
613
618
|
version: Number(row.version),
|
|
@@ -619,12 +624,12 @@ var SqliteStore = class {
|
|
|
619
624
|
const out = /* @__PURE__ */ new Map();
|
|
620
625
|
for (const row of headRes.rows) {
|
|
621
626
|
out.set(row.stream, {
|
|
622
|
-
head:
|
|
627
|
+
head: to_committed(row)
|
|
623
628
|
});
|
|
624
629
|
}
|
|
625
630
|
if (tailRes) {
|
|
626
631
|
for (const row of tailRes.rows) {
|
|
627
|
-
out.get(row.stream).tail =
|
|
632
|
+
out.get(row.stream).tail = to_committed(row);
|
|
628
633
|
}
|
|
629
634
|
}
|
|
630
635
|
return out;
|
|
@@ -634,20 +639,20 @@ var SqliteStore = class {
|
|
|
634
639
|
* `json_group_object(name, n)`. Heads (and optional tails) ride free
|
|
635
640
|
* on the same scan.
|
|
636
641
|
*/
|
|
637
|
-
async
|
|
638
|
-
const
|
|
642
|
+
async _query_stats_full_scan(from_clause, where_clause, args, want_tail, want_count, want_names) {
|
|
643
|
+
const tail_cte = want_tail ? `, tails AS (
|
|
639
644
|
SELECT * FROM (
|
|
640
645
|
SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version ASC) AS rn FROM ef
|
|
641
646
|
) WHERE rn = 1
|
|
642
647
|
)` : "";
|
|
643
|
-
const
|
|
644
|
-
const
|
|
648
|
+
const tail_join = want_tail ? `LEFT JOIN tails t ON t.stream = h.stream` : "";
|
|
649
|
+
const tail_cols = want_tail ? `, t.id AS t_id, t.stream AS t_stream, t.version AS t_version,
|
|
645
650
|
t.name AS t_name, t.data AS t_data, t.created AS t_created, t.meta AS t_meta` : "";
|
|
646
651
|
const sql = `
|
|
647
652
|
WITH ef AS (
|
|
648
653
|
SELECT e.id, e.stream, e.version, e.name, e.data, e.created, e.meta
|
|
649
|
-
FROM ${
|
|
650
|
-
${
|
|
654
|
+
FROM ${from_clause}
|
|
655
|
+
${where_clause}
|
|
651
656
|
),
|
|
652
657
|
agg AS (
|
|
653
658
|
SELECT stream,
|
|
@@ -665,18 +670,18 @@ var SqliteStore = class {
|
|
|
665
670
|
SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version DESC) AS rn FROM ef
|
|
666
671
|
) WHERE rn = 1
|
|
667
672
|
)
|
|
668
|
-
${
|
|
673
|
+
${tail_cte}
|
|
669
674
|
SELECT
|
|
670
675
|
h.id, h.stream, h.version, h.name, h.data, h.created, h.meta,
|
|
671
676
|
a.cnt AS agg_count,
|
|
672
677
|
a.names AS agg_names
|
|
673
|
-
${
|
|
678
|
+
${tail_cols}
|
|
674
679
|
FROM heads h
|
|
675
680
|
LEFT JOIN agg a ON a.stream = h.stream
|
|
676
|
-
${
|
|
681
|
+
${tail_join}
|
|
677
682
|
`;
|
|
678
683
|
const res = await this.client.execute({ sql, args });
|
|
679
|
-
const
|
|
684
|
+
const to_committed = (id, stream, version, name, data, meta, created) => ({
|
|
680
685
|
id: Number(id),
|
|
681
686
|
stream,
|
|
682
687
|
version: Number(version),
|
|
@@ -689,7 +694,7 @@ var SqliteStore = class {
|
|
|
689
694
|
for (const row of res.rows) {
|
|
690
695
|
const r = row;
|
|
691
696
|
const stats = {
|
|
692
|
-
head:
|
|
697
|
+
head: to_committed(
|
|
693
698
|
r.id,
|
|
694
699
|
r.stream,
|
|
695
700
|
r.version,
|
|
@@ -699,8 +704,8 @@ var SqliteStore = class {
|
|
|
699
704
|
r.created
|
|
700
705
|
)
|
|
701
706
|
};
|
|
702
|
-
if (
|
|
703
|
-
stats.tail =
|
|
707
|
+
if (want_tail && r.t_id !== null && r.t_id !== void 0) {
|
|
708
|
+
stats.tail = to_committed(
|
|
704
709
|
r.t_id,
|
|
705
710
|
r.t_stream,
|
|
706
711
|
r.t_version,
|
|
@@ -710,15 +715,15 @@ var SqliteStore = class {
|
|
|
710
715
|
r.t_created
|
|
711
716
|
);
|
|
712
717
|
}
|
|
713
|
-
if (
|
|
714
|
-
if (
|
|
718
|
+
if (want_count) stats.count = Number(r.agg_count);
|
|
719
|
+
if (want_names) stats.names = JSON.parse(r.agg_names);
|
|
715
720
|
out.set(r.stream, stats);
|
|
716
721
|
}
|
|
717
722
|
return out;
|
|
718
723
|
}
|
|
719
724
|
// --- prioritize: bulk priority update with filter (ACT-102) ---
|
|
720
725
|
async prioritize(filter, priority) {
|
|
721
|
-
const { clause, args: filterArgs } = this.
|
|
726
|
+
const { clause, args: filterArgs } = this._filter_clause(filter);
|
|
722
727
|
const sql = `UPDATE streams SET priority = ?
|
|
723
728
|
WHERE priority <> ? AND ${clause}`;
|
|
724
729
|
const result = await this.client.execute({
|
|
@@ -733,11 +738,11 @@ var SqliteStore = class {
|
|
|
733
738
|
const tx = await this.client.transaction("write");
|
|
734
739
|
try {
|
|
735
740
|
for (const { stream, snapshot, meta } of targets) {
|
|
736
|
-
const
|
|
741
|
+
const count_row = await tx.execute({
|
|
737
742
|
sql: "SELECT COUNT(*) as c FROM events WHERE stream = ?",
|
|
738
743
|
args: [stream]
|
|
739
744
|
});
|
|
740
|
-
const deleted = Number(
|
|
745
|
+
const deleted = Number(count_row.rows[0].c);
|
|
741
746
|
await tx.execute({
|
|
742
747
|
sql: "DELETE FROM events WHERE stream = ?",
|
|
743
748
|
args: [stream]
|
|
@@ -746,16 +751,16 @@ var SqliteStore = class {
|
|
|
746
751
|
sql: "DELETE FROM streams WHERE stream = ?",
|
|
747
752
|
args: [stream]
|
|
748
753
|
});
|
|
749
|
-
const
|
|
750
|
-
const
|
|
754
|
+
const event_name = snapshot !== void 0 ? "__snapshot__" : "__tombstone__";
|
|
755
|
+
const event_meta = meta ?? { correlation: "", causation: {} };
|
|
751
756
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
752
757
|
const ins = await tx.execute({
|
|
753
758
|
sql: "INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, 0, ?, ?, ?, ?)",
|
|
754
759
|
args: [
|
|
755
760
|
stream,
|
|
756
|
-
|
|
761
|
+
event_name,
|
|
757
762
|
JSON.stringify(snapshot ?? {}),
|
|
758
|
-
JSON.stringify(
|
|
763
|
+
JSON.stringify(event_meta),
|
|
759
764
|
now
|
|
760
765
|
]
|
|
761
766
|
});
|
|
@@ -766,9 +771,9 @@ var SqliteStore = class {
|
|
|
766
771
|
stream,
|
|
767
772
|
version: 0,
|
|
768
773
|
created: new Date(now),
|
|
769
|
-
name:
|
|
774
|
+
name: event_name,
|
|
770
775
|
data: snapshot ?? {},
|
|
771
|
-
meta:
|
|
776
|
+
meta: event_meta
|
|
772
777
|
}
|
|
773
778
|
});
|
|
774
779
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sqlite-store.ts"],"sourcesContent":["import { type Client, createClient } from \"@libsql/client\";\nimport type {\n BlockedLease,\n Committed,\n EventMeta,\n Lease,\n Message,\n Query,\n QueryStatsOptions,\n QueryStreams,\n QueryStreamsResult,\n Schemas,\n Store,\n StreamFilter,\n StreamPosition,\n StreamStats,\n} from \"@rotorsoft/act\";\n\n/**\n * SQLite store configuration\n */\nexport interface SqliteConfig {\n /** Path to the SQLite database file (default: \":memory:\") */\n url: string;\n /** Auth token for libSQL server connections (optional) */\n authToken?: string;\n}\n\nconst DEFAULT_CONFIG: SqliteConfig = {\n url: \"file::memory:\",\n};\n\n/** Translate a stream filter (regex-shaped or plain substring) into a\n * SQL LIKE pattern. Honors `^` / `$` anchors and converts `.*` → `%`,\n * `.` → `_`. Unanchored input gets `%` wildcards on both sides.\n *\n * Examples:\n * - `^abc$` → `abc` (exact)\n * - `^abc.*` → `abc%` (starts-with)\n * - `.*abc$` → `%abc` (ends-with)\n * - `abc` → `%abc%` (contains)\n * - `a.c` → `%a_c%` (single-char wildcard, contains)\n *\n * @internal exported for testing\n */\nexport function streamPatternToLike(input: string): string {\n let s = input;\n const start = s.startsWith(\"^\");\n const end = s.endsWith(\"$\");\n if (start) s = s.slice(1);\n if (end) s = s.slice(0, -1);\n s = s.replace(/\\.\\*/g, \"%\").replace(/\\./g, \"_\");\n const out = (start ? \"\" : \"%\") + s + (end ? \"\" : \"%\");\n // Collapse adjacent `%` — e.g. `^a.*` would otherwise yield `a%%`.\n // Same matching semantics, cleaner output.\n return out.replace(/%+/g, \"%\");\n}\n\n/**\n * SQLite event store adapter for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act).\n *\n * Provides persistent event storage using SQLite via `@libsql/client`.\n * All write operations use transactions for ACID guarantees.\n * Since SQLite serializes writes at the database level, the concurrency\n * model is equivalent to PostgreSQL's `FOR UPDATE SKIP LOCKED` for\n * single-server deployments.\n *\n * **`Store.notify` is intentionally not implemented.** The notify hook is\n * a cross-process wake-up signal that lets a horizontally-scaled Act\n * deployment wake `settle()` immediately on remote commits. SQLite is\n * single-node by design — there is no remote writer to be notified of —\n * so the {@link Act} orchestrator falls back to the existing\n * debounce/poll path, which is correct for this topology.\n *\n * @example\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * import { SqliteStore } from \"@rotorsoft/act-sqlite\";\n *\n * store(new SqliteStore({ url: \"file:myapp.db\" }));\n * await store().seed();\n * ```\n */\nexport class SqliteStore implements Store {\n private client: Client;\n\n constructor(config: Partial<SqliteConfig> = {}) {\n const cfg = { ...DEFAULT_CONFIG, ...config };\n this.client = createClient({\n url: cfg.url,\n authToken: cfg.authToken,\n });\n }\n\n async seed() {\n await this.client.execute(\"PRAGMA journal_mode=WAL\");\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream TEXT NOT NULL,\n version INTEGER NOT NULL,\n name TEXT NOT NULL,\n data TEXT NOT NULL,\n meta TEXT NOT NULL,\n created TEXT NOT NULL,\n UNIQUE(stream, version)\n )\n `);\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_stream ON events(stream)\"\n );\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_name ON events(name)\"\n );\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS streams (\n stream TEXT PRIMARY KEY,\n source TEXT,\n at INTEGER NOT NULL DEFAULT -1,\n retry INTEGER NOT NULL DEFAULT 0,\n blocked INTEGER NOT NULL DEFAULT 0,\n error TEXT NOT NULL DEFAULT '',\n leased_by TEXT,\n leased_until TEXT,\n priority INTEGER NOT NULL DEFAULT 0,\n lane TEXT NOT NULL DEFAULT 'default'\n )\n `);\n // Migration for tables created before priority lanes (ACT-102).\n // libSQL surfaces \"duplicate column\" as an error, hence the\n // try/swallow — this mirrors PG's `ADD COLUMN IF NOT EXISTS`.\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN priority INTEGER NOT NULL DEFAULT 0\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before drain lanes (ACT-1103).\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN lane TEXT NOT NULL DEFAULT 'default'\"\n );\n } catch {\n // already present\n }\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_claim ON streams(blocked, priority DESC, at)\"\n );\n // Lane filter index (ACT-1103).\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_lane ON streams(lane)\"\n );\n }\n\n async drop() {\n await this.client.execute(\"DROP TABLE IF EXISTS events\");\n await this.client.execute(\"DROP TABLE IF EXISTS streams\");\n }\n\n async dispose() {\n await Promise.resolve();\n this.client.close();\n }\n\n // --- commit: transaction + optimistic concurrency ---\n async commit<E extends Schemas>(\n stream: string,\n msgs: Message<E, keyof E>[],\n meta: EventMeta,\n expectedVersion?: number\n ): Promise<Committed<E, keyof E>[]> {\n const tx = await this.client.transaction(\"write\");\n try {\n const versionRow = await tx.execute({\n sql: \"SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?\",\n args: [stream],\n });\n const currentVersion = Number(versionRow.rows[0].v);\n\n if (\n typeof expectedVersion === \"number\" &&\n currentVersion !== expectedVersion\n ) {\n const { ConcurrencyError } = await import(\"@rotorsoft/act\");\n throw new ConcurrencyError(\n stream,\n currentVersion,\n msgs as Message<Schemas, keyof Schemas>[],\n expectedVersion\n );\n }\n\n const now = new Date().toISOString();\n const committed: Committed<E, keyof E>[] = [];\n let version = currentVersion + 1;\n\n for (const { name, data } of msgs) {\n const result = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, ?, ?, ?, ?, ?)\",\n args: [\n stream,\n version,\n name as string,\n JSON.stringify(data),\n JSON.stringify(meta),\n now,\n ],\n });\n committed.push({\n id: Number(result.lastInsertRowid),\n stream,\n version,\n created: new Date(now),\n name,\n data,\n meta,\n });\n version++;\n }\n\n await tx.commit();\n return committed;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- query: read-only, no transaction needed ---\n async query<E extends Schemas>(\n callback: (event: Committed<E, keyof E>) => void,\n query?: Query\n ): Promise<number> {\n let sql = \"SELECT * FROM events WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream LIKE ?\";\n args.push(streamPatternToLike(query.stream));\n }\n }\n if (query?.names) {\n sql += ` AND name IN (${query.names.map(() => \"?\").join(\",\")})`;\n args.push(...query.names);\n }\n if ((query as any)?.correlation) {\n sql += \" AND json_extract(meta, '$.correlation') = ?\";\n args.push((query as any).correlation);\n }\n if (query?.after !== undefined) {\n sql += \" AND id > ?\";\n args.push(query.after);\n }\n if (query?.before !== undefined) {\n sql += \" AND id < ?\";\n args.push(query.before);\n }\n if (query?.created_after) {\n sql += \" AND created > ?\";\n args.push(query.created_after.toISOString());\n }\n if (query?.created_before) {\n sql += \" AND created < ?\";\n args.push(query.created_before.toISOString());\n }\n if (!query?.with_snaps) {\n sql += \" AND name != '__snapshot__'\";\n }\n\n sql += query?.backward ? \" ORDER BY id DESC\" : \" ORDER BY id ASC\";\n\n if (query?.limit) {\n sql += \" LIMIT ?\";\n args.push(query.limit);\n }\n\n const result = await this.client.execute({ sql, args: args as any[] });\n let count = 0;\n\n for (const row of result.rows) {\n await Promise.resolve(\n callback({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n created: new Date(row.created as string),\n name: row.name as string,\n data: JSON.parse(row.data as string),\n meta: JSON.parse(row.meta as string),\n })\n );\n count++;\n }\n\n return count;\n }\n\n // --- subscribe: idempotent INSERT OR IGNORE (= PG ON CONFLICT DO NOTHING)\n // plus a UPDATE pass to keep the *max* priority across reactions\n // targeting the same stream (ACT-102). Operator overrides go\n // through `prioritize()` instead.\n async subscribe(\n streams: Array<{\n stream: string;\n source?: string;\n priority?: number;\n lane?: string;\n }>\n ) {\n const tx = await this.client.transaction(\"write\");\n try {\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = \"default\",\n } of streams) {\n const inserted = await tx.execute({\n sql: \"INSERT OR IGNORE INTO streams (stream, source, priority, lane) VALUES (?, ?, ?, ?)\",\n args: [stream, source ?? null, priority, lane],\n });\n if (inserted.rowsAffected > 0) {\n subscribed++;\n } else {\n if (priority > 0) {\n await tx.execute({\n sql: \"UPDATE streams SET priority = ? WHERE stream = ? AND priority < ?\",\n args: [priority, stream, priority],\n });\n }\n // ACT-1103: current subscribe wins on lane.\n await tx.execute({\n sql: \"UPDATE streams SET lane = ? WHERE stream = ? AND lane <> ?\",\n args: [lane, stream, lane],\n });\n }\n }\n const wm = await tx.execute(\n \"SELECT COALESCE(MAX(at), -1) as w FROM streams\"\n );\n await tx.commit();\n return { subscribed, watermark: Number(wm.rows[0].w) };\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- claim: write transaction (SQLite serializes writes = equivalent\n // to PG FOR UPDATE SKIP LOCKED for single-server) ---\n async claim(\n lagging: number,\n leading: number,\n by: string,\n millis: number,\n lane?: string\n ) {\n const tx = await this.client.transaction(\"write\");\n try {\n const now = new Date().toISOString();\n\n const laneClause = lane !== undefined ? \" AND lane = ?\" : \"\";\n const result = await tx.execute({\n sql: `SELECT stream, source, at, priority, lane FROM streams\n WHERE blocked = 0 AND (leased_until IS NULL OR leased_until <= ?)${laneClause}\n ORDER BY priority DESC, at ASC`,\n args: lane !== undefined ? [now, lane] : [now],\n });\n\n const candidates: {\n stream: string;\n source: string | undefined;\n at: number;\n priority: number;\n lane: string;\n }[] = [];\n for (const row of result.rows) {\n const stream = row.stream as string;\n const source = row.source as string | null;\n const at = Number(row.at);\n\n let hasEvents: boolean;\n if (source) {\n const check = await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' AND stream LIKE ? LIMIT 1`,\n args: [at, streamPatternToLike(source)],\n });\n hasEvents = check.rows.length > 0;\n } else {\n const check = await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' LIMIT 1`,\n args: [at],\n });\n hasEvents = check.rows.length > 0;\n }\n\n if (hasEvents) {\n candidates.push({\n stream,\n source: source ?? undefined,\n at,\n priority: Number(row.priority),\n lane: row.lane as string,\n });\n }\n }\n\n // Dual frontier: lagging (priority DESC, watermark ASC — ACT-102)\n // + leading (newest first). The candidates list arrives sorted\n // by `priority DESC, at ASC` from the SELECT above, so the\n // `slice(0, lagging)` already does the right thing.\n const lag = candidates.slice(0, lagging);\n const lead = [...candidates]\n .sort((a, b) => b.at - a.at)\n .slice(0, leading);\n const seen = new Set<string>();\n const combined = [...lag, ...lead].filter((p) => {\n if (seen.has(p.stream)) return false;\n seen.add(p.stream);\n return true;\n });\n\n const leases: Lease[] = [];\n const until = new Date(Date.now() + millis).toISOString();\n for (const row of combined) {\n await tx.execute({\n sql: \"UPDATE streams SET leased_by = ?, leased_until = ?, retry = retry + 1 WHERE stream = ?\",\n args: [by, until, row.stream],\n });\n leases.push({\n stream: row.stream,\n source: row.source,\n at: row.at,\n by,\n retry: 0,\n lagging: row.at < 0,\n lane: row.lane,\n });\n }\n\n await tx.commit();\n return leases;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- ack: transaction + ownership check (= PG WHERE leased_by) ---\n async ack(leases: Lease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n const result: Lease[] = [];\n for (const l of leases) {\n const r = await tx.execute({\n sql: `UPDATE streams SET at = ?, leased_by = NULL, leased_until = NULL, retry = -1\n WHERE stream = ? AND leased_by = ?`,\n args: [l.at, l.stream, l.by],\n });\n if (r.rowsAffected > 0) result.push(l);\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- block: transaction + ownership + idempotent (= PG) ---\n async block(leases: BlockedLease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n const result: BlockedLease[] = [];\n for (const l of leases) {\n const r = await tx.execute({\n sql: `UPDATE streams SET blocked = 1, error = ?\n WHERE stream = ? AND leased_by = ? AND blocked = 0`,\n args: [l.error, l.stream, l.by],\n });\n if (r.rowsAffected > 0) result.push(l);\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n /**\n * Translate a {@link StreamFilter} to a SQLite `WHERE` clause fragment\n * plus positional args. Returns `\"1\"` (always true) when empty so\n * callers can compose it unconditionally.\n */\n private _filterClause(filter: StreamFilter): {\n clause: string;\n args: unknown[];\n } {\n const conditions: string[] = [];\n const args: unknown[] = [];\n if (filter.stream !== undefined) {\n if (filter.stream_exact) {\n conditions.push(\"stream = ?\");\n args.push(filter.stream);\n } else {\n conditions.push(\"stream LIKE ?\");\n args.push(streamPatternToLike(filter.stream));\n }\n }\n if (filter.source !== undefined) {\n conditions.push(\"source IS NOT NULL\");\n if (filter.source_exact) {\n conditions.push(\"source = ?\");\n args.push(filter.source);\n } else {\n conditions.push(\"source LIKE ?\");\n args.push(streamPatternToLike(filter.source));\n }\n }\n if (filter.blocked !== undefined) {\n conditions.push(\"blocked = ?\");\n args.push(filter.blocked ? 1 : 0);\n }\n if (filter.lane !== undefined) {\n conditions.push(\"lane = ?\");\n args.push(filter.lane);\n }\n return { clause: conditions.length ? conditions.join(\" AND \") : \"1\", args };\n }\n\n // --- reset: transactional, accepts names or filter ---\n async reset(input: string[] | StreamFilter) {\n const setClause = `SET at = -1, retry = 0, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n for (const stream of input) {\n const r = await tx.execute({\n sql: `UPDATE streams ${setClause} WHERE stream = ?`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n const { clause, args } = this._filterClause(input);\n const r = await tx.execute({\n sql: `UPDATE streams ${setClause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- unblock: clear blocked + retry + lease without touching watermark ---\n // `retry = -1` so claim's post-bump returns retry=0 (first attempt),\n // matching the InMemoryStore convention.\n async unblock(input: string[] | StreamFilter) {\n const setClause = `SET retry = -1, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n for (const stream of input) {\n const r = await tx.execute({\n sql: `UPDATE streams ${setClause}\n WHERE stream = ? AND blocked = 1`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n // Filter form: force blocked = true regardless of what the\n // caller passed.\n const { clause, args } = this._filterClause({\n ...input,\n blocked: true,\n });\n const r = await tx.execute({\n sql: `UPDATE streams ${setClause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- query_streams: read-only introspection with filters ---\n async query_streams(\n callback: (position: StreamPosition) => void,\n query?: QueryStreams\n ): Promise<QueryStreamsResult> {\n const limit = query?.limit ?? 100;\n let sql =\n \"SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane FROM streams WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream !== undefined) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream LIKE ?\";\n args.push(streamPatternToLike(query.stream));\n }\n }\n if (query?.source !== undefined) {\n sql += \" AND source IS NOT NULL\";\n if (query.source_exact) {\n sql += \" AND source = ?\";\n args.push(query.source);\n } else {\n sql += \" AND source LIKE ?\";\n args.push(streamPatternToLike(query.source));\n }\n }\n if (query?.blocked !== undefined) {\n sql += \" AND blocked = ?\";\n args.push(query.blocked ? 1 : 0);\n }\n if (query?.lane !== undefined) {\n sql += \" AND lane = ?\";\n args.push(query.lane);\n }\n if (query?.after !== undefined) {\n sql += \" AND stream > ?\";\n args.push(query.after);\n }\n sql += \" ORDER BY stream LIMIT ?\";\n args.push(limit);\n\n const [streamsResult, maxResult] = await Promise.all([\n this.client.execute({ sql, args: args as any[] }),\n this.client.execute(\"SELECT COALESCE(MAX(id), -1) AS m FROM events\"),\n ]);\n\n let count = 0;\n for (const row of streamsResult.rows) {\n const leased_until = row.leased_until as string | null;\n callback({\n stream: row.stream as string,\n source: (row.source as string | null) ?? undefined,\n at: Number(row.at),\n retry: Number(row.retry),\n blocked: Number(row.blocked) === 1,\n error: row.error as string,\n priority: Number(row.priority),\n leased_by: (row.leased_by as string | null) ?? undefined,\n leased_until: leased_until ? new Date(leased_until) : undefined,\n lane: row.lane as string,\n });\n count++;\n }\n\n return { maxEventId: Number(maxResult.rows[0].m), count };\n }\n\n /**\n * Per-stream aggregated stats — see {@link Store.query_stats}.\n *\n * Two code paths (mirrors the PostgresStore strategy):\n *\n * - **Heads-only path** (no `count`, no `names`): one or two queries\n * using `ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version\n * DESC|ASC)` (SQLite lacks PG's `DISTINCT ON`). Window function +\n * `WHERE rn = 1` materializes the head (or tail) per stream from\n * the `(stream, version)` unique index. Parallel `Promise.all` when\n * tail is requested.\n *\n * - **Full-scan path** (`count` or `names` set): one CTE materializes\n * the filtered events, then `GROUP BY stream, name` →\n * `json_group_object(name, n)` for the names map plus `SUM(n)` for\n * count. Heads (and tails when requested) come from the same scan.\n *\n * SQLite specifics:\n * - `data` and `meta` are stored as TEXT (JSON-encoded); the reader\n * JSON-parses them when materializing the {@link Committed} rows.\n * - `blocked` is stored as 0/1 integer; the filter converts.\n * - Array input expands to a placeholder list (`IN (?, ?, ...)`)\n * since SQLite has no native array type.\n */\n async query_stats<E extends Schemas>(\n input: string[] | Pick<StreamFilter, \"stream\" | \"stream_exact\">,\n options?: QueryStatsOptions<E>\n ): Promise<Map<string, StreamStats<E>>> {\n const exclude = options?.exclude ?? [];\n const wantTail = options?.tail ?? false;\n const wantCount = options?.count ?? false;\n const wantNames = options?.names ?? false;\n const before = options?.before;\n const fullScan = wantCount || wantNames;\n\n if (Array.isArray(input) && input.length === 0) {\n return new Map<string, StreamStats<E>>();\n }\n\n // Build WHERE clause + positional args. Subscription-level filters\n // (source, blocked) are intentionally not accepted — events live in\n // the events table; subscription state in the streams table. For\n // \"stats for blocked subscriptions\" callers compose with\n // query_streams. So no JOIN here.\n const where: string[] = [];\n const args: unknown[] = [];\n\n if (Array.isArray(input)) {\n const placeholders = input.map(() => \"?\").join(\",\");\n where.push(`e.stream IN (${placeholders})`);\n args.push(...input);\n } else if (input.stream !== undefined) {\n if (input.stream_exact) {\n where.push(`e.stream = ?`);\n args.push(input.stream);\n } else {\n where.push(`e.stream LIKE ?`);\n args.push(streamPatternToLike(input.stream));\n }\n }\n if (exclude.length) {\n const placeholders = exclude.map(() => \"?\").join(\",\");\n where.push(`e.name NOT IN (${placeholders})`);\n args.push(...exclude);\n }\n if (before !== undefined) {\n where.push(`e.id < ?`);\n args.push(before);\n }\n\n const fromClause = `events e`;\n // Always emit a WHERE clause — `WHERE 1=1` short-circuits the\n // empty-filter case without a conditional branch on the generation\n // side. SQLite optimizes the trivial predicate out.\n const whereClause = `WHERE ${where.length ? where.join(\" AND \") : \"1=1\"}`;\n\n return fullScan\n ? this._queryStatsFullScan<E>(\n fromClause,\n whereClause,\n args,\n wantTail,\n wantCount,\n wantNames\n )\n : this._queryStatsHeadsOnly<E>(fromClause, whereClause, args, wantTail);\n }\n\n /**\n * Cheap path — head (and optional tail) via ROW_NUMBER() over the\n * `(stream, version)` unique index. Parallel queries when tail set.\n */\n private async _queryStatsHeadsOnly<E extends Schemas>(\n fromClause: string,\n whereClause: string,\n args: unknown[],\n wantTail: boolean\n ): Promise<Map<string, StreamStats<E>>> {\n const cols = `e.id, e.stream, e.version, e.name, e.data, e.created, e.meta`;\n const headSql = `SELECT * FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version DESC) AS rn\n FROM ${fromClause}\n ${whereClause}\n ) WHERE rn = 1`;\n const tailSql = wantTail\n ? `SELECT * FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version ASC) AS rn\n FROM ${fromClause}\n ${whereClause}\n ) WHERE rn = 1`\n : null;\n\n const [headRes, tailRes] = await Promise.all([\n this.client.execute({ sql: headSql, args: args as any[] }),\n tailSql\n ? this.client.execute({ sql: tailSql, args: args as any[] })\n : null,\n ]);\n\n const toCommitted = (row: Record<string, unknown>): Committed<E, keyof E> =>\n ({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n name: row.name as string,\n data: JSON.parse(row.data as string),\n meta: JSON.parse(row.meta as string),\n created: new Date(row.created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of headRes.rows) {\n out.set(row.stream as string, {\n head: toCommitted(row as Record<string, unknown>),\n });\n }\n if (tailRes) {\n for (const row of tailRes.rows) {\n // Head and tail share the same WHERE, so any stream returning\n // a tail must also have returned a head — no null check needed.\n (\n out.get(row.stream as string) as {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n }\n ).tail = toCommitted(row as Record<string, unknown>);\n }\n }\n return out;\n }\n\n /**\n * Full-scan path — one CTE-based query with per-stream `COUNT(*)` and\n * `json_group_object(name, n)`. Heads (and optional tails) ride free\n * on the same scan.\n */\n private async _queryStatsFullScan<E extends Schemas>(\n fromClause: string,\n whereClause: string,\n args: unknown[],\n wantTail: boolean,\n wantCount: boolean,\n wantNames: boolean\n ): Promise<Map<string, StreamStats<E>>> {\n const tailCte = wantTail\n ? `, tails AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version ASC) AS rn FROM ef\n ) WHERE rn = 1\n )`\n : \"\";\n const tailJoin = wantTail ? `LEFT JOIN tails t ON t.stream = h.stream` : \"\";\n const tailCols = wantTail\n ? `, t.id AS t_id, t.stream AS t_stream, t.version AS t_version,\n t.name AS t_name, t.data AS t_data, t.created AS t_created, t.meta AS t_meta`\n : \"\";\n\n const sql = `\n WITH ef AS (\n SELECT e.id, e.stream, e.version, e.name, e.data, e.created, e.meta\n FROM ${fromClause}\n ${whereClause}\n ),\n agg AS (\n SELECT stream,\n SUM(n) AS cnt,\n json_group_object(name, n) AS names\n FROM (\n SELECT stream, name, COUNT(*) AS n\n FROM ef\n GROUP BY stream, name\n )\n GROUP BY stream\n ),\n heads AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version DESC) AS rn FROM ef\n ) WHERE rn = 1\n )\n ${tailCte}\n SELECT\n h.id, h.stream, h.version, h.name, h.data, h.created, h.meta,\n a.cnt AS agg_count,\n a.names AS agg_names\n ${tailCols}\n FROM heads h\n LEFT JOIN agg a ON a.stream = h.stream\n ${tailJoin}\n `;\n\n const res = await this.client.execute({ sql, args: args as any[] });\n\n const toCommitted = (\n id: unknown,\n stream: unknown,\n version: unknown,\n name: unknown,\n data: unknown,\n meta: unknown,\n created: unknown\n ): Committed<E, keyof E> =>\n ({\n id: Number(id),\n stream: stream as string,\n version: Number(version),\n name: name as string,\n data: JSON.parse(data as string),\n meta: JSON.parse(meta as string),\n created: new Date(created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of res.rows) {\n const r = row as unknown as Record<string, unknown>;\n const stats: {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n count?: number;\n names?: Record<string, number>;\n } = {\n head: toCommitted(\n r.id,\n r.stream,\n r.version,\n r.name,\n r.data,\n r.meta,\n r.created\n ),\n };\n if (wantTail && r.t_id !== null && r.t_id !== undefined) {\n stats.tail = toCommitted(\n r.t_id,\n r.t_stream,\n r.t_version,\n r.t_name,\n r.t_data,\n r.t_meta,\n r.t_created\n );\n }\n if (wantCount) stats.count = Number(r.agg_count);\n // `agg_names` is non-null when this row exists: heads and agg are\n // both built from the same `ef` CTE, so any stream in heads has\n // at least one matching event and `json_group_object` returns a\n // JSON string (never null) for that group.\n if (wantNames) stats.names = JSON.parse(r.agg_names as string);\n out.set(r.stream as string, stats as StreamStats<E>);\n }\n return out;\n }\n\n // --- prioritize: bulk priority update with filter (ACT-102) ---\n async prioritize(filter: StreamFilter, priority: number): Promise<number> {\n const { clause, args: filterArgs } = this._filterClause(filter);\n // libSQL `?` placeholders are positional and NOT reusable, so we\n // bind `priority` twice: once for SET, once for the no-op skip\n // in WHERE.\n const sql = `UPDATE streams SET priority = ?\n WHERE priority <> ? AND ${clause}`;\n const result = await this.client.execute({\n sql,\n args: [priority, priority, ...filterArgs] as any[],\n });\n return result.rowsAffected;\n }\n\n // --- truncate: transactional delete + seed ---\n async truncate(\n targets: Array<{\n stream: string;\n snapshot?: Record<string, unknown>;\n meta?: EventMeta;\n }>\n ) {\n const result = new Map<\n string,\n { deleted: number; committed: Committed<Schemas, keyof Schemas> }\n >();\n\n const tx = await this.client.transaction(\"write\");\n try {\n for (const { stream, snapshot, meta } of targets) {\n const countRow = await tx.execute({\n sql: \"SELECT COUNT(*) as c FROM events WHERE stream = ?\",\n args: [stream],\n });\n const deleted = Number(countRow.rows[0].c);\n await tx.execute({\n sql: \"DELETE FROM events WHERE stream = ?\",\n args: [stream],\n });\n await tx.execute({\n sql: \"DELETE FROM streams WHERE stream = ?\",\n args: [stream],\n });\n\n const eventName =\n snapshot !== undefined ? \"__snapshot__\" : \"__tombstone__\";\n const eventMeta = meta ?? { correlation: \"\", causation: {} };\n const now = new Date().toISOString();\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, 0, ?, ?, ?, ?)\",\n args: [\n stream,\n eventName,\n JSON.stringify(snapshot ?? {}),\n JSON.stringify(eventMeta),\n now,\n ],\n });\n\n result.set(stream, {\n deleted,\n committed: {\n id: Number(ins.lastInsertRowid),\n stream,\n version: 0,\n created: new Date(now),\n name: eventName,\n data: snapshot ?? {},\n meta: eventMeta,\n },\n });\n }\n await tx.commit();\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n\n return result;\n }\n\n /**\n * Atomically wipe-and-rebuild the store inside a single libsql\n * `write` transaction.\n *\n * On any throw inside the driver the transaction rolls back and the\n * store is byte-for-byte unchanged. `DELETE FROM events` + `DELETE\n * FROM streams` wipe both tables; `DELETE FROM sqlite_sequence\n * WHERE name = 'events'` resets the autoincrement counter so the\n * new sequence is dense from 1. `created` is preserved verbatim\n * from the source.\n */\n async restore(\n driver: (\n callback: (event: Committed<Schemas, keyof Schemas>) => Promise<number>\n ) => Promise<void>\n ): Promise<void> {\n const tx = await this.client.transaction(\"write\");\n try {\n await tx.execute(\"DELETE FROM events\");\n await tx.execute(\"DELETE FROM streams\");\n // Reset the autoincrement counter so the new sequence is dense\n // from 1. `DELETE FROM sqlite_sequence WHERE name = '?'` is the\n // canonical SQLite reset; safe even if the row doesn't exist.\n await tx.execute(\"DELETE FROM sqlite_sequence WHERE name = 'events'\");\n await driver(async (event) => {\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, ?, ?, ?, ?, ?)\",\n args: [\n event.stream,\n event.version,\n event.name,\n JSON.stringify(event.data),\n JSON.stringify(event.meta),\n event.created.toISOString(),\n ],\n });\n return Number(ins.lastInsertRowid);\n });\n await tx.commit();\n } catch (error) {\n await tx.rollback();\n throw error;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/sqlite-store.ts"],"sourcesContent":["import { type Client, createClient } from \"@libsql/client\";\nimport type {\n BlockedLease,\n Committed,\n EventMeta,\n Lease,\n Message,\n Query,\n QueryStatsOptions,\n QueryStreams,\n QueryStreamsResult,\n Schemas,\n Store,\n StreamFilter,\n StreamPosition,\n StreamStats,\n} from \"@rotorsoft/act\";\n\n/**\n * SQLite store configuration\n */\nexport interface SqliteConfig {\n /** Path to the SQLite database file (default: \":memory:\") */\n url: string;\n /** Auth token for libSQL server connections (optional) */\n authToken?: string;\n}\n\nconst DEFAULT_CONFIG: SqliteConfig = {\n url: \"file::memory:\",\n};\n\n/** Translate a stream filter (regex-shaped or plain substring) into a\n * SQL LIKE pattern. Honors `^` / `$` anchors and converts `.*` → `%`,\n * `.` → `_`. Unanchored input gets `%` wildcards on both sides.\n *\n * Examples:\n * - `^abc$` → `abc` (exact)\n * - `^abc.*` → `abc%` (starts-with)\n * - `.*abc$` → `%abc` (ends-with)\n * - `abc` → `%abc%` (contains)\n * - `a.c` → `%a_c%` (single-char wildcard, contains)\n *\n * @internal exported for testing\n */\nexport function streamPatternToLike(input: string): string {\n let s = input;\n const start = s.startsWith(\"^\");\n const end = s.endsWith(\"$\");\n if (start) s = s.slice(1);\n if (end) s = s.slice(0, -1);\n s = s.replace(/\\.\\*/g, \"%\").replace(/\\./g, \"_\");\n const out = (start ? \"\" : \"%\") + s + (end ? \"\" : \"%\");\n // Collapse adjacent `%` — e.g. `^a.*` would otherwise yield `a%%`.\n // Same matching semantics, cleaner output.\n return out.replace(/%+/g, \"%\");\n}\n\n/**\n * SQLite event store adapter for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act).\n *\n * Provides persistent event storage using SQLite via `@libsql/client`.\n * All write operations use transactions for ACID guarantees.\n * Since SQLite serializes writes at the database level, the concurrency\n * model is equivalent to PostgreSQL's `FOR UPDATE SKIP LOCKED` for\n * single-server deployments.\n *\n * **`Store.notify` is intentionally not implemented.** The notify hook is\n * a cross-process wake-up signal that lets a horizontally-scaled Act\n * deployment wake `settle()` immediately on remote commits. SQLite is\n * single-node by design — there is no remote writer to be notified of —\n * so the {@link Act} orchestrator falls back to the existing\n * debounce/poll path, which is correct for this topology.\n *\n * @example\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * import { SqliteStore } from \"@rotorsoft/act-sqlite\";\n *\n * store(new SqliteStore({ url: \"file:myapp.db\" }));\n * await store().seed();\n * ```\n */\nexport class SqliteStore implements Store {\n private client: Client;\n\n constructor(config: Partial<SqliteConfig> = {}) {\n const cfg = { ...DEFAULT_CONFIG, ...config };\n this.client = createClient({\n url: cfg.url,\n authToken: cfg.authToken,\n });\n }\n\n async seed() {\n await this.client.execute(\"PRAGMA journal_mode=WAL\");\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream TEXT NOT NULL,\n version INTEGER NOT NULL,\n name TEXT NOT NULL,\n data TEXT NOT NULL,\n meta TEXT NOT NULL,\n created TEXT NOT NULL,\n UNIQUE(stream, version)\n )\n `);\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_stream ON events(stream)\"\n );\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_name ON events(name)\"\n );\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS streams (\n stream TEXT PRIMARY KEY,\n source TEXT,\n at INTEGER NOT NULL DEFAULT -1,\n retry INTEGER NOT NULL DEFAULT 0,\n blocked INTEGER NOT NULL DEFAULT 0,\n error TEXT NOT NULL DEFAULT '',\n leased_by TEXT,\n leased_until TEXT,\n priority INTEGER NOT NULL DEFAULT 0,\n lane TEXT NOT NULL DEFAULT 'default'\n )\n `);\n // Migration for tables created before priority lanes (ACT-102).\n // libSQL surfaces \"duplicate column\" as an error, hence the\n // try/swallow — this mirrors PG's `ADD COLUMN IF NOT EXISTS`.\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN priority INTEGER NOT NULL DEFAULT 0\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before drain lanes (ACT-1103).\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN lane TEXT NOT NULL DEFAULT 'default'\"\n );\n } catch {\n // already present\n }\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_claim ON streams(blocked, priority DESC, at)\"\n );\n // Lane filter index (ACT-1103).\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_lane ON streams(lane)\"\n );\n }\n\n async drop() {\n await this.client.execute(\"DROP TABLE IF EXISTS events\");\n await this.client.execute(\"DROP TABLE IF EXISTS streams\");\n }\n\n async dispose() {\n await Promise.resolve();\n this.client.close();\n }\n\n // --- commit: transaction + optimistic concurrency ---\n async commit<E extends Schemas>(\n stream: string,\n msgs: Message<E, keyof E>[],\n meta: EventMeta,\n expectedVersion?: number\n ): Promise<Committed<E, keyof E>[]> {\n const tx = await this.client.transaction(\"write\");\n try {\n const version_row = await tx.execute({\n sql: \"SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?\",\n args: [stream],\n });\n const current_version = Number(version_row.rows[0].v);\n\n if (\n typeof expectedVersion === \"number\" &&\n current_version !== expectedVersion\n ) {\n const { ConcurrencyError } = await import(\"@rotorsoft/act\");\n throw new ConcurrencyError(\n stream,\n current_version,\n msgs as Message<Schemas, keyof Schemas>[],\n expectedVersion\n );\n }\n\n const now = new Date().toISOString();\n const committed: Committed<E, keyof E>[] = [];\n let version = current_version + 1;\n\n for (const { name, data } of msgs) {\n const result = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, ?, ?, ?, ?, ?)\",\n args: [\n stream,\n version,\n name as string,\n JSON.stringify(data),\n JSON.stringify(meta),\n now,\n ],\n });\n committed.push({\n id: Number(result.lastInsertRowid),\n stream,\n version,\n created: new Date(now),\n name,\n data,\n meta,\n });\n version++;\n }\n\n await tx.commit();\n return committed;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- query: read-only, no transaction needed ---\n async query<E extends Schemas>(\n callback: (event: Committed<E, keyof E>) => void,\n query?: Query\n ): Promise<number> {\n let sql = \"SELECT * FROM events WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream LIKE ?\";\n args.push(streamPatternToLike(query.stream));\n }\n }\n if (query?.names) {\n sql += ` AND name IN (${query.names.map(() => \"?\").join(\",\")})`;\n args.push(...query.names);\n }\n if ((query as any)?.correlation) {\n sql += \" AND json_extract(meta, '$.correlation') = ?\";\n args.push((query as any).correlation);\n }\n if (query?.after !== undefined) {\n sql += \" AND id > ?\";\n args.push(query.after);\n }\n if (query?.before !== undefined) {\n sql += \" AND id < ?\";\n args.push(query.before);\n }\n if (query?.created_after) {\n sql += \" AND created > ?\";\n args.push(query.created_after.toISOString());\n }\n if (query?.created_before) {\n sql += \" AND created < ?\";\n args.push(query.created_before.toISOString());\n }\n if (!query?.with_snaps) {\n sql += \" AND name != '__snapshot__'\";\n }\n\n sql += query?.backward ? \" ORDER BY id DESC\" : \" ORDER BY id ASC\";\n\n if (query?.limit) {\n sql += \" LIMIT ?\";\n args.push(query.limit);\n }\n\n const result = await this.client.execute({ sql, args: args as any[] });\n let count = 0;\n\n for (const row of result.rows) {\n await Promise.resolve(\n callback({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n created: new Date(row.created as string),\n name: row.name as string,\n data: JSON.parse(row.data as string),\n meta: JSON.parse(row.meta as string),\n })\n );\n count++;\n }\n\n return count;\n }\n\n // --- subscribe: idempotent INSERT OR IGNORE (= PG ON CONFLICT DO NOTHING)\n // plus a UPDATE pass to keep the *max* priority across reactions\n // targeting the same stream (ACT-102). Operator overrides go\n // through `prioritize()` instead.\n async subscribe(\n streams: Array<{\n stream: string;\n source?: string;\n priority?: number;\n lane?: string;\n }>\n ) {\n const tx = await this.client.transaction(\"write\");\n try {\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = \"default\",\n } of streams) {\n const inserted = await tx.execute({\n sql: \"INSERT OR IGNORE INTO streams (stream, source, priority, lane) VALUES (?, ?, ?, ?)\",\n args: [stream, source ?? null, priority, lane],\n });\n if (inserted.rowsAffected > 0) {\n subscribed++;\n } else {\n if (priority > 0) {\n await tx.execute({\n sql: \"UPDATE streams SET priority = ? WHERE stream = ? AND priority < ?\",\n args: [priority, stream, priority],\n });\n }\n // ACT-1103: current subscribe wins on lane.\n await tx.execute({\n sql: \"UPDATE streams SET lane = ? WHERE stream = ? AND lane <> ?\",\n args: [lane, stream, lane],\n });\n }\n }\n const wm = await tx.execute(\n \"SELECT COALESCE(MAX(at), -1) as w FROM streams\"\n );\n await tx.commit();\n return { subscribed, watermark: Number(wm.rows[0].w) };\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- claim: write transaction (SQLite serializes writes = equivalent\n // to PG FOR UPDATE SKIP LOCKED for single-server) ---\n async claim(\n lagging: number,\n leading: number,\n by: string,\n millis: number,\n lane?: string\n ) {\n const tx = await this.client.transaction(\"write\");\n try {\n const now = new Date().toISOString();\n\n const lane_clause = lane !== undefined ? \" AND lane = ?\" : \"\";\n const result = await tx.execute({\n sql: `SELECT stream, source, at, priority, lane FROM streams\n WHERE blocked = 0 AND (leased_until IS NULL OR leased_until <= ?)${lane_clause}\n ORDER BY priority DESC, at ASC`,\n args: lane !== undefined ? [now, lane] : [now],\n });\n\n const candidates: {\n stream: string;\n source: string | undefined;\n at: number;\n priority: number;\n lane: string;\n }[] = [];\n for (const row of result.rows) {\n const stream = row.stream as string;\n const source = row.source as string | null;\n const at = Number(row.at);\n\n let has_events: boolean;\n if (source) {\n const check = await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' AND stream LIKE ? LIMIT 1`,\n args: [at, streamPatternToLike(source)],\n });\n has_events = check.rows.length > 0;\n } else {\n const check = await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' LIMIT 1`,\n args: [at],\n });\n has_events = check.rows.length > 0;\n }\n\n if (has_events) {\n candidates.push({\n stream,\n source: source ?? undefined,\n at,\n priority: Number(row.priority),\n lane: row.lane as string,\n });\n }\n }\n\n // Dual frontier: lagging (priority DESC, watermark ASC — ACT-102)\n // + leading (newest first). The candidates list arrives sorted\n // by `priority DESC, at ASC` from the SELECT above, so the\n // `slice(0, lagging)` already does the right thing.\n const lag = candidates.slice(0, lagging);\n const lead = [...candidates]\n .sort((a, b) => b.at - a.at)\n .slice(0, leading);\n const seen = new Set<string>();\n const combined = [...lag, ...lead].filter((p) => {\n if (seen.has(p.stream)) return false;\n seen.add(p.stream);\n return true;\n });\n\n const leases: Lease[] = [];\n const until = new Date(Date.now() + millis).toISOString();\n for (const row of combined) {\n await tx.execute({\n sql: \"UPDATE streams SET leased_by = ?, leased_until = ?, retry = retry + 1 WHERE stream = ?\",\n args: [by, until, row.stream],\n });\n leases.push({\n stream: row.stream,\n source: row.source,\n at: row.at,\n by,\n retry: 0,\n lagging: row.at < 0,\n lane: row.lane,\n });\n }\n\n await tx.commit();\n return leases;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- ack: transaction + ownership check (= PG WHERE leased_by) ---\n async ack(leases: Lease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n const result: Lease[] = [];\n for (const l of leases) {\n const r = await tx.execute({\n sql: `UPDATE streams SET at = ?, leased_by = NULL, leased_until = NULL, retry = -1\n WHERE stream = ? AND leased_by = ?`,\n args: [l.at, l.stream, l.by],\n });\n if (r.rowsAffected > 0) result.push(l);\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- block: transaction + ownership + idempotent (= PG) ---\n async block(leases: BlockedLease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n const result: BlockedLease[] = [];\n for (const l of leases) {\n const r = await tx.execute({\n sql: `UPDATE streams SET blocked = 1, error = ?\n WHERE stream = ? AND leased_by = ? AND blocked = 0`,\n args: [l.error, l.stream, l.by],\n });\n if (r.rowsAffected > 0) result.push(l);\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n /**\n * Translate a {@link StreamFilter} to a SQLite `WHERE` clause fragment\n * plus positional args. Returns `\"1\"` (always true) when empty so\n * callers can compose it unconditionally.\n */\n private _filter_clause(filter: StreamFilter): {\n clause: string;\n args: unknown[];\n } {\n const conditions: string[] = [];\n const args: unknown[] = [];\n if (filter.stream !== undefined) {\n if (filter.stream_exact) {\n conditions.push(\"stream = ?\");\n args.push(filter.stream);\n } else {\n conditions.push(\"stream LIKE ?\");\n args.push(streamPatternToLike(filter.stream));\n }\n }\n if (filter.source !== undefined) {\n conditions.push(\"source IS NOT NULL\");\n if (filter.source_exact) {\n conditions.push(\"source = ?\");\n args.push(filter.source);\n } else {\n conditions.push(\"source LIKE ?\");\n args.push(streamPatternToLike(filter.source));\n }\n }\n if (filter.blocked !== undefined) {\n conditions.push(\"blocked = ?\");\n args.push(filter.blocked ? 1 : 0);\n }\n if (filter.lane !== undefined) {\n conditions.push(\"lane = ?\");\n args.push(filter.lane);\n }\n return { clause: conditions.length ? conditions.join(\" AND \") : \"1\", args };\n }\n\n // --- reset: transactional, accepts names or filter ---\n async reset(input: string[] | StreamFilter) {\n const set_clause = `SET at = -1, retry = 0, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n for (const stream of input) {\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE stream = ?`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n const { clause, args } = this._filter_clause(input);\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- unblock: clear blocked + retry + lease without touching watermark ---\n // `retry = -1` so claim's post-bump returns retry=0 (first attempt),\n // matching the InMemoryStore convention.\n async unblock(input: string[] | StreamFilter) {\n const set_clause = `SET retry = -1, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n for (const stream of input) {\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause}\n WHERE stream = ? AND blocked = 1`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n // Filter form: force blocked = true regardless of what the\n // caller passed.\n const { clause, args } = this._filter_clause({\n ...input,\n blocked: true,\n });\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- query_streams: read-only introspection with filters ---\n async query_streams(\n callback: (position: StreamPosition) => void,\n query?: QueryStreams\n ): Promise<QueryStreamsResult> {\n const limit = query?.limit ?? 100;\n let sql =\n \"SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane FROM streams WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream !== undefined) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream LIKE ?\";\n args.push(streamPatternToLike(query.stream));\n }\n }\n if (query?.source !== undefined) {\n sql += \" AND source IS NOT NULL\";\n if (query.source_exact) {\n sql += \" AND source = ?\";\n args.push(query.source);\n } else {\n sql += \" AND source LIKE ?\";\n args.push(streamPatternToLike(query.source));\n }\n }\n if (query?.blocked !== undefined) {\n sql += \" AND blocked = ?\";\n args.push(query.blocked ? 1 : 0);\n }\n if (query?.lane !== undefined) {\n sql += \" AND lane = ?\";\n args.push(query.lane);\n }\n if (query?.after !== undefined) {\n sql += \" AND stream > ?\";\n args.push(query.after);\n }\n sql += \" ORDER BY stream LIMIT ?\";\n args.push(limit);\n\n const [streamsResult, maxResult] = await Promise.all([\n this.client.execute({ sql, args: args as any[] }),\n this.client.execute(\"SELECT COALESCE(MAX(id), -1) AS m FROM events\"),\n ]);\n\n let count = 0;\n for (const row of streamsResult.rows) {\n const leased_until = row.leased_until as string | null;\n callback({\n stream: row.stream as string,\n source: (row.source as string | null) ?? undefined,\n at: Number(row.at),\n retry: Number(row.retry),\n blocked: Number(row.blocked) === 1,\n error: row.error as string,\n priority: Number(row.priority),\n leased_by: (row.leased_by as string | null) ?? undefined,\n leased_until: leased_until ? new Date(leased_until) : undefined,\n lane: row.lane as string,\n });\n count++;\n }\n\n return { maxEventId: Number(maxResult.rows[0].m), count };\n }\n\n /**\n * Per-stream aggregated stats — see {@link Store.query_stats}.\n *\n * Two code paths (mirrors the PostgresStore strategy):\n *\n * - **Heads-only path** (no `count`, no `names`): one or two queries\n * using `ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version\n * DESC|ASC)` (SQLite lacks PG's `DISTINCT ON`). Window function +\n * `WHERE rn = 1` materializes the head (or tail) per stream from\n * the `(stream, version)` unique index. Parallel `Promise.all` when\n * tail is requested.\n *\n * - **Full-scan path** (`count` or `names` set): one CTE materializes\n * the filtered events, then `GROUP BY stream, name` →\n * `json_group_object(name, n)` for the names map plus `SUM(n)` for\n * count. Heads (and tails when requested) come from the same scan.\n *\n * SQLite specifics:\n * - `data` and `meta` are stored as TEXT (JSON-encoded); the reader\n * JSON-parses them when materializing the {@link Committed} rows.\n * - `blocked` is stored as 0/1 integer; the filter converts.\n * - Array input expands to a placeholder list (`IN (?, ?, ...)`)\n * since SQLite has no native array type.\n */\n async query_stats<E extends Schemas>(\n input: string[] | Pick<StreamFilter, \"stream\" | \"stream_exact\">,\n options?: QueryStatsOptions<E>\n ): Promise<Map<string, StreamStats<E>>> {\n const exclude = options?.exclude ?? [];\n const want_tail = options?.tail ?? false;\n const want_count = options?.count ?? false;\n const want_names = options?.names ?? false;\n const before = options?.before;\n const full_scan = want_count || want_names;\n\n if (Array.isArray(input) && input.length === 0) {\n return new Map<string, StreamStats<E>>();\n }\n\n // Build WHERE clause + positional args. Subscription-level filters\n // (source, blocked) are intentionally not accepted — events live in\n // the events table; subscription state in the streams table. For\n // \"stats for blocked subscriptions\" callers compose with\n // query_streams. So no JOIN here.\n const where: string[] = [];\n const args: unknown[] = [];\n\n if (Array.isArray(input)) {\n const placeholders = input.map(() => \"?\").join(\",\");\n where.push(`e.stream IN (${placeholders})`);\n args.push(...input);\n } else if (input.stream !== undefined) {\n if (input.stream_exact) {\n where.push(`e.stream = ?`);\n args.push(input.stream);\n } else {\n where.push(`e.stream LIKE ?`);\n args.push(streamPatternToLike(input.stream));\n }\n }\n if (exclude.length) {\n const placeholders = exclude.map(() => \"?\").join(\",\");\n where.push(`e.name NOT IN (${placeholders})`);\n args.push(...exclude);\n }\n if (before !== undefined) {\n where.push(`e.id < ?`);\n args.push(before);\n }\n\n const from_clause = `events e`;\n // Always emit a WHERE clause — `WHERE 1=1` short-circuits the\n // empty-filter case without a conditional branch on the generation\n // side. SQLite optimizes the trivial predicate out.\n const where_clause = `WHERE ${where.length ? where.join(\" AND \") : \"1=1\"}`;\n\n return full_scan\n ? this._query_stats_full_scan<E>(\n from_clause,\n where_clause,\n args,\n want_tail,\n want_count,\n want_names\n )\n : this._query_stats_heads_only<E>(\n from_clause,\n where_clause,\n args,\n want_tail\n );\n }\n\n /**\n * Cheap path — head (and optional tail) via ROW_NUMBER() over the\n * `(stream, version)` unique index. Parallel queries when tail set.\n */\n private async _query_stats_heads_only<E extends Schemas>(\n from_clause: string,\n where_clause: string,\n args: unknown[],\n want_tail: boolean\n ): Promise<Map<string, StreamStats<E>>> {\n const cols = `e.id, e.stream, e.version, e.name, e.data, e.created, e.meta`;\n const head_sql = `SELECT * FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version DESC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) WHERE rn = 1`;\n const tail_sql = want_tail\n ? `SELECT * FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version ASC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) WHERE rn = 1`\n : null;\n\n const [headRes, tailRes] = await Promise.all([\n this.client.execute({ sql: head_sql, args: args as any[] }),\n tail_sql\n ? this.client.execute({ sql: tail_sql, args: args as any[] })\n : null,\n ]);\n\n const to_committed = (\n row: Record<string, unknown>\n ): Committed<E, keyof E> =>\n ({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n name: row.name as string,\n data: JSON.parse(row.data as string),\n meta: JSON.parse(row.meta as string),\n created: new Date(row.created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of headRes.rows) {\n out.set(row.stream as string, {\n head: to_committed(row as Record<string, unknown>),\n });\n }\n if (tailRes) {\n for (const row of tailRes.rows) {\n // Head and tail share the same WHERE, so any stream returning\n // a tail must also have returned a head — no null check needed.\n (\n out.get(row.stream as string) as {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n }\n ).tail = to_committed(row as Record<string, unknown>);\n }\n }\n return out;\n }\n\n /**\n * Full-scan path — one CTE-based query with per-stream `COUNT(*)` and\n * `json_group_object(name, n)`. Heads (and optional tails) ride free\n * on the same scan.\n */\n private async _query_stats_full_scan<E extends Schemas>(\n from_clause: string,\n where_clause: string,\n args: unknown[],\n want_tail: boolean,\n want_count: boolean,\n want_names: boolean\n ): Promise<Map<string, StreamStats<E>>> {\n const tail_cte = want_tail\n ? `, tails AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version ASC) AS rn FROM ef\n ) WHERE rn = 1\n )`\n : \"\";\n const tail_join = want_tail\n ? `LEFT JOIN tails t ON t.stream = h.stream`\n : \"\";\n const tail_cols = want_tail\n ? `, t.id AS t_id, t.stream AS t_stream, t.version AS t_version,\n t.name AS t_name, t.data AS t_data, t.created AS t_created, t.meta AS t_meta`\n : \"\";\n\n const sql = `\n WITH ef AS (\n SELECT e.id, e.stream, e.version, e.name, e.data, e.created, e.meta\n FROM ${from_clause}\n ${where_clause}\n ),\n agg AS (\n SELECT stream,\n SUM(n) AS cnt,\n json_group_object(name, n) AS names\n FROM (\n SELECT stream, name, COUNT(*) AS n\n FROM ef\n GROUP BY stream, name\n )\n GROUP BY stream\n ),\n heads AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version DESC) AS rn FROM ef\n ) WHERE rn = 1\n )\n ${tail_cte}\n SELECT\n h.id, h.stream, h.version, h.name, h.data, h.created, h.meta,\n a.cnt AS agg_count,\n a.names AS agg_names\n ${tail_cols}\n FROM heads h\n LEFT JOIN agg a ON a.stream = h.stream\n ${tail_join}\n `;\n\n const res = await this.client.execute({ sql, args: args as any[] });\n\n const to_committed = (\n id: unknown,\n stream: unknown,\n version: unknown,\n name: unknown,\n data: unknown,\n meta: unknown,\n created: unknown\n ): Committed<E, keyof E> =>\n ({\n id: Number(id),\n stream: stream as string,\n version: Number(version),\n name: name as string,\n data: JSON.parse(data as string),\n meta: JSON.parse(meta as string),\n created: new Date(created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of res.rows) {\n const r = row as unknown as Record<string, unknown>;\n const stats: {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n count?: number;\n names?: Record<string, number>;\n } = {\n head: to_committed(\n r.id,\n r.stream,\n r.version,\n r.name,\n r.data,\n r.meta,\n r.created\n ),\n };\n if (want_tail && r.t_id !== null && r.t_id !== undefined) {\n stats.tail = to_committed(\n r.t_id,\n r.t_stream,\n r.t_version,\n r.t_name,\n r.t_data,\n r.t_meta,\n r.t_created\n );\n }\n if (want_count) stats.count = Number(r.agg_count);\n // `agg_names` is non-null when this row exists: heads and agg are\n // both built from the same `ef` CTE, so any stream in heads has\n // at least one matching event and `json_group_object` returns a\n // JSON string (never null) for that group.\n if (want_names) stats.names = JSON.parse(r.agg_names as string);\n out.set(r.stream as string, stats as StreamStats<E>);\n }\n return out;\n }\n\n // --- prioritize: bulk priority update with filter (ACT-102) ---\n async prioritize(filter: StreamFilter, priority: number): Promise<number> {\n const { clause, args: filterArgs } = this._filter_clause(filter);\n // libSQL `?` placeholders are positional and NOT reusable, so we\n // bind `priority` twice: once for SET, once for the no-op skip\n // in WHERE.\n const sql = `UPDATE streams SET priority = ?\n WHERE priority <> ? AND ${clause}`;\n const result = await this.client.execute({\n sql,\n args: [priority, priority, ...filterArgs] as any[],\n });\n return result.rowsAffected;\n }\n\n // --- truncate: transactional delete + seed ---\n async truncate(\n targets: Array<{\n stream: string;\n snapshot?: Record<string, unknown>;\n meta?: EventMeta;\n }>\n ) {\n const result = new Map<\n string,\n { deleted: number; committed: Committed<Schemas, keyof Schemas> }\n >();\n\n const tx = await this.client.transaction(\"write\");\n try {\n for (const { stream, snapshot, meta } of targets) {\n const count_row = await tx.execute({\n sql: \"SELECT COUNT(*) as c FROM events WHERE stream = ?\",\n args: [stream],\n });\n const deleted = Number(count_row.rows[0].c);\n await tx.execute({\n sql: \"DELETE FROM events WHERE stream = ?\",\n args: [stream],\n });\n await tx.execute({\n sql: \"DELETE FROM streams WHERE stream = ?\",\n args: [stream],\n });\n\n const event_name =\n snapshot !== undefined ? \"__snapshot__\" : \"__tombstone__\";\n const event_meta = meta ?? { correlation: \"\", causation: {} };\n const now = new Date().toISOString();\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, 0, ?, ?, ?, ?)\",\n args: [\n stream,\n event_name,\n JSON.stringify(snapshot ?? {}),\n JSON.stringify(event_meta),\n now,\n ],\n });\n\n result.set(stream, {\n deleted,\n committed: {\n id: Number(ins.lastInsertRowid),\n stream,\n version: 0,\n created: new Date(now),\n name: event_name,\n data: snapshot ?? {},\n meta: event_meta,\n },\n });\n }\n await tx.commit();\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n\n return result;\n }\n\n /**\n * Atomically wipe-and-rebuild the store inside a single libsql\n * `write` transaction.\n *\n * On any throw inside the driver the transaction rolls back and the\n * store is byte-for-byte unchanged. `DELETE FROM events` + `DELETE\n * FROM streams` wipe both tables; `DELETE FROM sqlite_sequence\n * WHERE name = 'events'` resets the autoincrement counter so the\n * new sequence is dense from 1. `created` is preserved verbatim\n * from the source.\n */\n async restore(\n driver: (\n callback: (event: Committed<Schemas, keyof Schemas>) => Promise<number>\n ) => Promise<void>\n ): Promise<void> {\n const tx = await this.client.transaction(\"write\");\n try {\n await tx.execute(\"DELETE FROM events\");\n await tx.execute(\"DELETE FROM streams\");\n // Reset the autoincrement counter so the new sequence is dense\n // from 1. `DELETE FROM sqlite_sequence WHERE name = '?'` is the\n // canonical SQLite reset; safe even if the row doesn't exist.\n await tx.execute(\"DELETE FROM sqlite_sequence WHERE name = 'events'\");\n await driver(async (event) => {\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, ?, ?, ?, ?, ?)\",\n args: [\n event.stream,\n event.version,\n event.name,\n JSON.stringify(event.data),\n JSON.stringify(event.meta),\n event.created.toISOString(),\n ],\n });\n return Number(ins.lastInsertRowid);\n });\n await tx.commit();\n } catch (error) {\n await tx.rollback();\n throw error;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rotorsoft/act-sqlite",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.1",
|
|
5
5
|
"description": "act sqlite adapters",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"typescript",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"sideEffects": false,
|
|
39
39
|
"engines": {
|
|
40
|
-
"node": ">=22.
|
|
40
|
+
"node": ">=22.22.3"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"zod": "^4.4.3",
|
|
50
|
-
"@rotorsoft/act": "^1.
|
|
50
|
+
"@rotorsoft/act": "^1.10.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@rotorsoft/act-tck": "^1.
|
|
53
|
+
"@rotorsoft/act-tck": "^1.5.0"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"clean": "rm -rf dist",
|