toiljs 0.0.124 → 0.0.125
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -10
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +2 -2
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/auth.d.ts +2 -0
- package/build/client/auth.js +7 -0
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/generate.js +1 -1
- package/build/compiler/index.js +2 -0
- package/build/compiler/toil-docs.generated.js +11 -11
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/daemon/index.d.ts +4 -0
- package/build/devserver/daemon/index.js +84 -2
- package/build/devserver/db/database.d.ts +11 -2
- package/build/devserver/db/database.js +227 -22
- package/build/devserver/db/limits.d.ts +6 -0
- package/build/devserver/db/limits.js +118 -0
- package/build/devserver/db/materialization.d.ts +4 -0
- package/build/devserver/db/materialization.js +25 -0
- package/build/devserver/db/types.d.ts +3 -0
- package/build/devserver/db/types.js +1 -0
- package/build/devserver/runtime/host.js +17 -0
- package/build/devserver/runtime/module.d.ts +4 -0
- package/build/devserver/runtime/module.js +47 -9
- package/build/devserver/wasm/envelope-window.d.ts +1 -0
- package/build/devserver/wasm/envelope-window.js +64 -0
- package/build/testing/.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/server/auth/AuthController.ts +265 -61
- package/server/runtime/response.ts +12 -4
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { performance } from 'node:perf_hooks';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
|
+
import { DbFunctionKind, persistDb, parseDerives, derivesForWrites, commitDeriveCheckpoints, setDbCatalog, } from '../db/index.js';
|
|
5
|
+
import { flushMaterializations, queueMaterialization } from '../db/materialization.js';
|
|
6
|
+
import { resetDbLimits } from '../db/limits.js';
|
|
4
7
|
import { parseSurface } from '../wasm/surface.js';
|
|
5
8
|
import { parseDaemonCatalog, } from './catalog.js';
|
|
6
9
|
import { cronMatches, cronNeverFires, nextCronFireMs } from './cron.js';
|
|
@@ -35,8 +38,11 @@ export class DaemonHost {
|
|
|
35
38
|
running = false;
|
|
36
39
|
epochValue = 0n;
|
|
37
40
|
timers = new Map();
|
|
41
|
+
deriveRetryTimer = null;
|
|
38
42
|
nextFire = new Map();
|
|
39
43
|
ticking = new Set();
|
|
44
|
+
derives = [];
|
|
45
|
+
pendingDerives = new Set();
|
|
40
46
|
constructor(coldWasmPath, cfg, nodeMode, log = (s) => process.stdout.write(s)) {
|
|
41
47
|
this.coldWasmPath = coldWasmPath;
|
|
42
48
|
this.cfg = cfg;
|
|
@@ -115,6 +121,11 @@ export class DaemonHost {
|
|
|
115
121
|
return false;
|
|
116
122
|
}
|
|
117
123
|
this.module = new WebAssembly.Module(bytes);
|
|
124
|
+
setDbCatalog(bytes, true);
|
|
125
|
+
this.derives = parseDerives(bytes);
|
|
126
|
+
this.pendingDerives.clear();
|
|
127
|
+
for (const derive of this.derives)
|
|
128
|
+
this.pendingDerives.add(derive.deriveId);
|
|
118
129
|
this.catalog = catalog;
|
|
119
130
|
this.loadedMtimeMs = mtimeMs;
|
|
120
131
|
this.epochValue += 1n;
|
|
@@ -126,6 +137,7 @@ export class DaemonHost {
|
|
|
126
137
|
return;
|
|
127
138
|
const ref = { memory: null };
|
|
128
139
|
this.state = freshDaemonState();
|
|
140
|
+
this.state.db.functionKind = DbFunctionKind.Action;
|
|
129
141
|
const imports = buildDaemonImports(ref, this.state, this);
|
|
130
142
|
const provided = imports;
|
|
131
143
|
const missing = WebAssembly.Module.imports(this.module)
|
|
@@ -139,9 +151,24 @@ export class DaemonHost {
|
|
|
139
151
|
this.instance = new WebAssembly.Instance(this.module, imports);
|
|
140
152
|
this.exports = this.instance.exports;
|
|
141
153
|
ref.memory = this.exports.memory;
|
|
154
|
+
if (this.derives.length > 0 && typeof this.exports.derive_run !== 'function') {
|
|
155
|
+
this.log(pc.red(' ✗ cold materializers require derive_run(i32) -> i64') + '\n');
|
|
156
|
+
this.instance = null;
|
|
157
|
+
this.exports = null;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
142
160
|
try {
|
|
143
|
-
if (typeof this.exports.init === 'function')
|
|
144
|
-
this.exports.init();
|
|
161
|
+
if (typeof this.exports.init === 'function') {
|
|
162
|
+
const initRc = this.exports.init();
|
|
163
|
+
if (initRc !== 0) {
|
|
164
|
+
this.log(pc.red(` ✗ init() returned ${String(initRc)}; daemon not running`) + '\n');
|
|
165
|
+
this.instance = null;
|
|
166
|
+
this.exports = null;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this.state.db.logMessages = 0;
|
|
170
|
+
resetDbLimits(this.state.db);
|
|
171
|
+
}
|
|
145
172
|
const rc = this.exports.daemon_start();
|
|
146
173
|
if (rc !== 0) {
|
|
147
174
|
this.log(pc.red(` ✗ daemon_start() returned ${String(rc)}; daemon not running`) + '\n');
|
|
@@ -156,7 +183,15 @@ export class DaemonHost {
|
|
|
156
183
|
this.exports = null;
|
|
157
184
|
return;
|
|
158
185
|
}
|
|
186
|
+
finally {
|
|
187
|
+
this.materializeWrites();
|
|
188
|
+
}
|
|
159
189
|
this.running = true;
|
|
190
|
+
this.deriveRetryTimer = setInterval(() => {
|
|
191
|
+
if (this.running && this.pendingDerives.size > 0 && this.isLeader())
|
|
192
|
+
this.materializeWrites();
|
|
193
|
+
}, 1000);
|
|
194
|
+
this.deriveRetryTimer.unref?.();
|
|
160
195
|
const limited = this.catalog.tasks.slice(0, this.cfg.maxTasks);
|
|
161
196
|
for (const task of limited)
|
|
162
197
|
this.registerTask(task);
|
|
@@ -165,6 +200,9 @@ export class DaemonHost {
|
|
|
165
200
|
'\n');
|
|
166
201
|
}
|
|
167
202
|
stop() {
|
|
203
|
+
if (this.deriveRetryTimer !== null)
|
|
204
|
+
clearInterval(this.deriveRetryTimer);
|
|
205
|
+
this.deriveRetryTimer = null;
|
|
168
206
|
for (const t of this.timers.values())
|
|
169
207
|
clearTimeout(t);
|
|
170
208
|
this.timers.clear();
|
|
@@ -230,6 +268,10 @@ export class DaemonHost {
|
|
|
230
268
|
if (!this.isLeader())
|
|
231
269
|
return;
|
|
232
270
|
this.ticking.add(task.taskIndex);
|
|
271
|
+
this.state.db.functionKind = DbFunctionKind.Action;
|
|
272
|
+
this.state.db.logMessages = 0;
|
|
273
|
+
resetDbLimits(this.state.db);
|
|
274
|
+
this.state.db.writtenCollections.clear();
|
|
233
275
|
const startedAt = performance.now();
|
|
234
276
|
try {
|
|
235
277
|
const ret = this.exports.scheduled_tick(task.taskIndex);
|
|
@@ -241,12 +283,52 @@ export class DaemonHost {
|
|
|
241
283
|
this.log(pc.red(` ✗ @scheduled ${task.name} trapped: ${String(e)}`) + '\n');
|
|
242
284
|
}
|
|
243
285
|
finally {
|
|
286
|
+
this.materializeWrites();
|
|
244
287
|
const took = performance.now() - startedAt;
|
|
245
288
|
if (took > this.cfg.tickBudgetMs)
|
|
246
289
|
this.log(pc.yellow(` ⏱ @scheduled ${task.name} took ${String(took)}ms (> tickBudgetMs ${String(this.cfg.tickBudgetMs)})`) + '\n');
|
|
247
290
|
this.ticking.delete(task.taskIndex);
|
|
248
291
|
}
|
|
249
292
|
}
|
|
293
|
+
materializeWrites() {
|
|
294
|
+
if (this.derives.length > 0 && this.exports?.derive_run !== undefined) {
|
|
295
|
+
for (const derive of derivesForWrites(this.derives, this.state.db.writtenCollections))
|
|
296
|
+
this.pendingDerives.add(derive.deriveId);
|
|
297
|
+
const db = this.state.db;
|
|
298
|
+
const kind = db.functionKind;
|
|
299
|
+
try {
|
|
300
|
+
db.functionKind = DbFunctionKind.Derive;
|
|
301
|
+
for (const derive of this.derives.filter((d) => this.pendingDerives.has(d.deriveId))) {
|
|
302
|
+
db.deriveId = derive.deriveId;
|
|
303
|
+
db.logMessages = 0;
|
|
304
|
+
resetDbLimits(db);
|
|
305
|
+
db.pendingCheckpoints.clear();
|
|
306
|
+
try {
|
|
307
|
+
const rc = this.exports.derive_run(derive.deriveId);
|
|
308
|
+
if (rc !== 0n)
|
|
309
|
+
throw new Error(`derive_run returned ${String(rc)}`);
|
|
310
|
+
commitDeriveCheckpoints(db);
|
|
311
|
+
this.pendingDerives.delete(derive.deriveId);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
this.log(pc.red(` ✗ daemon derive ${derive.dbName}.${derive.methodName} failed; retry in one second: ${String(error)}`) + '\n');
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
db.functionKind = kind;
|
|
321
|
+
db.pendingCheckpoints.clear();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
for (const collection of this.state.db.writtenCollections)
|
|
326
|
+
queueMaterialization(collection);
|
|
327
|
+
flushMaterializations();
|
|
328
|
+
}
|
|
329
|
+
persistDb();
|
|
330
|
+
this.state.db.writtenCollections.clear();
|
|
331
|
+
}
|
|
250
332
|
close() {
|
|
251
333
|
if (this.running)
|
|
252
334
|
this.stop();
|
|
@@ -23,10 +23,13 @@ export declare class DevDatabase {
|
|
|
23
23
|
private readonly capacity;
|
|
24
24
|
private readonly versions;
|
|
25
25
|
private readonly eventVersions;
|
|
26
|
+
private readonly eventHlcs;
|
|
27
|
+
private readonly erasedEvents;
|
|
28
|
+
private eventClock;
|
|
26
29
|
private readonly memberVersions;
|
|
27
30
|
private catalog;
|
|
28
31
|
private persistPath;
|
|
29
|
-
setCatalog(wasm: Buffer): void;
|
|
32
|
+
setCatalog(wasm: Buffer, merge?: boolean): void;
|
|
30
33
|
private currentSchemaVersion;
|
|
31
34
|
private stampVersion;
|
|
32
35
|
private recordIdemStart;
|
|
@@ -57,8 +60,10 @@ export declare class DevDatabase {
|
|
|
57
60
|
membershipAdd(ref: MemoryRef, db: DbDevState, handle: number, setPtr: number, setLen: number, memberPtr: number, memberLen: number): number;
|
|
58
61
|
membershipRemove(ref: MemoryRef, db: DbDevState, handle: number, setPtr: number, setLen: number, memberPtr: number, memberLen: number): number;
|
|
59
62
|
membershipList(ref: MemoryRef, db: DbDevState, handle: number, setPtr: number, setLen: number, limit: number): number;
|
|
63
|
+
membershipPage(ref: MemoryRef, db: DbDevState, handle: number, setPtr: number, setLen: number, cursorPtr: number, cursorLen: number, limit: number): number;
|
|
60
64
|
viewGet(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number): number;
|
|
61
65
|
viewPublish(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number, valPtr: number, valLen: number): number;
|
|
66
|
+
viewDelete(ref: MemoryRef, db: DbDevState, handle: number, kp: number, kl: number): number;
|
|
62
67
|
counterGet(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number): number;
|
|
63
68
|
private recordWrite;
|
|
64
69
|
counterAdd(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number, delta: number | bigint, idemPtr: number): number;
|
|
@@ -67,7 +72,11 @@ export declare class DevDatabase {
|
|
|
67
72
|
eventsGet(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number, evidPtr: number, evidLen: number): number;
|
|
68
73
|
eventsLast(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number): number;
|
|
69
74
|
private eventResult;
|
|
75
|
+
eventsReference(ref: MemoryRef, db: DbDevState, handle: number, kp: number, kl: number, ip: number, il: number): number;
|
|
76
|
+
eventsRedact(ref: MemoryRef, db: DbDevState, handle: number, kp: number, kl: number, seq: bigint, hlc: bigint): number;
|
|
77
|
+
eventsEntries(ref: MemoryRef, db: DbDevState, handle: number, kp: number, kl: number, limit: number, after: bigint): number;
|
|
70
78
|
compareExchange(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number, expectedPtr: number, expectedLen: number, valuePtr: number, valueLen: number): number;
|
|
79
|
+
compareDelete(ref: MemoryRef, db: DbDevState, handle: number, kp: number, kl: number, ep: number, el: number): number;
|
|
71
80
|
enqueue(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number, valPtr: number, valLen: number, idemPtr: number): number;
|
|
72
81
|
latest(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number, limit: number): number;
|
|
73
82
|
since(ref: MemoryRef, db: DbDevState, handle: number, keyPtr: number, keyLen: number, limit: number): number;
|
|
@@ -83,7 +92,7 @@ export declare class DevDatabase {
|
|
|
83
92
|
setCatalogForTests(entries: Record<string, CatalogSeedEntry>): void;
|
|
84
93
|
}
|
|
85
94
|
export declare const devDb: DevDatabase;
|
|
86
|
-
export declare function setDbCatalog(wasm: Buffer): void;
|
|
95
|
+
export declare function setDbCatalog(wasm: Buffer, merge?: boolean): void;
|
|
87
96
|
export declare function configureDbPersistence(filePath: string): void;
|
|
88
97
|
export declare function persistDb(): void;
|
|
89
98
|
export declare function commitDeriveCheckpoints(db: DbDevState): void;
|
|
@@ -3,7 +3,9 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { DataReader, DataWriter } from 'toiljs/io';
|
|
5
5
|
import { parseCatalog } from './catalog.js';
|
|
6
|
-
import {
|
|
6
|
+
import { queueMaterialization } from './materialization.js';
|
|
7
|
+
import { checkDbResultSize, withDbLimits } from './limits.js';
|
|
8
|
+
import { ABSENT, ALREADY_EXISTS, CODEC_ERR, CollectionFamily, CONFLICT, DbFunctionKind, DEFAULT_FILL_WAIT_MS, INVALID_HANDLE, MAX_KEY, MAX_FILL_WAIT_MS, MAX_NAME, MAX_RESERVATION_TTL_MS, MAX_RESERVATIONS, MAX_VALUE, OP_NOT_ALLOWED_FOR_FAMILY, OP_NOT_ALLOWED_IN_KIND, satI64, SCHEMA_UNAVAILABLE, TOO_SMALL, UNAVAILABLE, isCollectionFamily, } from './types.js';
|
|
7
9
|
function mem(ref) {
|
|
8
10
|
if (!ref.memory)
|
|
9
11
|
throw new Error('data host import called before memory was bound');
|
|
@@ -131,10 +133,14 @@ var DbOp;
|
|
|
131
133
|
DbOp[DbOp["CapacityCancel"] = 24] = "CapacityCancel";
|
|
132
134
|
DbOp[DbOp["ViewGet"] = 25] = "ViewGet";
|
|
133
135
|
DbOp[DbOp["ViewPublish"] = 26] = "ViewPublish";
|
|
134
|
-
DbOp[DbOp["
|
|
135
|
-
DbOp[DbOp["
|
|
136
|
-
DbOp[DbOp["
|
|
137
|
-
DbOp[DbOp["
|
|
136
|
+
DbOp[DbOp["ViewDelete"] = 27] = "ViewDelete";
|
|
137
|
+
DbOp[DbOp["CapacitySetTotal"] = 28] = "CapacitySetTotal";
|
|
138
|
+
DbOp[DbOp["EventsSince"] = 29] = "EventsSince";
|
|
139
|
+
DbOp[DbOp["EventsGet"] = 30] = "EventsGet";
|
|
140
|
+
DbOp[DbOp["EventsLast"] = 31] = "EventsLast";
|
|
141
|
+
DbOp[DbOp["EventsEntries"] = 32] = "EventsEntries";
|
|
142
|
+
DbOp[DbOp["EventsReference"] = 33] = "EventsReference";
|
|
143
|
+
DbOp[DbOp["EventsRedact"] = 34] = "EventsRedact";
|
|
138
144
|
})(DbOp || (DbOp = {}));
|
|
139
145
|
var UpsertResult;
|
|
140
146
|
(function (UpsertResult) {
|
|
@@ -155,10 +161,12 @@ function isReadOp(op) {
|
|
|
155
161
|
op === DbOp.EventsSince ||
|
|
156
162
|
op === DbOp.EventsGet ||
|
|
157
163
|
op === DbOp.EventsLast ||
|
|
164
|
+
op === DbOp.EventsEntries ||
|
|
165
|
+
op === DbOp.EventsReference ||
|
|
158
166
|
op === DbOp.CapacityAvailable);
|
|
159
167
|
}
|
|
160
168
|
function isScanOp(op) {
|
|
161
|
-
return op === DbOp.Latest || op === DbOp.MembershipList || op === DbOp.EventsSince;
|
|
169
|
+
return op === DbOp.Latest || op === DbOp.MembershipList || op === DbOp.EventsSince || op === DbOp.EventsEntries;
|
|
162
170
|
}
|
|
163
171
|
function kindAllows(kind, op) {
|
|
164
172
|
if (kind === DbFunctionKind.Query)
|
|
@@ -169,10 +177,12 @@ function kindAllows(kind, op) {
|
|
|
169
177
|
op === DbOp.Patch ||
|
|
170
178
|
op === DbOp.Upsert ||
|
|
171
179
|
op === DbOp.Delete ||
|
|
180
|
+
op === DbOp.ViewDelete ||
|
|
172
181
|
op === DbOp.GetDelete ||
|
|
173
182
|
op === DbOp.Enqueue ||
|
|
174
183
|
op === DbOp.Append ||
|
|
175
184
|
op === DbOp.AppendOnce ||
|
|
185
|
+
op === DbOp.EventsRedact ||
|
|
176
186
|
op === DbOp.CounterAdd ||
|
|
177
187
|
op === DbOp.MembershipAdd ||
|
|
178
188
|
op === DbOp.MembershipRemove ||
|
|
@@ -183,7 +193,11 @@ function kindAllows(kind, op) {
|
|
|
183
193
|
op === DbOp.CapacityCancel);
|
|
184
194
|
}
|
|
185
195
|
if (kind === DbFunctionKind.Derive)
|
|
186
|
-
return (isReadOp(op) ||
|
|
196
|
+
return (isReadOp(op) ||
|
|
197
|
+
op === DbOp.ViewPublish ||
|
|
198
|
+
op === DbOp.ViewDelete ||
|
|
199
|
+
op === DbOp.Append ||
|
|
200
|
+
op === DbOp.CounterAdd);
|
|
187
201
|
if (kind === DbFunctionKind.Job)
|
|
188
202
|
return true;
|
|
189
203
|
return false;
|
|
@@ -209,11 +223,20 @@ export class DevDatabase {
|
|
|
209
223
|
capacity = new Map();
|
|
210
224
|
versions = new Map();
|
|
211
225
|
eventVersions = new Map();
|
|
226
|
+
eventHlcs = new Map();
|
|
227
|
+
erasedEvents = new Map();
|
|
228
|
+
eventClock = 0n;
|
|
212
229
|
memberVersions = new Map();
|
|
213
230
|
catalog = { kind: 'no-section' };
|
|
214
231
|
persistPath = null;
|
|
215
|
-
setCatalog(wasm) {
|
|
216
|
-
|
|
232
|
+
setCatalog(wasm, merge = false) {
|
|
233
|
+
const incoming = parseCatalog(wasm);
|
|
234
|
+
if (merge && this.catalog.kind === 'present' && incoming.kind === 'present') {
|
|
235
|
+
for (const [name, collection] of incoming.collections)
|
|
236
|
+
this.catalog.collections.set(name, collection);
|
|
237
|
+
}
|
|
238
|
+
else if (!merge || incoming.kind !== 'no-section')
|
|
239
|
+
this.catalog = incoming;
|
|
217
240
|
}
|
|
218
241
|
currentSchemaVersion(coll) {
|
|
219
242
|
if (this.catalog.kind !== 'present')
|
|
@@ -283,6 +306,7 @@ export class DevDatabase {
|
|
|
283
306
|
events: {},
|
|
284
307
|
eventDedup: {},
|
|
285
308
|
eventPositions: {},
|
|
309
|
+
erasedEvents: {},
|
|
286
310
|
deriveCheckpoints: {},
|
|
287
311
|
capacity: {},
|
|
288
312
|
};
|
|
@@ -311,10 +335,12 @@ export class DevDatabase {
|
|
|
311
335
|
snap.counterIdem[k] = v.toString();
|
|
312
336
|
for (const [k, log] of this.events) {
|
|
313
337
|
const ver = this.eventVersions.get(k) ?? [];
|
|
314
|
-
snap.events[k] = log.map((b, i) => ({ v: b.toString('base64'), sv: ver[i] ?? 0 }));
|
|
338
|
+
snap.events[k] = log.map((b, i) => ({ v: b.toString('base64'), sv: ver[i] ?? 0, hlc: (this.eventHlcs.get(k)?.[i] ?? BigInt(i + 1)).toString() }));
|
|
315
339
|
}
|
|
316
340
|
for (const [k, positions] of this.eventPositions)
|
|
317
341
|
snap.eventPositions[k] = [...positions];
|
|
342
|
+
for (const [k, erased] of this.erasedEvents)
|
|
343
|
+
snap.erasedEvents[k] = [...erased];
|
|
318
344
|
for (const [k, s] of this.eventDedup)
|
|
319
345
|
snap.eventDedup[k] = [...s];
|
|
320
346
|
for (const [k, v] of this.deriveCheckpoints)
|
|
@@ -383,6 +409,21 @@ export class DevDatabase {
|
|
|
383
409
|
for (const [k, log] of Object.entries(snap.events ?? {})) {
|
|
384
410
|
this.events.set(k, log.map((e) => Buffer.from(e.v, 'base64')));
|
|
385
411
|
this.eventVersions.set(k, log.map((e) => e.sv));
|
|
412
|
+
const hlcs = log.map((e, i) => BigInt(e.hlc ?? String(i + 1)));
|
|
413
|
+
this.eventHlcs.set(k, hlcs);
|
|
414
|
+
for (const hlc of hlcs)
|
|
415
|
+
if (hlc > this.eventClock)
|
|
416
|
+
this.eventClock = hlc;
|
|
417
|
+
}
|
|
418
|
+
for (const [k, values] of Object.entries(snap.erasedEvents ?? {})) {
|
|
419
|
+
const erased = new Set(values);
|
|
420
|
+
this.erasedEvents.set(k, erased);
|
|
421
|
+
const log = this.events.get(k) ?? [];
|
|
422
|
+
const hlcs = this.eventHlcs.get(k) ?? [];
|
|
423
|
+
for (let i = 0; i < log.length; i++) {
|
|
424
|
+
if (erased.has(String(i + 1) + ':' + String(hlcs[i] ?? BigInt(i + 1))))
|
|
425
|
+
log[i] = Buffer.alloc(0);
|
|
426
|
+
}
|
|
386
427
|
}
|
|
387
428
|
for (const [k, positions] of Object.entries(snap.eventPositions ?? {}))
|
|
388
429
|
this.eventPositions.set(k, new Map(positions));
|
|
@@ -416,6 +457,9 @@ export class DevDatabase {
|
|
|
416
457
|
this.counters.clear();
|
|
417
458
|
this.counterIdem.clear();
|
|
418
459
|
this.eventVersions.clear();
|
|
460
|
+
this.eventHlcs.clear();
|
|
461
|
+
this.erasedEvents.clear();
|
|
462
|
+
this.eventClock = 0n;
|
|
419
463
|
this.events.clear();
|
|
420
464
|
this.eventDedup.clear();
|
|
421
465
|
this.eventPositions.clear();
|
|
@@ -505,15 +549,18 @@ export class DevDatabase {
|
|
|
505
549
|
? 64
|
|
506
550
|
: 1024;
|
|
507
551
|
if (count > maxKeys)
|
|
508
|
-
|
|
552
|
+
throw new Error(`data.get_many failed: TDL020 batch-key budget exceeded; collection=${coll.name} kind=${db.functionKind} batch_keys=${String(count)} max_batch_keys=${String(maxKeys)}. Split the keys into smaller batches.`);
|
|
509
553
|
const w = new DataWriter();
|
|
510
554
|
w.writeU32(count);
|
|
555
|
+
let resultBytes = 4;
|
|
511
556
|
for (let i = 0; i < count; i++) {
|
|
512
557
|
const key = r.readBytes();
|
|
513
558
|
if (key.length > MAX_KEY)
|
|
514
559
|
throw new Error('data: key too long');
|
|
515
560
|
const sk = storeKey(coll.name, Buffer.from(key));
|
|
516
561
|
const v = table.get(sk);
|
|
562
|
+
resultBytes += v === undefined ? 1 : 9 + v.length;
|
|
563
|
+
checkDbResultSize(db, resultBytes);
|
|
517
564
|
if (v === undefined) {
|
|
518
565
|
w.writeU8(0);
|
|
519
566
|
}
|
|
@@ -816,6 +863,32 @@ export class DevDatabase {
|
|
|
816
863
|
db.lastResult = Buffer.from(w.toBytes());
|
|
817
864
|
return db.lastResult.length;
|
|
818
865
|
}
|
|
866
|
+
membershipPage(ref, db, handle, setPtr, setLen, cursorPtr, cursorLen, limit) {
|
|
867
|
+
if (cursorLen < -1 || limit < 1 || limit > 0xffff)
|
|
868
|
+
throw new Error('data.membership_page: invalid cursor length or limit');
|
|
869
|
+
checkValueSize(cursorLen, 'cursor');
|
|
870
|
+
const coll = collForOp(db, handle, DbOp.MembershipList, CollectionFamily.Membership);
|
|
871
|
+
if (typeof coll === 'number')
|
|
872
|
+
return coll;
|
|
873
|
+
if (limit > 1024)
|
|
874
|
+
throw new Error('data.membership_page: TDL020 scan limit exceeds 1024');
|
|
875
|
+
const cursor = cursorLen === -1 ? null : readCopy(ref, cursorPtr, cursorLen);
|
|
876
|
+
const sk = storeKey(coll.name, readKey(ref, setPtr, setLen));
|
|
877
|
+
const members = Array.from(this.members.get(sk)?.values() ?? [])
|
|
878
|
+
.sort(Buffer.compare)
|
|
879
|
+
.filter((m) => cursor === null || Buffer.compare(m, cursor) > 0)
|
|
880
|
+
.slice(0, limit);
|
|
881
|
+
const versions = this.memberVersions.get(sk);
|
|
882
|
+
const w = new DataWriter().writeU32(members.length);
|
|
883
|
+
for (const member of members)
|
|
884
|
+
w.writeU32(versions?.get(member.toString('latin1')) ?? 0).writeBytes(member);
|
|
885
|
+
const next = members.length === limit ? members[members.length - 1] : null;
|
|
886
|
+
w.writeU8(next === null ? 0 : 1);
|
|
887
|
+
if (next !== null)
|
|
888
|
+
w.writeBytes(next);
|
|
889
|
+
db.lastResult = Buffer.from(w.toBytes());
|
|
890
|
+
return db.lastResult.length;
|
|
891
|
+
}
|
|
819
892
|
viewGet(ref, db, handle, keyPtr, keyLen) {
|
|
820
893
|
const coll = collForOp(db, handle, DbOp.ViewGet, CollectionFamily.View);
|
|
821
894
|
if (typeof coll === 'number')
|
|
@@ -838,6 +911,15 @@ export class DevDatabase {
|
|
|
838
911
|
this.stampVersion(coll, sk);
|
|
839
912
|
return 0;
|
|
840
913
|
}
|
|
914
|
+
viewDelete(ref, db, handle, kp, kl) {
|
|
915
|
+
const coll = collForOp(db, handle, DbOp.ViewDelete, CollectionFamily.View);
|
|
916
|
+
if (typeof coll === 'number')
|
|
917
|
+
return coll;
|
|
918
|
+
const key = storeKey(coll.name, readKey(ref, kp, kl));
|
|
919
|
+
this.views.delete(key);
|
|
920
|
+
this.versions.delete(key);
|
|
921
|
+
return 0;
|
|
922
|
+
}
|
|
841
923
|
counterGet(ref, db, handle, keyPtr, keyLen) {
|
|
842
924
|
const coll = collForOp(db, handle, DbOp.CounterGet, CollectionFamily.Counter);
|
|
843
925
|
if (typeof coll === 'number')
|
|
@@ -852,6 +934,8 @@ export class DevDatabase {
|
|
|
852
934
|
if (db.functionKind === DbFunctionKind.Derive)
|
|
853
935
|
return;
|
|
854
936
|
db.writtenCollections.add(coll.name);
|
|
937
|
+
if (db.functionKind === DbFunctionKind.Job)
|
|
938
|
+
queueMaterialization(coll.name);
|
|
855
939
|
}
|
|
856
940
|
counterAdd(ref, db, handle, keyPtr, keyLen, delta, idemPtr) {
|
|
857
941
|
const coll = collForOp(db, handle, DbOp.CounterAdd, CollectionFamily.Counter);
|
|
@@ -904,6 +988,11 @@ export class DevDatabase {
|
|
|
904
988
|
versions.push(sv);
|
|
905
989
|
this.eventVersions.set(sk, versions);
|
|
906
990
|
}
|
|
991
|
+
const hlcs = this.eventHlcs.get(sk) ?? [];
|
|
992
|
+
const now = BigInt(Date.now()) << 16n;
|
|
993
|
+
this.eventClock = now > this.eventClock ? now : this.eventClock + 1n;
|
|
994
|
+
hlcs.push(this.eventClock);
|
|
995
|
+
this.eventHlcs.set(sk, hlcs);
|
|
907
996
|
this.recordWrite(db, coll);
|
|
908
997
|
return 0;
|
|
909
998
|
}
|
|
@@ -942,6 +1031,11 @@ export class DevDatabase {
|
|
|
942
1031
|
}
|
|
943
1032
|
positions.set(evid, position);
|
|
944
1033
|
seen.add(evid);
|
|
1034
|
+
const hlcs = this.eventHlcs.get(sk) ?? [];
|
|
1035
|
+
const now = BigInt(Date.now()) << 16n;
|
|
1036
|
+
this.eventClock = now > this.eventClock ? now : this.eventClock + 1n;
|
|
1037
|
+
hlcs.push(this.eventClock);
|
|
1038
|
+
this.eventHlcs.set(sk, hlcs);
|
|
945
1039
|
this.recordWrite(db, coll);
|
|
946
1040
|
return 1;
|
|
947
1041
|
}
|
|
@@ -963,10 +1057,15 @@ export class DevDatabase {
|
|
|
963
1057
|
if (typeof coll === 'number')
|
|
964
1058
|
return coll;
|
|
965
1059
|
const sk = storeKey(coll.name, readKey(ref, keyPtr, keyLen));
|
|
966
|
-
|
|
1060
|
+
const log = this.events.get(sk) ?? [];
|
|
1061
|
+
let position = log.length - 1;
|
|
1062
|
+
while (position >= 0 && log[position].length === 0)
|
|
1063
|
+
position--;
|
|
1064
|
+
return this.eventResult(db, sk, position);
|
|
967
1065
|
}
|
|
968
1066
|
eventResult(db, sk, position) {
|
|
969
|
-
const
|
|
1067
|
+
const stored = position === undefined ? undefined : this.events.get(sk)?.[position];
|
|
1068
|
+
const value = stored?.length === 0 ? undefined : stored;
|
|
970
1069
|
db.lastResult = value === undefined ? null : Buffer.from(value);
|
|
971
1070
|
db.lastResultVersion =
|
|
972
1071
|
value === undefined || position === undefined
|
|
@@ -974,6 +1073,79 @@ export class DevDatabase {
|
|
|
974
1073
|
: (this.eventVersions.get(sk)?.[position] ?? 0);
|
|
975
1074
|
return value === undefined ? ABSENT : value.length;
|
|
976
1075
|
}
|
|
1076
|
+
eventsReference(ref, db, handle, kp, kl, ip, il) {
|
|
1077
|
+
const coll = collForOp(db, handle, DbOp.EventsReference, CollectionFamily.Events);
|
|
1078
|
+
if (typeof coll === 'number')
|
|
1079
|
+
return coll;
|
|
1080
|
+
if (il < 0 || il > MAX_KEY)
|
|
1081
|
+
throw new Error('Invalid event ID length');
|
|
1082
|
+
const sk = storeKey(coll.name, readKey(ref, kp, kl));
|
|
1083
|
+
const id = readCopy(ref, ip, il).toString('latin1');
|
|
1084
|
+
const position = this.eventPositions.get(sk)?.get(id);
|
|
1085
|
+
if (position === undefined)
|
|
1086
|
+
return this.eventDedup.get(sk)?.has(id) ? UNAVAILABLE : ABSENT;
|
|
1087
|
+
const bytes = Buffer.alloc(16);
|
|
1088
|
+
bytes.writeBigUInt64LE(BigInt(position + 1));
|
|
1089
|
+
bytes.writeBigUInt64LE(this.eventHlcs.get(sk)?.[position] ?? BigInt(position + 1), 8);
|
|
1090
|
+
db.lastResult = bytes;
|
|
1091
|
+
db.lastResultVersion = -1;
|
|
1092
|
+
return bytes.length;
|
|
1093
|
+
}
|
|
1094
|
+
eventsRedact(ref, db, handle, kp, kl, seq, hlc) {
|
|
1095
|
+
const coll = collForOp(db, handle, DbOp.EventsRedact, CollectionFamily.Events);
|
|
1096
|
+
if (typeof coll === 'number')
|
|
1097
|
+
return coll;
|
|
1098
|
+
if (seq <= 0n || hlc < 0n)
|
|
1099
|
+
throw new Error('Invalid event reference');
|
|
1100
|
+
const sk = storeKey(coll.name, readKey(ref, kp, kl));
|
|
1101
|
+
let erased = this.erasedEvents.get(sk);
|
|
1102
|
+
if (erased === undefined) {
|
|
1103
|
+
erased = new Set();
|
|
1104
|
+
this.erasedEvents.set(sk, erased);
|
|
1105
|
+
}
|
|
1106
|
+
erased.add(seq.toString() + ':' + hlc.toString());
|
|
1107
|
+
const position = Number(seq - 1n);
|
|
1108
|
+
const log = this.events.get(sk);
|
|
1109
|
+
if (Number.isSafeInteger(position) && position >= 0 && log !== undefined && position < log.length &&
|
|
1110
|
+
(this.eventHlcs.get(sk)?.[position] ?? seq) === hlc)
|
|
1111
|
+
log[position] = Buffer.alloc(0);
|
|
1112
|
+
for (const key of this.deriveCheckpoints.keys())
|
|
1113
|
+
if (key.endsWith('\0' + sk))
|
|
1114
|
+
this.deriveCheckpoints.delete(key);
|
|
1115
|
+
for (const key of db.pendingCheckpoints.keys())
|
|
1116
|
+
if (key.endsWith('\0' + sk))
|
|
1117
|
+
db.pendingCheckpoints.delete(key);
|
|
1118
|
+
this.recordWrite(db, coll);
|
|
1119
|
+
return 1;
|
|
1120
|
+
}
|
|
1121
|
+
eventsEntries(ref, db, handle, kp, kl, limit, after) {
|
|
1122
|
+
const coll = collForOp(db, handle, DbOp.EventsEntries, CollectionFamily.Events);
|
|
1123
|
+
if (typeof coll === 'number')
|
|
1124
|
+
return coll;
|
|
1125
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 1024 || after < 0n || after > BigInt(Number.MAX_SAFE_INTEGER))
|
|
1126
|
+
throw new Error('Invalid event page cursor or limit');
|
|
1127
|
+
const sk = storeKey(coll.name, readKey(ref, kp, kl));
|
|
1128
|
+
const log = this.events.get(sk) ?? [];
|
|
1129
|
+
const positions = [];
|
|
1130
|
+
let cursor = Number(after);
|
|
1131
|
+
while (cursor < log.length && positions.length < limit) {
|
|
1132
|
+
if (log[cursor].length > 0)
|
|
1133
|
+
positions.push(cursor);
|
|
1134
|
+
cursor++;
|
|
1135
|
+
}
|
|
1136
|
+
const writer = new DataWriter().writeU32(positions.length);
|
|
1137
|
+
for (const position of positions)
|
|
1138
|
+
writer.writeU64(BigInt(position + 1))
|
|
1139
|
+
.writeU64(this.eventHlcs.get(sk)?.[position] ?? BigInt(position + 1))
|
|
1140
|
+
.writeU32(this.eventVersions.get(sk)?.[position] ?? 0).writeBytes(log[position]);
|
|
1141
|
+
const hasMore = positions.length === limit;
|
|
1142
|
+
writer.writeU8(hasMore ? 1 : 0);
|
|
1143
|
+
if (hasMore)
|
|
1144
|
+
writer.writeU64(BigInt(cursor));
|
|
1145
|
+
db.lastResult = Buffer.from(writer.toBytes());
|
|
1146
|
+
db.lastResultVersion = -1;
|
|
1147
|
+
return db.lastResult.length;
|
|
1148
|
+
}
|
|
977
1149
|
compareExchange(ref, db, handle, keyPtr, keyLen, expectedPtr, expectedLen, valuePtr, valueLen) {
|
|
978
1150
|
const coll = collForOp(db, handle, DbOp.Patch, CollectionFamily.Record);
|
|
979
1151
|
if (typeof coll === 'number')
|
|
@@ -997,6 +1169,26 @@ export class DevDatabase {
|
|
|
997
1169
|
this.recordWrite(db, coll);
|
|
998
1170
|
return 1;
|
|
999
1171
|
}
|
|
1172
|
+
compareDelete(ref, db, handle, kp, kl, ep, el) {
|
|
1173
|
+
const coll = collForOp(db, handle, DbOp.Delete, CollectionFamily.Record);
|
|
1174
|
+
if (typeof coll === 'number')
|
|
1175
|
+
return coll;
|
|
1176
|
+
checkKeyValueSize(kl, el, 'expected value');
|
|
1177
|
+
checkValueSize(el, 'expected value');
|
|
1178
|
+
if (el < 0)
|
|
1179
|
+
throw new Error('data: conditional delete requires an expected value');
|
|
1180
|
+
const expected = readCopy(ref, ep, el);
|
|
1181
|
+
if (this.uniqueValuesOf(coll, expected).length > 0)
|
|
1182
|
+
throw new Error('Conditional delete does not support @unique fields');
|
|
1183
|
+
const sk = storeKey(coll.name, readKey(ref, kp, kl));
|
|
1184
|
+
const prior = this.store.get(sk);
|
|
1185
|
+
if (prior === undefined || !prior.equals(expected))
|
|
1186
|
+
return 0;
|
|
1187
|
+
this.store.delete(sk);
|
|
1188
|
+
this.versions.delete(sk);
|
|
1189
|
+
this.recordWrite(db, coll);
|
|
1190
|
+
return 1;
|
|
1191
|
+
}
|
|
1000
1192
|
enqueue(ref, db, handle, keyPtr, keyLen, valPtr, valLen, idemPtr) {
|
|
1001
1193
|
const coll = collForOp(db, handle, DbOp.Enqueue, CollectionFamily.Record);
|
|
1002
1194
|
if (typeof coll === 'number')
|
|
@@ -1028,9 +1220,9 @@ export class DevDatabase {
|
|
|
1028
1220
|
const log = this.events.get(sk) ?? [];
|
|
1029
1221
|
const vers = this.eventVersions.get(sk) ?? [];
|
|
1030
1222
|
const n = Math.max(0, Math.min(limit, 0xffff));
|
|
1031
|
-
const
|
|
1032
|
-
const newest =
|
|
1033
|
-
const newestVers =
|
|
1223
|
+
const positions = n === 0 ? [] : log.map((_, i) => i).filter(i => log[i].length > 0).slice(-n).reverse();
|
|
1224
|
+
const newest = positions.map(i => log[i]);
|
|
1225
|
+
const newestVers = positions.map(i => vers[i]);
|
|
1034
1226
|
const w = new DataWriter();
|
|
1035
1227
|
w.writeU32(newest.length);
|
|
1036
1228
|
for (let i = 0; i < newest.length; i++) {
|
|
@@ -1049,9 +1241,15 @@ export class DevDatabase {
|
|
|
1049
1241
|
const vers = this.eventVersions.get(sk) ?? [];
|
|
1050
1242
|
const cursor = db.pendingCheckpoints.get(ckKey) ?? this.deriveCheckpoints.get(ckKey) ?? 0;
|
|
1051
1243
|
const n = Math.max(0, Math.min(limit, 0xffff));
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1054
|
-
|
|
1244
|
+
const positions = [];
|
|
1245
|
+
let end = cursor;
|
|
1246
|
+
while (end < log.length && positions.length < n) {
|
|
1247
|
+
if (log[end].length > 0)
|
|
1248
|
+
positions.push(end);
|
|
1249
|
+
end++;
|
|
1250
|
+
}
|
|
1251
|
+
const batch = positions.map(i => log[i]);
|
|
1252
|
+
const batchVers = positions.map(i => vers[i]);
|
|
1055
1253
|
db.pendingCheckpoints.set(ckKey, end);
|
|
1056
1254
|
const w = new DataWriter();
|
|
1057
1255
|
w.writeU32(batch.length);
|
|
@@ -1204,8 +1402,8 @@ export class DevDatabase {
|
|
|
1204
1402
|
}
|
|
1205
1403
|
}
|
|
1206
1404
|
export const devDb = new DevDatabase();
|
|
1207
|
-
export function setDbCatalog(wasm) {
|
|
1208
|
-
devDb.setCatalog(wasm);
|
|
1405
|
+
export function setDbCatalog(wasm, merge = false) {
|
|
1406
|
+
devDb.setCatalog(wasm, merge);
|
|
1209
1407
|
}
|
|
1210
1408
|
export function configureDbPersistence(filePath) {
|
|
1211
1409
|
devDb.configurePersistence(filePath);
|
|
@@ -1217,7 +1415,7 @@ export function commitDeriveCheckpoints(db) {
|
|
|
1217
1415
|
devDb.commitDeriveCheckpoints(db);
|
|
1218
1416
|
}
|
|
1219
1417
|
export function buildDatabaseImports(ref, db) {
|
|
1220
|
-
|
|
1418
|
+
const imports = {
|
|
1221
1419
|
'data.resolve_collection': (namePtr, nameLen, outHandlePtr) => devDb.resolveCollection(ref, db, namePtr, nameLen, outHandlePtr),
|
|
1222
1420
|
'data.get': (handle, keyPtr, keyLen) => devDb.get(ref, db, handle, keyPtr, keyLen),
|
|
1223
1421
|
'data.get_many': (handle, keysPtr, keysLen) => devDb.getMany(ref, db, handle, keysPtr, keysLen),
|
|
@@ -1233,7 +1431,9 @@ export function buildDatabaseImports(ref, db) {
|
|
|
1233
1431
|
'data.membership_contains': (handle, setPtr, setLen, memberPtr, memberLen) => devDb.membershipContains(ref, db, handle, setPtr, setLen, memberPtr, memberLen),
|
|
1234
1432
|
'data.membership_add': (handle, setPtr, setLen, memberPtr, memberLen, _idemPtr) => devDb.membershipAdd(ref, db, handle, setPtr, setLen, memberPtr, memberLen),
|
|
1235
1433
|
'data.membership_remove': (handle, setPtr, setLen, memberPtr, memberLen, _idemPtr) => devDb.membershipRemove(ref, db, handle, setPtr, setLen, memberPtr, memberLen),
|
|
1434
|
+
'data.membership_page': (h, sp, sl, cp, cl, limit) => devDb.membershipPage(ref, db, h, sp, sl, cp, cl, limit),
|
|
1236
1435
|
'data.membership_list': (handle, setPtr, setLen, limit) => devDb.membershipList(ref, db, handle, setPtr, setLen, limit),
|
|
1436
|
+
'data.view_delete': (h, kp, kl, _idem) => devDb.viewDelete(ref, db, h, kp, kl),
|
|
1237
1437
|
'data.view_get': (handle, keyPtr, keyLen) => devDb.viewGet(ref, db, handle, keyPtr, keyLen),
|
|
1238
1438
|
'data.view_publish': (handle, keyPtr, keyLen, valPtr, valLen, _idemPtr) => devDb.viewPublish(ref, db, handle, keyPtr, keyLen, valPtr, valLen),
|
|
1239
1439
|
'data.counter_get': (handle, keyPtr, keyLen) => devDb.counterGet(ref, db, handle, keyPtr, keyLen),
|
|
@@ -1241,9 +1441,13 @@ export function buildDatabaseImports(ref, db) {
|
|
|
1241
1441
|
'data.append': (handle, keyPtr, keyLen, evPtr, evLen, idemPtr) => devDb.append(ref, db, handle, keyPtr, keyLen, evPtr, evLen, idemPtr),
|
|
1242
1442
|
'data.append_once': (handle, keyPtr, keyLen, evidPtr, evidLen, evPtr, evLen) => devDb.appendOnce(ref, db, handle, keyPtr, keyLen, evidPtr, evidLen, evPtr, evLen),
|
|
1243
1443
|
'data.get_current': (handle, kp, kl) => devDb.get(ref, db, handle, kp, kl),
|
|
1444
|
+
'data.compare_delete': (h, kp, kl, ep, el) => devDb.compareDelete(ref, db, h, kp, kl, ep, el),
|
|
1244
1445
|
'data.compare_exchange': (handle, kp, kl, ep, el, vp, vl) => devDb.compareExchange(ref, db, handle, kp, kl, ep, el, vp, vl),
|
|
1245
1446
|
'data.enqueue': (handle, keyPtr, keyLen, valPtr, valLen, idemPtr) => devDb.enqueue(ref, db, handle, keyPtr, keyLen, valPtr, valLen, idemPtr),
|
|
1246
1447
|
'data.latest': (handle, keyPtr, keyLen, limit) => devDb.latest(ref, db, handle, keyPtr, keyLen, limit),
|
|
1448
|
+
'data.events_reference': (h, kp, kl, ip, il) => devDb.eventsReference(ref, db, h, kp, kl, ip, il),
|
|
1449
|
+
'data.events_redact': (h, kp, kl, seq, hlc) => devDb.eventsRedact(ref, db, h, kp, kl, BigInt(seq), BigInt(hlc)),
|
|
1450
|
+
'data.events_entries': (h, kp, kl, limit, after) => devDb.eventsEntries(ref, db, h, kp, kl, limit, BigInt(after)),
|
|
1247
1451
|
'data.events_get': (handle, keyPtr, keyLen, evidPtr, evidLen) => devDb.eventsGet(ref, db, handle, keyPtr, keyLen, evidPtr, evidLen),
|
|
1248
1452
|
'data.events_last': (handle, keyPtr, keyLen) => devDb.eventsLast(ref, db, handle, keyPtr, keyLen),
|
|
1249
1453
|
'data.events_since': (handle, keyPtr, keyLen, limit) => devDb.since(ref, db, handle, keyPtr, keyLen, limit),
|
|
@@ -1256,6 +1460,7 @@ export function buildDatabaseImports(ref, db) {
|
|
|
1256
1460
|
'data.result_schema_version': () => devDb.resultSchemaVersion(db),
|
|
1257
1461
|
'data.write_allowed': () => (kindAllows(db.functionKind, DbOp.Patch) ? 1 : 0),
|
|
1258
1462
|
};
|
|
1463
|
+
return withDbLimits(imports, db);
|
|
1259
1464
|
}
|
|
1260
1465
|
export function __resetDbForTests() {
|
|
1261
1466
|
devDb.resetForTests();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type DbDevState } from './types.js';
|
|
2
|
+
type Imports = Record<string, (...args: number[]) => number | bigint>;
|
|
3
|
+
export declare function resetDbLimits(db: DbDevState): void;
|
|
4
|
+
export declare function checkDbResultSize(db: DbDevState, bytes: number): void;
|
|
5
|
+
export declare function withDbLimits(imports: Imports, db: DbDevState): Imports;
|
|
6
|
+
export {};
|