sporades 0.9.29 โ 0.9.30
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/CHANGES.md +6 -14
- package/bin/sporades-host-helper.js +12 -1
- package/bin/sporades.js +125 -19
- package/dist/cli/cli-version.d.ts +1 -1
- package/dist/cli/cli-version.js +1 -1
- package/dist/database-runtime.d.ts.map +1 -1
- package/dist/database-runtime.js +34 -4
- package/dist/database-runtime.js.map +1 -1
- package/dist/generated-source-manifest.json +7 -2
- package/dist/live-query-invalidation.d.ts +22 -0
- package/dist/live-query-invalidation.d.ts.map +1 -0
- package/dist/live-query-invalidation.js +71 -0
- package/dist/live-query-invalidation.js.map +1 -0
- package/dist/promise-coordinator.d.ts +2 -1
- package/dist/promise-coordinator.d.ts.map +1 -1
- package/dist/promise-coordinator.js +37 -10
- package/dist/promise-coordinator.js.map +1 -1
- package/dist/server-runtime-source.d.ts.map +1 -1
- package/dist/server-runtime-source.js +16 -2
- package/dist/server-runtime-source.js.map +1 -1
- package/package.json +1 -1
package/CHANGES.md
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
-
## Unreleased - 2026-09-
|
|
3
|
+
## Unreleased - 2026-09-24
|
|
4
4
|
|
|
5
|
-
Changes since v0.9.
|
|
5
|
+
Changes since v0.9.29.
|
|
6
6
|
|
|
7
7
|
### ๐ Bug Fixes
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Start no freshclam after retained-updater cleanup once shutdown began (0185fc01).
|
|
12
|
-
- Close ClamAV refresh shutdown race and recover crashed clamd (6c23b686).
|
|
13
|
-
- Start degraded on stale ClamAV signatures and allow 26h freshness (79dd3ece).
|
|
14
|
-
- Own ClamAV signature refresh in the Capsule runtime (17193be4).
|
|
15
|
-
|
|
16
|
-
### ๐งช Tests
|
|
17
|
-
|
|
18
|
-
- Buffer raw WebSocket frames across TCP chunks (e07288dd).
|
|
19
|
-
- Describe the ClamAV freshness gate without a fixed age (b180ac1a).
|
|
9
|
+
- Refresh only live queries that read a written table (6942754d).
|
|
10
|
+
- Detect Promise combinators from call sites, not formatted stacks (a55a4fd3).
|
|
20
11
|
|
|
21
12
|
### ๐ฆ Packaging
|
|
22
13
|
|
|
23
|
-
- Regenerate runtime artifacts for
|
|
14
|
+
- Regenerate runtime artifacts for scoped live query refresh (14235222).
|
|
24
15
|
|
|
25
16
|
## v0.9.11 - 2026-09-02
|
|
26
17
|
|
|
@@ -123,3 +114,4 @@ generated artifacts, and documentation.
|
|
|
123
114
|
|
|
124
115
|
|
|
125
116
|
|
|
117
|
+
|
|
@@ -26881,6 +26881,17 @@ function logPayloadMaxBytes(config = {}) {
|
|
|
26881
26881
|
return config.logs?.payloadMaxBytes ?? config.logging?.payloadMaxBytes ?? 4096;
|
|
26882
26882
|
}
|
|
26883
26883
|
|
|
26884
|
+
// src/live-query-invalidation.ts
|
|
26885
|
+
var { AsyncLocalStorage } = process.getBuiltinModule("node:async_hooks");
|
|
26886
|
+
var liveQueryTablesTracked = Symbol.for("sporades.database.liveQueryTablesTracked");
|
|
26887
|
+
var liveQueryReads = new AsyncLocalStorage();
|
|
26888
|
+
var quotedIdentifier = String.raw`(?:\[([^\]]+)\]|"([^"]+)")`;
|
|
26889
|
+
var readTablePattern = new RegExp(String.raw`\b(?:FROM|JOIN)\s+${quotedIdentifier}`, "gi");
|
|
26890
|
+
var writeTablePattern = new RegExp(
|
|
26891
|
+
String.raw`^\s*(?:INSERT(?:\s+OR\s+\w+)?\s+INTO|REPLACE\s+INTO|UPDATE(?:\s+OR\s+\w+)?|DELETE\s+FROM)\s+${quotedIdentifier}`,
|
|
26892
|
+
"i"
|
|
26893
|
+
);
|
|
26894
|
+
|
|
26884
26895
|
// src/auth-admission.ts
|
|
26885
26896
|
var AUTH_REQUIREMENTS = Symbol.for("sporades.auth.requirements");
|
|
26886
26897
|
var ACCESS_KEY_SCOPE_LIMIT = 1024;
|
|
@@ -42989,7 +43000,7 @@ var MutationExecutionStorage = process.getBuiltinModule("node:async_hooks").Asyn
|
|
|
42989
43000
|
var mutationExecution = new MutationExecutionStorage();
|
|
42990
43001
|
|
|
42991
43002
|
// src/cli/cli-version.ts
|
|
42992
|
-
var CLI_VERSION = "0.9.
|
|
43003
|
+
var CLI_VERSION = "0.9.30";
|
|
42993
43004
|
|
|
42994
43005
|
// src/cli/schedule-inspection-envelope.ts
|
|
42995
43006
|
var SCHEDULE_DIAGNOSTIC_FIELDS = /* @__PURE__ */ new Set([
|
package/bin/sporades.js
CHANGED
|
@@ -91674,7 +91674,26 @@ var promiseHookStop;
|
|
|
91674
91674
|
var compositionRootCandidates = [];
|
|
91675
91675
|
var thenableWrapperRoots = /* @__PURE__ */ new WeakSet();
|
|
91676
91676
|
var promiseCombinatorKinds = /* @__PURE__ */ new WeakMap();
|
|
91677
|
+
var promiseCombinatorNames = /* @__PURE__ */ new Set(["all", "allSettled", "any", "race"]);
|
|
91677
91678
|
var compositionRootClearQueued = false;
|
|
91679
|
+
function promiseCombinatorOnStack() {
|
|
91680
|
+
const prepareStackTrace = Error.prepareStackTrace;
|
|
91681
|
+
let callSites;
|
|
91682
|
+
Error.prepareStackTrace = (_error, sites) => sites;
|
|
91683
|
+
try {
|
|
91684
|
+
callSites = new Error().stack;
|
|
91685
|
+
} finally {
|
|
91686
|
+
Error.prepareStackTrace = prepareStackTrace;
|
|
91687
|
+
}
|
|
91688
|
+
if (!Array.isArray(callSites)) return void 0;
|
|
91689
|
+
for (const site of callSites) {
|
|
91690
|
+
if (site.isAsync?.()) continue;
|
|
91691
|
+
const name2 = site.getFunctionName();
|
|
91692
|
+
const type = site.getTypeName();
|
|
91693
|
+
if (name2 && promiseCombinatorNames.has(name2) && (type === "Promise" || type === "Function")) return name2;
|
|
91694
|
+
}
|
|
91695
|
+
return void 0;
|
|
91696
|
+
}
|
|
91678
91697
|
function retainCompositionRootCandidate(promise) {
|
|
91679
91698
|
compositionRootCandidates.push(promise);
|
|
91680
91699
|
if (compositionRootClearQueued) return;
|
|
@@ -91690,11 +91709,11 @@ function installPromiseHook() {
|
|
|
91690
91709
|
init(promise, parent) {
|
|
91691
91710
|
if (parent) promiseParents.set(promise, parent);
|
|
91692
91711
|
else retainCompositionRootCandidate(promise);
|
|
91693
|
-
const
|
|
91694
|
-
if (parent &&
|
|
91712
|
+
const combinator = parent ? promiseCombinatorOnStack() : void 0;
|
|
91713
|
+
if (parent && combinator) {
|
|
91695
91714
|
const root = [...compositionRootCandidates].reverse().find((candidate) => !thenableWrapperRoots.has(candidate));
|
|
91696
91715
|
if (root) {
|
|
91697
|
-
promiseCombinatorKinds.set(root,
|
|
91716
|
+
promiseCombinatorKinds.set(root, combinator);
|
|
91698
91717
|
const inputs = promiseCombinatorInputs.get(root) ?? /* @__PURE__ */ new Set();
|
|
91699
91718
|
inputs.add(parent);
|
|
91700
91719
|
promiseCombinatorInputs.set(root, inputs);
|
|
@@ -91769,25 +91788,23 @@ function promiseCompositionRootCandidate() {
|
|
|
91769
91788
|
const wrapper = compositionRootCandidates.at(-1);
|
|
91770
91789
|
if (!wrapper) return void 0;
|
|
91771
91790
|
thenableWrapperRoots.add(wrapper);
|
|
91772
|
-
const
|
|
91773
|
-
|
|
91774
|
-
if (!match) return void 0;
|
|
91791
|
+
const combinator = promiseCombinatorOnStack();
|
|
91792
|
+
if (!combinator) return void 0;
|
|
91775
91793
|
for (let index = compositionRootCandidates.length - 2; index >= 0; index -= 1) {
|
|
91776
91794
|
const candidate = compositionRootCandidates[index];
|
|
91777
91795
|
if (!thenableWrapperRoots.has(candidate)) {
|
|
91778
|
-
promiseCombinatorKinds.set(candidate,
|
|
91796
|
+
promiseCombinatorKinds.set(candidate, combinator);
|
|
91779
91797
|
return candidate;
|
|
91780
91798
|
}
|
|
91781
91799
|
}
|
|
91782
|
-
promiseCombinatorKinds.set(wrapper,
|
|
91800
|
+
promiseCombinatorKinds.set(wrapper, combinator);
|
|
91783
91801
|
return wrapper;
|
|
91784
91802
|
}
|
|
91785
91803
|
function promiseCombinatorKind(promise) {
|
|
91786
91804
|
return promiseCombinatorKinds.get(promise);
|
|
91787
91805
|
}
|
|
91788
91806
|
function enclosingPromiseCombinatorRoot() {
|
|
91789
|
-
|
|
91790
|
-
if (!/at (?:Promise|Function)\.(?:all|allSettled|any|race)\b/.test(stack)) return void 0;
|
|
91807
|
+
if (!promiseCombinatorOnStack()) return void 0;
|
|
91791
91808
|
for (let index = compositionRootCandidates.length - 1; index >= 0; index -= 1) {
|
|
91792
91809
|
const candidate = compositionRootCandidates[index];
|
|
91793
91810
|
if (!settledPromises.has(candidate)) return candidate;
|
|
@@ -114132,6 +114149,53 @@ function createEmailEventEndpoints(mailConfig, serverEnv, subscription) {
|
|
|
114132
114149
|
return endpoints;
|
|
114133
114150
|
}
|
|
114134
114151
|
|
|
114152
|
+
// src/live-query-invalidation.ts
|
|
114153
|
+
var { AsyncLocalStorage } = process.getBuiltinModule("node:async_hooks");
|
|
114154
|
+
var liveQueryTablesTracked = Symbol.for("sporades.database.liveQueryTablesTracked");
|
|
114155
|
+
var LIVE_QUERY_ANY_TABLE = "*";
|
|
114156
|
+
var liveQueryReads = new AsyncLocalStorage();
|
|
114157
|
+
var dirtyTables = /* @__PURE__ */ new Set();
|
|
114158
|
+
var quotedIdentifier = String.raw`(?:\[([^\]]+)\]|"([^"]+)")`;
|
|
114159
|
+
var readTablePattern = new RegExp(String.raw`\b(?:FROM|JOIN)\s+${quotedIdentifier}`, "gi");
|
|
114160
|
+
var writeTablePattern = new RegExp(
|
|
114161
|
+
String.raw`^\s*(?:INSERT(?:\s+OR\s+\w+)?\s+INTO|REPLACE\s+INTO|UPDATE(?:\s+OR\s+\w+)?|DELETE\s+FROM)\s+${quotedIdentifier}`,
|
|
114162
|
+
"i"
|
|
114163
|
+
);
|
|
114164
|
+
var nonWritingStatementPattern = /^\s*(?:BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE|PRAGMA|SELECT)\b/i;
|
|
114165
|
+
function trackLiveQueryReads(tables, run2) {
|
|
114166
|
+
return liveQueryReads.run(tables, run2);
|
|
114167
|
+
}
|
|
114168
|
+
function recordLiveQueryStatementRead(sql2) {
|
|
114169
|
+
const tables = liveQueryReads.getStore();
|
|
114170
|
+
if (!tables) return;
|
|
114171
|
+
let identified = false;
|
|
114172
|
+
for (const match of String(sql2).matchAll(readTablePattern)) {
|
|
114173
|
+
tables.add(match[1] ?? match[2]);
|
|
114174
|
+
identified = true;
|
|
114175
|
+
}
|
|
114176
|
+
if (!identified) tables.add(LIVE_QUERY_ANY_TABLE);
|
|
114177
|
+
}
|
|
114178
|
+
function recordLiveQueryTableRead(table) {
|
|
114179
|
+
liveQueryReads.getStore()?.add(table);
|
|
114180
|
+
}
|
|
114181
|
+
function recordLiveQueryStatementWrite(sql2, result) {
|
|
114182
|
+
if (result && typeof result === "object" && "changes" in result && Number(result.changes) === 0) return;
|
|
114183
|
+
const text2 = String(sql2);
|
|
114184
|
+
if (nonWritingStatementPattern.test(text2)) return;
|
|
114185
|
+
const match = writeTablePattern.exec(text2);
|
|
114186
|
+
dirtyTables.add(match ? match[1] ?? match[2] : LIVE_QUERY_ANY_TABLE);
|
|
114187
|
+
}
|
|
114188
|
+
function takeLiveQueryDirtyTables() {
|
|
114189
|
+
const taken = dirtyTables;
|
|
114190
|
+
dirtyTables = /* @__PURE__ */ new Set();
|
|
114191
|
+
return taken;
|
|
114192
|
+
}
|
|
114193
|
+
function liveQueryNeedsRefresh(readTables, dirty) {
|
|
114194
|
+
if (!readTables || dirty.has(LIVE_QUERY_ANY_TABLE)) return true;
|
|
114195
|
+
for (const table of readTables) if (dirty.has(table)) return true;
|
|
114196
|
+
return false;
|
|
114197
|
+
}
|
|
114198
|
+
|
|
114135
114199
|
// src/service-users-runtime.ts
|
|
114136
114200
|
var SERVICE_USER_DISPLAY_NAME_BYTES = 160;
|
|
114137
114201
|
function serviceUserError(code, message, hint) {
|
|
@@ -115097,8 +115161,8 @@ function chainSchemaOperation(previous, operation) {
|
|
|
115097
115161
|
var nodeCryptoModule4 = process.getBuiltinModule("node:crypto");
|
|
115098
115162
|
var nodeFsModule = process.getBuiltinModule("node:fs");
|
|
115099
115163
|
function createConnectionTransactionGate() {
|
|
115100
|
-
const
|
|
115101
|
-
const transactionOwnership = new
|
|
115164
|
+
const AsyncLocalStorage2 = process.getBuiltinModule("node:async_hooks").AsyncLocalStorage;
|
|
115165
|
+
const transactionOwnership = new AsyncLocalStorage2();
|
|
115102
115166
|
const transactionOwner = Object.freeze({});
|
|
115103
115167
|
let transactionTail = Promise.resolve();
|
|
115104
115168
|
let transactionActive = false;
|
|
@@ -116449,18 +116513,28 @@ async function createSqliteDatabaseAdapter(databasePath, options = {}) {
|
|
|
116449
116513
|
};
|
|
116450
116514
|
return {
|
|
116451
116515
|
exec(sql2) {
|
|
116452
|
-
return run2(() => useConnection(() =>
|
|
116516
|
+
return run2(() => useConnection(() => {
|
|
116517
|
+
const result = connection.exec(sql2);
|
|
116518
|
+
recordLiveQueryStatementWrite(sql2);
|
|
116519
|
+
return result;
|
|
116520
|
+
}));
|
|
116453
116521
|
},
|
|
116454
116522
|
prepare(sql2) {
|
|
116455
116523
|
return {
|
|
116456
116524
|
all(...params) {
|
|
116525
|
+
recordLiveQueryStatementRead(sql2);
|
|
116457
116526
|
return run2(() => useConnection(() => connection.prepare(sql2).all(...params)));
|
|
116458
116527
|
},
|
|
116459
116528
|
get(...params) {
|
|
116529
|
+
recordLiveQueryStatementRead(sql2);
|
|
116460
116530
|
return run2(() => useConnection(() => connection.prepare(sql2).get(...params)));
|
|
116461
116531
|
},
|
|
116462
116532
|
run(...params) {
|
|
116463
|
-
return run2(() => useConnection(() =>
|
|
116533
|
+
return run2(() => useConnection(() => {
|
|
116534
|
+
const result = connection.prepare(sql2).run(...params);
|
|
116535
|
+
recordLiveQueryStatementWrite(sql2, result);
|
|
116536
|
+
return result;
|
|
116537
|
+
}));
|
|
116464
116538
|
},
|
|
116465
116539
|
columns() {
|
|
116466
116540
|
return run2(() => useConnection(() => connection.prepare(sql2).columns()));
|
|
@@ -116473,6 +116547,9 @@ async function createSqliteDatabaseAdapter(databasePath, options = {}) {
|
|
|
116473
116547
|
...createSharedDatabaseAdapterMethods(dialect),
|
|
116474
116548
|
...createOperations(connectionGate.runOperation),
|
|
116475
116549
|
engine: "sqlite",
|
|
116550
|
+
// Every statement on the shared and dedicated connections reports its tables, so live
|
|
116551
|
+
// query refreshes can be scoped to the tables a write changed.
|
|
116552
|
+
[liveQueryTablesTracked]: true,
|
|
116476
116553
|
// Outer resources must be able to open one independent durable SQLite
|
|
116477
116554
|
// connection. This runtime-owned marker propagates through transaction
|
|
116478
116555
|
// adapters; callers cannot opt an in-memory or read-only adapter in.
|
|
@@ -116492,8 +116569,30 @@ async function createSqliteDatabaseAdapter(databasePath, options = {}) {
|
|
|
116492
116569
|
let begun = false;
|
|
116493
116570
|
let commitIssued = false;
|
|
116494
116571
|
const operations = {
|
|
116495
|
-
exec: (sql2) =>
|
|
116496
|
-
|
|
116572
|
+
exec: (sql2) => {
|
|
116573
|
+
const result = dedicated.exec(sql2);
|
|
116574
|
+
recordLiveQueryStatementWrite(sql2);
|
|
116575
|
+
return result;
|
|
116576
|
+
},
|
|
116577
|
+
prepare: (sql2) => {
|
|
116578
|
+
const statement = dedicated.prepare(sql2);
|
|
116579
|
+
return Object.assign(Object.create(statement), {
|
|
116580
|
+
all: (...params) => {
|
|
116581
|
+
recordLiveQueryStatementRead(sql2);
|
|
116582
|
+
return statement.all(...params);
|
|
116583
|
+
},
|
|
116584
|
+
get: (...params) => {
|
|
116585
|
+
recordLiveQueryStatementRead(sql2);
|
|
116586
|
+
return statement.get(...params);
|
|
116587
|
+
},
|
|
116588
|
+
run: (...params) => {
|
|
116589
|
+
const result = statement.run(...params);
|
|
116590
|
+
recordLiveQueryStatementWrite(sql2, result);
|
|
116591
|
+
return result;
|
|
116592
|
+
},
|
|
116593
|
+
columns: () => statement.columns()
|
|
116594
|
+
});
|
|
116595
|
+
}
|
|
116497
116596
|
};
|
|
116498
116597
|
const transaction = createTransactionScopedAdapter(adapter, operations, adapter, "transaction");
|
|
116499
116598
|
try {
|
|
@@ -123214,9 +123313,11 @@ function createWebSocketHub(getDatabase, trustedRefresh = null, options = {}) {
|
|
|
123214
123313
|
subscription.generation = generation;
|
|
123215
123314
|
try {
|
|
123216
123315
|
const database = getDatabase();
|
|
123217
|
-
const
|
|
123316
|
+
const readTables = /* @__PURE__ */ new Set();
|
|
123317
|
+
const result = await trackLiveQueryReads(readTables, () => runQuery(database, client.session.auth, subscription.name, subscription.args, {
|
|
123218
123318
|
sessionToken: client.session.token
|
|
123219
|
-
});
|
|
123319
|
+
}));
|
|
123320
|
+
if (subscription.generation === generation) subscription.readTables = result?.error || readTables.has(LIVE_QUERY_ANY_TABLE) ? null : readTables;
|
|
123220
123321
|
const data2 = subscription.style === "direct" ? result.data ?? result.rows : { rows: result.data ?? result.rows };
|
|
123221
123322
|
if (client.subscriptions.get(subscription.id) !== subscription || subscription.generation !== generation) return;
|
|
123222
123323
|
sendJson(client, {
|
|
@@ -123228,6 +123329,7 @@ function createWebSocketHub(getDatabase, trustedRefresh = null, options = {}) {
|
|
|
123228
123329
|
});
|
|
123229
123330
|
} catch (error) {
|
|
123230
123331
|
if (client.subscriptions.get(subscription.id) !== subscription || subscription.generation !== generation) return;
|
|
123332
|
+
subscription.readTables = null;
|
|
123231
123333
|
try {
|
|
123232
123334
|
onError(error);
|
|
123233
123335
|
} catch {
|
|
@@ -123235,8 +123337,11 @@ function createWebSocketHub(getDatabase, trustedRefresh = null, options = {}) {
|
|
|
123235
123337
|
}
|
|
123236
123338
|
}
|
|
123237
123339
|
function refreshQueries() {
|
|
123340
|
+
const dirty = takeLiveQueryDirtyTables();
|
|
123341
|
+
const scoped = getDatabase()?.adapter?.[liveQueryTablesTracked] === true;
|
|
123238
123342
|
for (const subscribedClient of clients) {
|
|
123239
123343
|
for (const subscription of subscribedClient.subscriptions.values()) {
|
|
123344
|
+
if (scoped && !liveQueryNeedsRefresh(subscription.readTables, dirty)) continue;
|
|
123240
123345
|
void sendQueryResult(
|
|
123241
123346
|
subscribedClient,
|
|
123242
123347
|
subscription,
|
|
@@ -123515,6 +123620,7 @@ async function runQuery(database, auth, queryName, rawArgs = [], options = {}) {
|
|
|
123515
123620
|
}
|
|
123516
123621
|
if (args.length > 0) return { rows: null, data: null, error: invalidQueryArgumentsError() };
|
|
123517
123622
|
const cacheKey = `${table.name}:${context.auth.userId}`;
|
|
123623
|
+
recordLiveQueryTableRead(table.name);
|
|
123518
123624
|
if (!database.rowCache.has(cacheKey)) {
|
|
123519
123625
|
const columns = ["id", "createdAt", "updatedAt", ...table.fields.map((field) => field.name)];
|
|
123520
123626
|
const ownerScoped = table.fields.some((field) => field.name === "ownerId");
|
|
@@ -131249,7 +131355,7 @@ jobs:
|
|
|
131249
131355
|
}
|
|
131250
131356
|
|
|
131251
131357
|
// src/cli/cli-version.ts
|
|
131252
|
-
var CLI_VERSION = "0.9.
|
|
131358
|
+
var CLI_VERSION = "0.9.30";
|
|
131253
131359
|
|
|
131254
131360
|
// src/cli/sporades.ts
|
|
131255
131361
|
var SUPPORTED_TEMPLATES = new Set(CLIENT_TEMPLATES);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.9.
|
|
1
|
+
export declare const CLI_VERSION = "0.9.30";
|
|
2
2
|
//# sourceMappingURL=cli-version.d.ts.map
|
package/dist/cli/cli-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-runtime.d.ts","sourceRoot":"","sources":["../src/database-runtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"database-runtime.d.ts","sourceRoot":"","sources":["../src/database-runtime.ts"],"names":[],"mappings":"AAoKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAyBxC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACvC,KAAK,aAAa,GAAG,WAAW,CAAC;AACjC,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAgGrD,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,WAOvE;AA+DD,wBAAsB,4BAA4B,CAAC,YAAY,EAAE,GAAG,EAAE,SAAS,GAAE,UAAe,EAAE,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAqBlJ;AAED,wBAAsB,8BAA8B,CAAC,YAAY,EAAE,GAAG,EAAE,SAAS,GAAE,UAAe,EAAE,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAS3J;AAkBD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAyBpE;AA2BD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAa1E;AAMD,wBAAgB,sBAAsB,gBAMrC;AAED,wBAAgB,wBAAwB,gBAgBvC;AAED,wBAAgB,sBAAsB,gBASrC;AAGD,wBAAgB,qBAAqB,gBAoCpC;AAED,wBAAgB,uBAAuB,gBAmDtC;AAaD,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAunCpF;AAED,wBAAsB,2BAA2B,CAAC,YAAY,EAAE,QAAQ,EAAE,OAAO,GAAE,WAAgB,wBAyMlG;AAED,wBAAsB,6BAA6B,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,GAAG,CAAC;CAAE,wBA2YzE;AAID,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,WAAW;;eAsG9D,MAAM;;;;;;GAwIpB;AA8MD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,GAAE,GAAG,EAAO,UA2E/D;AAMD,wBAAsB,2BAA2B,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC;CAAE;;;;wBAiF1D,CAAC,kBAAkB,EAAE,WAAW,KAAK,GAAG,YAAW;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE;6BA8BtE,CAAC,OAAO,EAAE,WAAW,KAAK,GAAG;;cAtFlD,MAAM;iBAUH,MAAM;uBAGA,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE;uBAQtB,SAAS,EAAE;uBAGX,MAAM,EAAE;;;GAuG9B;AA4VD,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,MAAa,OAM7F;AAyED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;CAAE,gBAEhF;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;CAAE,gBAE1E;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;CAAE,EAAE,GAAG,EAAE,GAAG,gBAExF;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,YAuE1C;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,GAAG,UAE9C"}
|
package/dist/database-runtime.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { acquirePostgresResourceBootstrapLock, resourceError } from "./resource-runtime.js";
|
|
2
2
|
import { notificationIntentSchemas } from "./notification-intent-runtime.js";
|
|
3
|
+
import { liveQueryTablesTracked, recordLiveQueryStatementRead, recordLiveQueryStatementWrite } from "./live-query-invalidation.js";
|
|
3
4
|
// The Capsule runtime's Database adapters and dialect: the three engines, the seam they answer, the
|
|
4
5
|
// one shared method set every behavioural call goes through, and the app-schema DDL that method set
|
|
5
6
|
// emits. Batch 9 of the migration ADR-0041 records, and the last domain to leave
|
|
@@ -1382,18 +1383,28 @@ export async function createSqliteDatabaseAdapter(databasePath, options = {}) {
|
|
|
1382
1383
|
};
|
|
1383
1384
|
return {
|
|
1384
1385
|
exec(sql) {
|
|
1385
|
-
return run(() => useConnection(() =>
|
|
1386
|
+
return run(() => useConnection(() => {
|
|
1387
|
+
const result = connection.exec(sql);
|
|
1388
|
+
recordLiveQueryStatementWrite(sql);
|
|
1389
|
+
return result;
|
|
1390
|
+
}));
|
|
1386
1391
|
},
|
|
1387
1392
|
prepare(sql) {
|
|
1388
1393
|
return {
|
|
1389
1394
|
all(...params) {
|
|
1395
|
+
recordLiveQueryStatementRead(sql);
|
|
1390
1396
|
return run(() => useConnection(() => connection.prepare(sql).all(...params)));
|
|
1391
1397
|
},
|
|
1392
1398
|
get(...params) {
|
|
1399
|
+
recordLiveQueryStatementRead(sql);
|
|
1393
1400
|
return run(() => useConnection(() => connection.prepare(sql).get(...params)));
|
|
1394
1401
|
},
|
|
1395
1402
|
run(...params) {
|
|
1396
|
-
return run(() => useConnection(() =>
|
|
1403
|
+
return run(() => useConnection(() => {
|
|
1404
|
+
const result = connection.prepare(sql).run(...params);
|
|
1405
|
+
recordLiveQueryStatementWrite(sql, result);
|
|
1406
|
+
return result;
|
|
1407
|
+
}));
|
|
1397
1408
|
},
|
|
1398
1409
|
columns() {
|
|
1399
1410
|
return run(() => useConnection(() => connection.prepare(sql).columns()));
|
|
@@ -1408,6 +1419,9 @@ export async function createSqliteDatabaseAdapter(databasePath, options = {}) {
|
|
|
1408
1419
|
...createSharedDatabaseAdapterMethods(dialect),
|
|
1409
1420
|
...createOperations(connectionGate.runOperation),
|
|
1410
1421
|
engine: "sqlite",
|
|
1422
|
+
// Every statement on the shared and dedicated connections reports its tables, so live
|
|
1423
|
+
// query refreshes can be scoped to the tables a write changed.
|
|
1424
|
+
[liveQueryTablesTracked]: true,
|
|
1411
1425
|
// Outer resources must be able to open one independent durable SQLite
|
|
1412
1426
|
// connection. This runtime-owned marker propagates through transaction
|
|
1413
1427
|
// adapters; callers cannot opt an in-memory or read-only adapter in.
|
|
@@ -1432,8 +1446,24 @@ export async function createSqliteDatabaseAdapter(databasePath, options = {}) {
|
|
|
1432
1446
|
let begun = false;
|
|
1433
1447
|
let commitIssued = false;
|
|
1434
1448
|
const operations = {
|
|
1435
|
-
exec: (sql) =>
|
|
1436
|
-
|
|
1449
|
+
exec: (sql) => {
|
|
1450
|
+
const result = dedicated.exec(sql);
|
|
1451
|
+
recordLiveQueryStatementWrite(sql);
|
|
1452
|
+
return result;
|
|
1453
|
+
},
|
|
1454
|
+
prepare: (sql) => {
|
|
1455
|
+
const statement = dedicated.prepare(sql);
|
|
1456
|
+
return Object.assign(Object.create(statement), {
|
|
1457
|
+
all: (...params) => { recordLiveQueryStatementRead(sql); return statement.all(...params); },
|
|
1458
|
+
get: (...params) => { recordLiveQueryStatementRead(sql); return statement.get(...params); },
|
|
1459
|
+
run: (...params) => {
|
|
1460
|
+
const result = statement.run(...params);
|
|
1461
|
+
recordLiveQueryStatementWrite(sql, result);
|
|
1462
|
+
return result;
|
|
1463
|
+
},
|
|
1464
|
+
columns: () => statement.columns(),
|
|
1465
|
+
});
|
|
1466
|
+
},
|
|
1437
1467
|
};
|
|
1438
1468
|
const transaction = createTransactionScopedAdapter(adapter, operations, adapter, "transaction");
|
|
1439
1469
|
try {
|