toilscript 0.1.63 → 0.1.65
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/cli.generated.d.ts +8 -6
- package/dist/cli.js +243 -6
- package/dist/cli.js.map +2 -2
- package/dist/importmap.json +2 -2
- package/dist/toilscript.generated.d.ts +8 -6
- package/dist/toilscript.js +43 -43
- package/dist/toilscript.js.map +3 -3
- package/dist/web.js +3 -3
- package/package.json +4 -2
- package/std/assembly/bindings/toildb.ts +40 -0
- package/std/assembly/log.ts +14 -0
- package/std/assembly/toildb.ts +186 -3
- package/std/assembly/toilscript.d.ts +41 -0
package/dist/web.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var ASSEMBLYSCRIPT_VERSION = "0.1.
|
|
1
|
+
var ASSEMBLYSCRIPT_VERSION = "0.1.65";
|
|
2
2
|
var ASSEMBLYSCRIPT_IMPORTMAP = {
|
|
3
3
|
"imports": {
|
|
4
|
-
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
5
|
-
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
4
|
+
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.65/dist/toilscript.js",
|
|
5
|
+
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.65/dist/cli.js",
|
|
6
6
|
"binaryen": "https://cdn.jsdelivr.net/npm/binaryen@130.0.0/index.js",
|
|
7
7
|
"long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
|
|
8
8
|
}
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"toilscript",
|
|
9
9
|
"wasm"
|
|
10
10
|
],
|
|
11
|
-
"version": "0.1.
|
|
11
|
+
"version": "0.1.65",
|
|
12
12
|
"author": "Daniel Wirtz <dcode+assemblyscript@dcode.io>",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"homepage": "https://github.com/dacely-cloud/toilscript",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"prepublishOnly": "node scripts/build",
|
|
82
82
|
"watch": "node scripts/build --watch",
|
|
83
83
|
"coverage": "npx c8 -- npm test",
|
|
84
|
-
"test": "npm run test:parser && npm run test:compiler -- --parallel && npm run test:browser && npm run test:toilconfig && npm run test:transform && npm run test:cli && npm run test:json && npm run test:data && npm run test:dbresolve && npm run test:dbstatic && npm run test:dbtypes && npm run test:routekinds && npm run test:derive && npm run test:streams && npm run test:daemon && npm run test:dberror && npm run test:events && npm run test:uniquebatch",
|
|
84
|
+
"test": "npm run test:parser && npm run test:compiler -- --parallel && npm run test:browser && npm run test:toilconfig && npm run test:transform && npm run test:cli && npm run test:json && npm run test:data && npm run test:dbresolve && npm run test:dbstatic && npm run test:dbtypes && npm run test:routekinds && npm run test:derive && npm run test:streams && npm run test:daemon && npm run test:dberror && npm run test:events && npm run test:uniquebatch && npm run test:membership && npm run test:event-redaction",
|
|
85
85
|
"test:parser": "node --enable-source-maps tests/parser",
|
|
86
86
|
"test:compiler": "node --enable-source-maps --no-warnings tests/compiler",
|
|
87
87
|
"test:browser": "node --enable-source-maps tests/browser",
|
|
@@ -98,6 +98,8 @@
|
|
|
98
98
|
"test:routekinds": "node tests/routekinds/run.mjs",
|
|
99
99
|
"test:derive": "node tests/derive/run.mjs",
|
|
100
100
|
"test:streams": "node tests/streams/run.mjs && node tests/streams/catalog.mjs && node tests/streams/codegen.mjs",
|
|
101
|
+
"test:membership": "node tests/membership/run.mjs",
|
|
102
|
+
"test:event-redaction": "node tests/event-redaction/run.mjs",
|
|
101
103
|
"test:daemon": "node tests/daemon/run.mjs",
|
|
102
104
|
"test:uniquebatch": "node tests/uniquebatch/run.mjs",
|
|
103
105
|
"test:events": "node tests/events/run.mjs",
|
|
@@ -71,6 +71,24 @@ export namespace toildbHost {
|
|
|
71
71
|
idemPtr: usize
|
|
72
72
|
): i32;
|
|
73
73
|
|
|
74
|
+
// Caller-observed byte CAS: expectedLen=-1 expects absence. 1 applied, 0 mismatch.
|
|
75
|
+
// @ts-ignore: decorator
|
|
76
|
+
@external("env", "data.compare_exchange")
|
|
77
|
+
export declare function compareExchange(
|
|
78
|
+
handle: u32, keyPtr: usize, keyLen: i32,
|
|
79
|
+
expectedPtr: usize, expectedLen: i32, valPtr: usize, valLen: i32
|
|
80
|
+
): i32;
|
|
81
|
+
|
|
82
|
+
// @ts-ignore: decorator
|
|
83
|
+
@external("env", "data.compare_delete")
|
|
84
|
+
export declare function compareDelete(
|
|
85
|
+
handle: u32, keyPtr: usize, keyLen: i32, expectedPtr: usize, expectedLen: i32
|
|
86
|
+
): i32;
|
|
87
|
+
|
|
88
|
+
// @ts-ignore: decorator
|
|
89
|
+
@external("env", "data.get_current")
|
|
90
|
+
export declare function getCurrent(handle: u32, keyPtr: usize, keyLen: i32): i32;
|
|
91
|
+
|
|
74
92
|
// record.upsert -> 0 created | 1 updated | 2 @unique-conflict (not written) |
|
|
75
93
|
// negative error. A single blind last-writer-wins put: creates the row if
|
|
76
94
|
// absent, overwrites it if present, in ONE op (no create-then-patch round trip).
|
|
@@ -141,6 +159,11 @@ export namespace toildbHost {
|
|
|
141
159
|
idemPtr: usize
|
|
142
160
|
): i32;
|
|
143
161
|
|
|
162
|
+
// Remove a view key (Action/Derive/Job), idempotently.
|
|
163
|
+
// @ts-ignore: decorator
|
|
164
|
+
@external("env", "data.view_delete")
|
|
165
|
+
export declare function viewDelete(handle: u32, keyPtr: usize, keyLen: i32, idemPtr: usize): i32;
|
|
166
|
+
|
|
144
167
|
// membership.contains -> 1 present | 0 absent | negative error.
|
|
145
168
|
// @ts-ignore: decorator
|
|
146
169
|
@external("env", "data.membership_contains")
|
|
@@ -168,6 +191,13 @@ export namespace toildbHost {
|
|
|
168
191
|
@external("env", "data.membership_list")
|
|
169
192
|
export declare function membershipList(handle: u32, setPtr: usize, setLen: i32, limit: i32): i32;
|
|
170
193
|
|
|
194
|
+
// Ordered membership page: list frame + optional length-prefixed raw cursor.
|
|
195
|
+
// @ts-ignore: decorator
|
|
196
|
+
@external("env", "data.membership_page")
|
|
197
|
+
export declare function membershipPage(
|
|
198
|
+
handle: u32, setPtr: usize, setLen: i32, cursorPtr: usize, cursorLen: i32, limit: i32
|
|
199
|
+
): i32;
|
|
200
|
+
|
|
171
201
|
// capacity.set_total(total: i64) -> 0 ok | negative error (job/derive only).
|
|
172
202
|
// @ts-ignore: decorator
|
|
173
203
|
@external("env", "data.capacity_set_total")
|
|
@@ -232,6 +262,16 @@ export namespace toildbHost {
|
|
|
232
262
|
|
|
233
263
|
// Point reads: stashed value length | -2 absent | typed negative error.
|
|
234
264
|
// result_schema_version exposes the stored event's schema version.
|
|
265
|
+
// @ts-ignore: decorator
|
|
266
|
+
@external("env", "data.events_reference")
|
|
267
|
+
export declare function eventsReference(handle: u32, keyPtr: usize, keyLen: i32, idPtr: usize, idLen: i32): i32;
|
|
268
|
+
// @ts-ignore: decorator
|
|
269
|
+
@external("env", "data.events_redact")
|
|
270
|
+
export declare function eventsRedact(handle: u32, keyPtr: usize, keyLen: i32, sequence: u64, hlc: u64): i32;
|
|
271
|
+
// @ts-ignore: decorator
|
|
272
|
+
@external("env", "data.events_entries")
|
|
273
|
+
export declare function eventsEntries(handle: u32, keyPtr: usize, keyLen: i32, limit: i32, after: u64): i32;
|
|
274
|
+
|
|
235
275
|
// @ts-ignore: decorator
|
|
236
276
|
@external("env", "data.events_get")
|
|
237
277
|
export declare function eventsGet(handle: u32, keyPtr: usize, keyLen: i32, evidPtr: usize, evidLen: i32): i32;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Structured server logging, available to request, stream, and daemon code.
|
|
2
|
+
@external("env", "server_log")
|
|
3
|
+
declare function __serverLog(level: i32, ptr: usize, len: i32): i32;
|
|
4
|
+
|
|
5
|
+
@global
|
|
6
|
+
export class Log {
|
|
7
|
+
private static write(level: i32, message: string): bool {
|
|
8
|
+
const bytes = Uint8Array.wrap(String.UTF8.encode(message));
|
|
9
|
+
return __serverLog(level, bytes.dataStart, bytes.byteLength) == 1;
|
|
10
|
+
}
|
|
11
|
+
static info(message: string): bool { return Log.write(0, message); }
|
|
12
|
+
static warn(message: string): bool { return Log.write(1, message); }
|
|
13
|
+
static error(message: string): bool { return Log.write(2, message); }
|
|
14
|
+
}
|
package/std/assembly/toildb.ts
CHANGED
|
@@ -233,6 +233,14 @@ export enum UpsertResult {
|
|
|
233
233
|
Conflict = 2,
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/// An observed value plus its original storage bytes (before decoding/migration).
|
|
237
|
+
/// Keep the snapshot unchanged while constructing a replacement value.
|
|
238
|
+
@global
|
|
239
|
+
export class DocumentSnapshot<V> {
|
|
240
|
+
constructor(public value: V, public encoded: Uint8Array) {}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
@global
|
|
236
244
|
export class Documents<K, V> {
|
|
237
245
|
private __handle: u32;
|
|
238
246
|
|
|
@@ -259,6 +267,47 @@ export class Documents<K, V> {
|
|
|
259
267
|
return v;
|
|
260
268
|
}
|
|
261
269
|
|
|
270
|
+
/// Read without rewrite-on-read. Retains the exact bytes for conditional writes,
|
|
271
|
+
/// including records whose codec or schema migrated during decoding.
|
|
272
|
+
getSnapshot(key: K): DocumentSnapshot<V> | null {
|
|
273
|
+
const kb = key.encode();
|
|
274
|
+
const status = toildbHost.get(this.__handle, kb.dataStart, kb.byteLength);
|
|
275
|
+
if (status < 0) { __toildbMiss(status); return null; }
|
|
276
|
+
const bytes = __toildbTake(status);
|
|
277
|
+
const value = instantiate<V>();
|
|
278
|
+
value.decodeInto(bytes);
|
|
279
|
+
return new DocumentSnapshot<V>(value, bytes);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/// Owner-authoritative snapshot, bypassing replica and request caches.
|
|
283
|
+
getCurrentSnapshot(key: K): DocumentSnapshot<V> | null {
|
|
284
|
+
const kb = key.encode();
|
|
285
|
+
const status = toildbHost.getCurrent(this.__handle, kb.dataStart, kb.byteLength);
|
|
286
|
+
if (status < 0) { __toildbMiss(status); return null; }
|
|
287
|
+
const bytes = __toildbTake(status);
|
|
288
|
+
const value = instantiate<V>();
|
|
289
|
+
value.decodeInto(bytes);
|
|
290
|
+
return new DocumentSnapshot<V>(value, bytes);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
compareExchangeSnapshot(key: K, expected: DocumentSnapshot<V> | null, value: V): bool {
|
|
294
|
+
const kb = key.encode();
|
|
295
|
+
const vb = value.encode();
|
|
296
|
+
const eb = expected == null ? new Uint8Array(0) : expected.encoded;
|
|
297
|
+
const status = toildbHost.compareExchange(this.__handle, kb.dataStart, kb.byteLength,
|
|
298
|
+
eb.dataStart, expected == null ? -1 : eb.byteLength, vb.dataStart, vb.byteLength);
|
|
299
|
+
if (status < 0) abort("ToilDB compareExchange failed: status " + status.toString(), "toildb", 0, 0);
|
|
300
|
+
return status == 1;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
compareDeleteSnapshot(key: K, expected: DocumentSnapshot<V>): bool {
|
|
304
|
+
const kb = key.encode();
|
|
305
|
+
const eb = expected.encoded;
|
|
306
|
+
const status = toildbHost.compareDelete(this.__handle, kb.dataStart, kb.byteLength, eb.dataStart, eb.byteLength);
|
|
307
|
+
if (status < 0) abort("ToilDB compareDelete failed: status " + status.toString(), "toildb", 0, 0);
|
|
308
|
+
return status == 1;
|
|
309
|
+
}
|
|
310
|
+
|
|
262
311
|
/// Like `get`, but traps if the record is absent.
|
|
263
312
|
require(key: K): V {
|
|
264
313
|
const v = this.get(key);
|
|
@@ -276,7 +325,13 @@ export class Documents<K, V> {
|
|
|
276
325
|
}
|
|
277
326
|
const blob = w.toBytes();
|
|
278
327
|
const status = toildbHost.getMany(this.__handle, blob.dataStart, blob.byteLength);
|
|
279
|
-
if (status < 0)
|
|
328
|
+
if (status < 0) {
|
|
329
|
+
__toildbMiss(status);
|
|
330
|
+
abort("ToilDB getMany failed: status " + status.toString() +
|
|
331
|
+
"; DbError=" + (<i32>Db.lastError()).toString() +
|
|
332
|
+
"; keys=" + keys.length.toString() +
|
|
333
|
+
". Query accepts 32 keys, Action 64, background 1024; split larger batches.", "toildb", 0, 0);
|
|
334
|
+
}
|
|
280
335
|
const out = __toildbTake(status);
|
|
281
336
|
const results = new Array<V | null>();
|
|
282
337
|
let off: i32 = 0;
|
|
@@ -354,6 +409,21 @@ export class Documents<K, V> {
|
|
|
354
409
|
return v;
|
|
355
410
|
}
|
|
356
411
|
|
|
412
|
+
/// Replace only if the stored bytes equal `expected`; null expects absence.
|
|
413
|
+
/// Returns false on a mismatch. Routed to the owner; @unique values are rejected.
|
|
414
|
+
compareExchange(key: K, expected: V | null, value: V): bool {
|
|
415
|
+
const kb = key.encode();
|
|
416
|
+
const vb = value.encode();
|
|
417
|
+
const eb = expected == null ? new Uint8Array(0) : expected.encode();
|
|
418
|
+
const status = toildbHost.compareExchange(
|
|
419
|
+
this.__handle, kb.dataStart, kb.byteLength,
|
|
420
|
+
eb.dataStart, expected == null ? -1 : eb.byteLength,
|
|
421
|
+
vb.dataStart, vb.byteLength
|
|
422
|
+
);
|
|
423
|
+
if (status < 0) abort("ToilDB compareExchange failed: status " + status.toString(), "toildb", 0, 0);
|
|
424
|
+
return status == 1;
|
|
425
|
+
}
|
|
426
|
+
|
|
357
427
|
/// Submit a replacement of an EXISTING record: true when accepted, false on
|
|
358
428
|
/// absence or rejection. The ABI carries no caller-observed version or value,
|
|
359
429
|
/// so this cannot protect a read-modify-write based on an earlier application read.
|
|
@@ -363,10 +433,16 @@ export class Documents<K, V> {
|
|
|
363
433
|
return toildbHost.enqueue(this.__handle, kb.dataStart, kb.byteLength, vb.dataStart, vb.byteLength, 0) == 0;
|
|
364
434
|
}
|
|
365
435
|
|
|
436
|
+
/// Delete only when the stored bytes still equal the caller's expectation.
|
|
437
|
+
compareDelete(key: K, expected: V): bool {
|
|
438
|
+
return this.compareDeleteSnapshot(key, new DocumentSnapshot<V>(expected, expected.encode()));
|
|
439
|
+
}
|
|
440
|
+
|
|
366
441
|
/// Delete the record (idempotent).
|
|
367
442
|
delete(key: K): void {
|
|
368
443
|
const kb = key.encode();
|
|
369
|
-
toildbHost.del(this.__handle, kb.dataStart, kb.byteLength, 0);
|
|
444
|
+
const status = toildbHost.del(this.__handle, kb.dataStart, kb.byteLength, 0);
|
|
445
|
+
if (status < 0) abort("ToilDB delete failed: status " + status.toString(), "toildb", 0, 0);
|
|
370
446
|
}
|
|
371
447
|
|
|
372
448
|
/// Atomic fetch-and-delete (consume-once); returns the prior value or `null`.
|
|
@@ -409,14 +485,22 @@ export class View<K, V> {
|
|
|
409
485
|
return v!;
|
|
410
486
|
}
|
|
411
487
|
|
|
488
|
+
/// Remove a projection key. Delete its source personal data before rebuilding.
|
|
489
|
+
delete(key: K): void {
|
|
490
|
+
const kb = key.encode();
|
|
491
|
+
const status = toildbHost.viewDelete(this.__handle, kb.dataStart, kb.byteLength, 0);
|
|
492
|
+
if (status < 0) abort("ToilDB view.delete failed: status " + status.toString(), "toildb", 0, 0);
|
|
493
|
+
}
|
|
494
|
+
|
|
412
495
|
/// Publish (overwrite) the view for `key`. Derive/job only; the host assigns
|
|
413
496
|
/// the version so a later publish always supersedes an earlier one.
|
|
414
497
|
publish(key: K, value: V): void {
|
|
415
498
|
const kb = key.encode();
|
|
416
499
|
const vb = value.encode();
|
|
417
|
-
toildbHost.viewPublish(
|
|
500
|
+
const status = toildbHost.viewPublish(
|
|
418
501
|
this.__handle, kb.dataStart, kb.byteLength, vb.dataStart, vb.byteLength, 0
|
|
419
502
|
);
|
|
503
|
+
if (status < 0) abort("ToilDB view.publish failed: status " + status.toString(), "toildb", 0, 0);
|
|
420
504
|
}
|
|
421
505
|
}
|
|
422
506
|
|
|
@@ -483,6 +567,17 @@ export class Unique<K, V> {
|
|
|
483
567
|
}
|
|
484
568
|
}
|
|
485
569
|
|
|
570
|
+
/// A page in encoded-member order; pass cursor to listPage to continue.
|
|
571
|
+
@global
|
|
572
|
+
export class MembershipPage<M> {
|
|
573
|
+
members: M[];
|
|
574
|
+
cursor: Uint8Array | null;
|
|
575
|
+
constructor(members: M[], cursor: Uint8Array | null) {
|
|
576
|
+
this.members = members;
|
|
577
|
+
this.cursor = cursor;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
486
581
|
/// An unordered set (spec 7.3): followers, tags, ACLs, room members. `M` is the
|
|
487
582
|
/// `@data` member type, `K` the `@data` set-key type.
|
|
488
583
|
@global
|
|
@@ -520,6 +615,36 @@ export class Membership<K, M> {
|
|
|
520
615
|
);
|
|
521
616
|
}
|
|
522
617
|
|
|
618
|
+
/// Read members strictly after an opaque cursor. A full page may require an
|
|
619
|
+
/// empty final page to establish the end. Pagination is not a snapshot.
|
|
620
|
+
listPage(key: K, limit: i32, cursor: Uint8Array | null = null): MembershipPage<M> {
|
|
621
|
+
const kb = key.encode();
|
|
622
|
+
const cb = cursor == null ? new Uint8Array(0) : cursor;
|
|
623
|
+
const status = toildbHost.membershipPage(
|
|
624
|
+
this.__handle, kb.dataStart, kb.byteLength, cb.dataStart,
|
|
625
|
+
cursor == null ? -1 : cb.byteLength, limit
|
|
626
|
+
);
|
|
627
|
+
if (status < 0) abort("ToilDB membership.listPage failed: status " + status.toString(), "toildb", 0, 0);
|
|
628
|
+
const blob = __toildbTake(status);
|
|
629
|
+
const out = new Array<M>();
|
|
630
|
+
let off: i32 = 4;
|
|
631
|
+
const count = load<u32>(blob.dataStart);
|
|
632
|
+
for (let i: u32 = 0; i < count; i++) {
|
|
633
|
+
const version = <i64>load<u32>(blob.dataStart + off); off += 4;
|
|
634
|
+
const len = <i32>load<u32>(blob.dataStart + off); off += 4;
|
|
635
|
+
const member = instantiate<M>();
|
|
636
|
+
member.decodeIntoVersioned(blob.subarray(off, off + len), version);
|
|
637
|
+
out.push(member); off += len;
|
|
638
|
+
}
|
|
639
|
+
const hasCursor = load<u8>(blob.dataStart + off); off++;
|
|
640
|
+
let next: Uint8Array | null = null;
|
|
641
|
+
if (hasCursor != 0) {
|
|
642
|
+
const len = <i32>load<u32>(blob.dataStart + off); off += 4;
|
|
643
|
+
next = blob.slice(off, off + len);
|
|
644
|
+
}
|
|
645
|
+
return new MembershipPage<M>(out, next);
|
|
646
|
+
}
|
|
647
|
+
|
|
523
648
|
/// Up to `limit` members of the set. Decodes each framed member into an `M`.
|
|
524
649
|
list(key: K, limit: i32): M[] {
|
|
525
650
|
const kb = key.encode();
|
|
@@ -944,6 +1069,21 @@ export class Analytics {
|
|
|
944
1069
|
/// An append-only event log (spec 7.5): activity feeds, audit trails, the
|
|
945
1070
|
/// fact stream a `@derive` consumes. `V` is the `@data` event type, `K` the
|
|
946
1071
|
/// `@data` stream-key type.
|
|
1072
|
+
@global
|
|
1073
|
+
export class EventRef {
|
|
1074
|
+
constructor(public sequence: u64 = 0, public hlc: u64 = 0) {}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
@global
|
|
1078
|
+
export class EventEntry<V> {
|
|
1079
|
+
constructor(public reference: EventRef, public value: V) {}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
@global
|
|
1083
|
+
export class EventPage<V> {
|
|
1084
|
+
constructor(public entries: Array<EventEntry<V>>, public cursor: u64, public hasMore: bool) {}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
947
1087
|
@global
|
|
948
1088
|
export class Events<K, V> {
|
|
949
1089
|
private __handle: u32;
|
|
@@ -952,6 +1092,49 @@ export class Events<K, V> {
|
|
|
952
1092
|
this.__handle = handle;
|
|
953
1093
|
}
|
|
954
1094
|
|
|
1095
|
+
/// Read an appendOnce reference, including during pending cleanup. No payload is returned.
|
|
1096
|
+
reference(key: K, eventId: string): EventRef | null {
|
|
1097
|
+
const kb = key.encode();
|
|
1098
|
+
const id = Uint8Array.wrap(String.UTF8.encode(eventId));
|
|
1099
|
+
const status = toildbHost.eventsReference(this.__handle, kb.dataStart, kb.byteLength, id.dataStart, id.byteLength);
|
|
1100
|
+
if (status < 0) { __toildbMiss(status); return null; }
|
|
1101
|
+
const reader = new DataReader(__toildbTake(status));
|
|
1102
|
+
const reference = new EventRef(reader.readU64(), reader.readU64());
|
|
1103
|
+
if (!reader.ok) abort("Invalid event reference", "toildb", 0, 0);
|
|
1104
|
+
return reference;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/// Erase this entry and one bounded page of recovery copies. Repeat in later
|
|
1108
|
+
/// background calls until true. The payload is hidden from the first successful call.
|
|
1109
|
+
redact(key: K, reference: EventRef): bool {
|
|
1110
|
+
const kb = key.encode();
|
|
1111
|
+
const status = toildbHost.eventsRedact(this.__handle, kb.dataStart, kb.byteLength, reference.sequence, reference.hlc);
|
|
1112
|
+
if (status < 0) abort("ToilDB events.redact failed: status " + status.toString(), "toildb", 0, 0);
|
|
1113
|
+
return status == 1;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/// Bounded, oldest-first page with references for ordinary and appendOnce events.
|
|
1117
|
+
/// Keep the opaque cursor and use hasMore even if an erased page is empty.
|
|
1118
|
+
entries(key: K, limit: i32 = 128, after: u64 = 0): EventPage<V> {
|
|
1119
|
+
const kb = key.encode();
|
|
1120
|
+
const status = toildbHost.eventsEntries(this.__handle, kb.dataStart, kb.byteLength, limit, after);
|
|
1121
|
+
if (status < 0) abort("ToilDB events.entries failed: status " + status.toString(), "toildb", 0, 0);
|
|
1122
|
+
const reader = new DataReader(__toildbTake(status));
|
|
1123
|
+
const count = reader.readU32();
|
|
1124
|
+
const entries = new Array<EventEntry<V>>();
|
|
1125
|
+
for (let i: u32 = 0; i < count; i++) {
|
|
1126
|
+
const reference = new EventRef(reader.readU64(), reader.readU64());
|
|
1127
|
+
const version = <i64>reader.readU32();
|
|
1128
|
+
const bytes = reader.readBytes();
|
|
1129
|
+
const value = instantiate<V>(); value.decodeIntoVersioned(bytes, version);
|
|
1130
|
+
entries.push(new EventEntry<V>(reference, value));
|
|
1131
|
+
}
|
|
1132
|
+
const hasMore = reader.readU8() != 0;
|
|
1133
|
+
const cursor: u64 = hasMore ? reader.readU64() : 0;
|
|
1134
|
+
if (!reader.ok) abort("Invalid event page", "toildb", 0, 0);
|
|
1135
|
+
return new EventPage<V>(entries, cursor, hasMore);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
955
1138
|
/// Read an event by its appendOnce eventId, or null if absent. Bounded request-path read.
|
|
956
1139
|
get(key: K, eventId: string): V | null {
|
|
957
1140
|
const kb = key.encode();
|
|
@@ -240,6 +240,12 @@ declare const enum UpsertResult {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
/** A mutable keyed-entity collection (spec 7.1): user profiles, items, sessions. */
|
|
243
|
+
declare class DocumentSnapshot<V> {
|
|
244
|
+
value: V;
|
|
245
|
+
encoded: Uint8Array;
|
|
246
|
+
constructor(value: V, encoded: Uint8Array);
|
|
247
|
+
}
|
|
248
|
+
|
|
243
249
|
declare class Documents<K, V> {
|
|
244
250
|
get(key: K): V | null;
|
|
245
251
|
require(key: K): V;
|
|
@@ -247,6 +253,14 @@ declare class Documents<K, V> {
|
|
|
247
253
|
getMany(keys: K[]): (V | null)[];
|
|
248
254
|
create(key: K, value: V): bool;
|
|
249
255
|
patch(key: K, value: V): V;
|
|
256
|
+
/** Replace if stored bytes equal expected; null expects absence. Owner-cell only,
|
|
257
|
+
* rejects @unique values. False on mismatch; other failures abort. */
|
|
258
|
+
compareExchange(key: K, expected: V | null, value: V): bool;
|
|
259
|
+
compareDelete(key: K, expected: V): bool;
|
|
260
|
+
getSnapshot(key: K): DocumentSnapshot<V> | null;
|
|
261
|
+
getCurrentSnapshot(key: K): DocumentSnapshot<V> | null;
|
|
262
|
+
compareExchangeSnapshot(key: K, expected: DocumentSnapshot<V> | null, value: V): bool;
|
|
263
|
+
compareDeleteSnapshot(key: K, expected: DocumentSnapshot<V>): bool;
|
|
250
264
|
/** Create-or-overwrite in one op (blind last-writer-wins); replaces the two-op
|
|
251
265
|
* `if (!create(k,v)) patch(k,v)` idiom. Right for writing a whole value; wrong
|
|
252
266
|
* for read-modify-write of accumulating state (a balance/count), where a
|
|
@@ -265,6 +279,7 @@ declare class Documents<K, V> {
|
|
|
265
279
|
declare class View<K, V> {
|
|
266
280
|
get(key: K): V | null;
|
|
267
281
|
require(key: K): V;
|
|
282
|
+
delete(key: K): void;
|
|
268
283
|
publish(key: K, value: V): void;
|
|
269
284
|
}
|
|
270
285
|
|
|
@@ -284,12 +299,20 @@ declare class Unique<K, V> {
|
|
|
284
299
|
release(key: K, value: V): void;
|
|
285
300
|
}
|
|
286
301
|
|
|
302
|
+
/** A page in encoded-member byte order, with an opaque continuation cursor. */
|
|
303
|
+
declare class MembershipPage<M> {
|
|
304
|
+
members: M[];
|
|
305
|
+
cursor: Uint8Array | null;
|
|
306
|
+
constructor(members: M[], cursor: Uint8Array | null);
|
|
307
|
+
}
|
|
308
|
+
|
|
287
309
|
/** An unordered set (spec 7.3): followers, tags, ACLs, room members. */
|
|
288
310
|
declare class Membership<K, M> {
|
|
289
311
|
contains(key: K, member: M): bool;
|
|
290
312
|
add(key: K, member: M): void;
|
|
291
313
|
remove(key: K, member: M): void;
|
|
292
314
|
list(key: K, limit: i32): M[];
|
|
315
|
+
listPage(key: K, limit: i32, cursor?: Uint8Array | null): MembershipPage<M>;
|
|
293
316
|
}
|
|
294
317
|
|
|
295
318
|
/** A finite, strongly-consistent resource via escrow (spec 7.7): stock, seats. */
|
|
@@ -308,7 +331,17 @@ declare class Counter<K> {
|
|
|
308
331
|
}
|
|
309
332
|
|
|
310
333
|
/** An append-only event log (spec 7.5): activity feeds, audit trails. */
|
|
334
|
+
declare class EventRef {
|
|
335
|
+
sequence: u64;
|
|
336
|
+
hlc: u64;
|
|
337
|
+
constructor(sequence?: u64, hlc?: u64);
|
|
338
|
+
}
|
|
339
|
+
declare class EventEntry<V> { reference: EventRef; value: V; constructor(reference: EventRef, value: V); }
|
|
340
|
+
declare class EventPage<V> { entries: EventEntry<V>[]; cursor: u64; hasMore: bool; constructor(entries: EventEntry<V>[], cursor: u64, hasMore: bool); }
|
|
311
341
|
declare class Events<K, V> {
|
|
342
|
+
reference(key: K, eventId: string): EventRef | null;
|
|
343
|
+
redact(key: K, reference: EventRef): bool;
|
|
344
|
+
entries(key: K, limit?: i32, after?: u64): EventPage<V>;
|
|
312
345
|
/** Read an event by its appendOnce eventId, or null if absent. */
|
|
313
346
|
get(key: K, eventId: string): V | null;
|
|
314
347
|
/** Read the newest appended event, or null for an empty stream. */
|
|
@@ -872,3 +905,11 @@ declare class JSON {
|
|
|
872
905
|
/** Serialize this value tree to a JSON string. */
|
|
873
906
|
toString(): string;
|
|
874
907
|
}
|
|
908
|
+
|
|
909
|
+
/** Server logging. At most 4096 UTF-8 bytes/message, 32 messages per call.
|
|
910
|
+
* Returns false when the message allowance is exhausted. */
|
|
911
|
+
declare class Log {
|
|
912
|
+
static info(message: string): bool;
|
|
913
|
+
static warn(message: string): bool;
|
|
914
|
+
static error(message: string): bool;
|
|
915
|
+
}
|