@rotorsoft/act-sqlite 2.0.2 → 2.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/dist/.tsbuildinfo +1 -1
- package/dist/@types/sqlite-store.d.ts +0 -37
- package/dist/@types/sqlite-store.d.ts.map +1 -1
- package/dist/@types/stream-glob.d.ts +37 -0
- package/dist/@types/stream-glob.d.ts.map +1 -0
- package/dist/index.cjs +59 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +59 -57
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2,57 +2,26 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createClient
|
|
4
4
|
} from "@libsql/client";
|
|
5
|
+
import { ConcurrencyError, StoreError, ValidationError as ValidationError2 } from "@rotorsoft/act";
|
|
5
6
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
throw_if_stale,
|
|
15
|
-
ValidationError
|
|
16
|
-
} from "@rotorsoft/act";
|
|
7
|
+
checkAppends,
|
|
8
|
+
checkVersion,
|
|
9
|
+
concurrencyError,
|
|
10
|
+
fairSlots,
|
|
11
|
+
isStreamName,
|
|
12
|
+
seedEvent
|
|
13
|
+
} from "@rotorsoft/act/adapter";
|
|
14
|
+
import { DEFAULT_QUERY_STREAMS_LIMIT } from "@rotorsoft/act/types";
|
|
17
15
|
import {
|
|
18
16
|
decrypt,
|
|
19
17
|
encrypt,
|
|
20
18
|
makeKeyResolver
|
|
21
19
|
} from "@rotorsoft/act-crypto";
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (typeof url !== "string" || url.trim().length === 0)
|
|
26
|
-
throw new ValidationError(
|
|
27
|
-
'SqliteStore config \u2014 "url" is required and has no default. Use a file database ("file:myapp.db"), a remote libSQL server ("libsql://\u2026" plus authToken), or ":memory:" for a process-wide shared in-memory database. For isolated throwaway state, prefer InMemoryStore from @rotorsoft/act',
|
|
28
|
-
{ url },
|
|
29
|
-
"missing url"
|
|
30
|
-
);
|
|
31
|
-
if (url.startsWith("file::memory:?") && url.includes("cache=private"))
|
|
32
|
-
throw new ValidationError(
|
|
33
|
-
'SqliteStore config \u2014 a private in-memory database ("cache=private") cannot back a store: libSQL gives each connection its own database, so writes are accepted and then lost on the next statement. Use ":memory:" for the shared-cache database, a "file:" path, or InMemoryStore from @rotorsoft/act',
|
|
34
|
-
{ url },
|
|
35
|
-
"private in-memory url"
|
|
36
|
-
);
|
|
37
|
-
return is_in_memory_url(url) ? SHARED_MEMORY_URL : url;
|
|
38
|
-
};
|
|
39
|
-
var parse_json = (raw) => JSON.parse(raw);
|
|
40
|
-
var to_committed = (row, prefix = "") => ({
|
|
41
|
-
id: Number(row[`${prefix}id`]),
|
|
42
|
-
stream: row[`${prefix}stream`],
|
|
43
|
-
version: Number(row[`${prefix}version`]),
|
|
44
|
-
created: new Date(row[`${prefix}created`]),
|
|
45
|
-
name: row[`${prefix}name`],
|
|
46
|
-
data: parse_json(row[`${prefix}data`]),
|
|
47
|
-
meta: parse_json(row[`${prefix}meta`])
|
|
48
|
-
});
|
|
49
|
-
var SQLITE_CONSTRAINT_UNIQUE = 2067;
|
|
50
|
-
var is_unique_violation = (e) => {
|
|
51
|
-
const err = e;
|
|
52
|
-
return err?.rawCode === SQLITE_CONSTRAINT_UNIQUE || typeof err?.message === "string" && err.message.includes("UNIQUE constraint failed");
|
|
53
|
-
};
|
|
20
|
+
|
|
21
|
+
// src/stream-glob.ts
|
|
22
|
+
import { ValidationError } from "@rotorsoft/act";
|
|
54
23
|
var PORTABLE_BODY = /^(?:\.\*|\.|[^\\^$.()[\]{}|+?*])*$/;
|
|
55
|
-
function
|
|
24
|
+
function stream_glob(input) {
|
|
56
25
|
let s = input;
|
|
57
26
|
const start = s.startsWith("^");
|
|
58
27
|
const end = s.endsWith("$");
|
|
@@ -85,6 +54,40 @@ function streamPatternToGlob(input) {
|
|
|
85
54
|
}
|
|
86
55
|
return out;
|
|
87
56
|
}
|
|
57
|
+
|
|
58
|
+
// src/sqlite-store.ts
|
|
59
|
+
var SHARED_MEMORY_URL = "file::memory:?cache=shared";
|
|
60
|
+
var is_in_memory_url = (url) => url === ":memory:" || url === "file::memory:" || url.startsWith("file::memory:?");
|
|
61
|
+
var resolve_url = (url) => {
|
|
62
|
+
if (typeof url !== "string" || url.trim().length === 0)
|
|
63
|
+
throw new ValidationError2(
|
|
64
|
+
'SqliteStore config \u2014 "url" is required and has no default. Use a file database ("file:myapp.db"), a remote libSQL server ("libsql://\u2026" plus authToken), or ":memory:" for a process-wide shared in-memory database. For isolated throwaway state, prefer InMemoryStore from @rotorsoft/act',
|
|
65
|
+
{ url },
|
|
66
|
+
"missing url"
|
|
67
|
+
);
|
|
68
|
+
if (url.startsWith("file::memory:?") && url.includes("cache=private"))
|
|
69
|
+
throw new ValidationError2(
|
|
70
|
+
'SqliteStore config \u2014 a private in-memory database ("cache=private") cannot back a store: libSQL gives each connection its own database, so writes are accepted and then lost on the next statement. Use ":memory:" for the shared-cache database, a "file:" path, or InMemoryStore from @rotorsoft/act',
|
|
71
|
+
{ url },
|
|
72
|
+
"private in-memory url"
|
|
73
|
+
);
|
|
74
|
+
return is_in_memory_url(url) ? SHARED_MEMORY_URL : url;
|
|
75
|
+
};
|
|
76
|
+
var parse_json = (raw) => JSON.parse(raw);
|
|
77
|
+
var to_committed = (row, prefix = "") => ({
|
|
78
|
+
id: Number(row[`${prefix}id`]),
|
|
79
|
+
stream: row[`${prefix}stream`],
|
|
80
|
+
version: Number(row[`${prefix}version`]),
|
|
81
|
+
created: new Date(row[`${prefix}created`]),
|
|
82
|
+
name: row[`${prefix}name`],
|
|
83
|
+
data: parse_json(row[`${prefix}data`]),
|
|
84
|
+
meta: parse_json(row[`${prefix}meta`])
|
|
85
|
+
});
|
|
86
|
+
var SQLITE_CONSTRAINT_UNIQUE = 2067;
|
|
87
|
+
var is_unique_violation = (e) => {
|
|
88
|
+
const err = e;
|
|
89
|
+
return err?.rawCode === SQLITE_CONSTRAINT_UNIQUE || typeof err?.message === "string" && err.message.includes("UNIQUE constraint failed");
|
|
90
|
+
};
|
|
88
91
|
var SqliteStore = class {
|
|
89
92
|
client;
|
|
90
93
|
// Memoized key resolver for the optional `pii_encryption` envelope.
|
|
@@ -285,7 +288,7 @@ var SqliteStore = class {
|
|
|
285
288
|
}
|
|
286
289
|
// --- commit: one transaction across every append ---
|
|
287
290
|
async commit(appends, meta) {
|
|
288
|
-
|
|
291
|
+
checkAppends(appends);
|
|
289
292
|
if (!appends.length) return [];
|
|
290
293
|
return this._serial(async () => {
|
|
291
294
|
const tx = await this._begin("commit");
|
|
@@ -299,7 +302,7 @@ var SqliteStore = class {
|
|
|
299
302
|
args: [append.stream]
|
|
300
303
|
});
|
|
301
304
|
const current = Number(row.rows[0].v);
|
|
302
|
-
|
|
305
|
+
checkVersion(append, current);
|
|
303
306
|
heads.set(append.stream, current);
|
|
304
307
|
}
|
|
305
308
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -340,7 +343,7 @@ var SqliteStore = class {
|
|
|
340
343
|
} catch (e) {
|
|
341
344
|
await tx.rollback();
|
|
342
345
|
if (e instanceof ConcurrencyError) throw e;
|
|
343
|
-
if (is_unique_violation(e)) throw
|
|
346
|
+
if (is_unique_violation(e)) throw concurrencyError(appending, head);
|
|
344
347
|
throw new StoreError("commit", { cause: e });
|
|
345
348
|
}
|
|
346
349
|
});
|
|
@@ -355,7 +358,7 @@ var SqliteStore = class {
|
|
|
355
358
|
args.push(query.stream);
|
|
356
359
|
} else {
|
|
357
360
|
sql += " AND stream GLOB ?";
|
|
358
|
-
args.push(
|
|
361
|
+
args.push(stream_glob(query.stream));
|
|
359
362
|
}
|
|
360
363
|
}
|
|
361
364
|
if (query?.names !== void 0) {
|
|
@@ -413,8 +416,8 @@ var SqliteStore = class {
|
|
|
413
416
|
// through `prioritize()` instead.
|
|
414
417
|
async subscribe(streams, correlated_at, correlator) {
|
|
415
418
|
for (const { stream, source } of streams) {
|
|
416
|
-
if (source !== void 0 && !
|
|
417
|
-
|
|
419
|
+
if (source !== void 0 && !isStreamName(source, stream))
|
|
420
|
+
stream_glob(source);
|
|
418
421
|
}
|
|
419
422
|
return this._tx("subscribe", async (tx) => {
|
|
420
423
|
let subscribed = 0;
|
|
@@ -547,7 +550,7 @@ var SqliteStore = class {
|
|
|
547
550
|
priority: Number(row.priority),
|
|
548
551
|
lane: row.lane
|
|
549
552
|
})).sort((a, b) => b.priority - a.priority || a.at - b.at);
|
|
550
|
-
const fair =
|
|
553
|
+
const fair = fairSlots(lagging);
|
|
551
554
|
const priority_slice = candidates.slice(0, lagging - fair);
|
|
552
555
|
const picked = new Set(priority_slice.map((c) => c.stream));
|
|
553
556
|
const fair_slice = [...candidates].sort((a, b) => a.at - b.at).filter((c) => !picked.has(c.stream)).slice(0, fair);
|
|
@@ -705,7 +708,7 @@ var SqliteStore = class {
|
|
|
705
708
|
args.push(filter.stream);
|
|
706
709
|
} else {
|
|
707
710
|
conditions.push("stream GLOB ?");
|
|
708
|
-
args.push(
|
|
711
|
+
args.push(stream_glob(filter.stream));
|
|
709
712
|
}
|
|
710
713
|
}
|
|
711
714
|
if (filter.source !== void 0) {
|
|
@@ -715,7 +718,7 @@ var SqliteStore = class {
|
|
|
715
718
|
args.push(filter.source);
|
|
716
719
|
} else {
|
|
717
720
|
conditions.push("source GLOB ?");
|
|
718
|
-
args.push(
|
|
721
|
+
args.push(stream_glob(filter.source));
|
|
719
722
|
}
|
|
720
723
|
}
|
|
721
724
|
if (filter.blocked !== void 0) {
|
|
@@ -838,7 +841,7 @@ var SqliteStore = class {
|
|
|
838
841
|
args.push(input.stream);
|
|
839
842
|
} else {
|
|
840
843
|
where.push(`e.stream GLOB ?`);
|
|
841
|
-
args.push(
|
|
844
|
+
args.push(stream_glob(input.stream));
|
|
842
845
|
}
|
|
843
846
|
}
|
|
844
847
|
if (exclude.length) {
|
|
@@ -1029,7 +1032,7 @@ var SqliteStore = class {
|
|
|
1029
1032
|
sql: "DELETE FROM events WHERE stream = ?",
|
|
1030
1033
|
args: [stream]
|
|
1031
1034
|
});
|
|
1032
|
-
const seed =
|
|
1035
|
+
const seed = seedEvent(snapshot, meta);
|
|
1033
1036
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1034
1037
|
const ins = await tx.execute({
|
|
1035
1038
|
sql: "INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, 0, ?, ?, ?, ?)",
|
|
@@ -1125,7 +1128,6 @@ var SqliteStore = class {
|
|
|
1125
1128
|
}
|
|
1126
1129
|
};
|
|
1127
1130
|
export {
|
|
1128
|
-
SqliteStore
|
|
1129
|
-
streamPatternToGlob
|
|
1131
|
+
SqliteStore
|
|
1130
1132
|
};
|
|
1131
1133
|
//# sourceMappingURL=index.js.map
|