toilscript 0.1.26 → 0.1.28

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/web.js CHANGED
@@ -1,8 +1,8 @@
1
- var ASSEMBLYSCRIPT_VERSION = "0.1.26";
1
+ var ASSEMBLYSCRIPT_VERSION = "0.1.28";
2
2
  var ASSEMBLYSCRIPT_IMPORTMAP = {
3
3
  "imports": {
4
- "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.26/dist/toilscript.js",
5
- "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.26/dist/cli.js",
4
+ "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.28/dist/toilscript.js",
5
+ "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.28/dist/cli.js",
6
6
  "binaryen": "https://cdn.jsdelivr.net/npm/binaryen@129.0.0-nightly.20260428/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.26",
11
+ "version": "0.1.28",
12
12
  "author": "Daniel Wirtz <dcode+assemblyscript@dcode.io>",
13
13
  "license": "Apache-2.0",
14
14
  "homepage": "https://github.com/dacely-cloud/toilscript",
@@ -0,0 +1,169 @@
1
+ // Host-import declarations for the ToilDB data API. The production edge
2
+ // (toil-backend `src/wasm/host/import_functions/db`) and the toiljs dev server
3
+ // both provide these under the `env` namespace. All byte regions are passed as
4
+ // a (pointer, length) pair into guest linear memory.
5
+ //
6
+ // Collections are resolved by name ONCE at module init (`resolveCollection`),
7
+ // which returns an opaque numeric handle; request-time ops pass the handle.
8
+ //
9
+ // Return convention (see toildb/ABI.md):
10
+ // >= 0 success: a length (a value stashed for `takeResult`), a count, a
11
+ // boolean (0/1), a tag, or 0 (ok).
12
+ // -1 output buffer too small (retry `takeResult` with a bigger buffer).
13
+ // -2 absent (a normal null / not-found).
14
+ // <= -1000 a typed failure; the diagnostic is TDL(|v| - 1000).
15
+ //
16
+ // Variable-length results (a fetched value, a patched record, a claim owner)
17
+ // use the two-step pull: the op returns the stashed length, then the guest
18
+ // allocates a buffer and calls `takeResult` to copy the bytes out.
19
+ //
20
+ // `idemPtr` is 0 for none, otherwise a pointer to a 16-byte idempotency key.
21
+ //
22
+ // This is the guest half of the ABI contract; the byte framing of keys/values
23
+ // is the `@data` binary codec (`DataWriter`/`DataReader`).
24
+
25
+ export namespace toildbHost {
26
+ // Resolve a collection by its "<db>/<collection>" name; writes the u32 handle
27
+ // to outHandlePtr. Returns 0 on success or a negative TDL code.
28
+ // @ts-ignore: decorator
29
+ @external("env", "data.resolve_collection")
30
+ export declare function resolveCollection(namePtr: usize, nameLen: i32, outHandlePtr: usize): i32;
31
+
32
+ // record.get -> value length (stashed) | -2 absent | negative error.
33
+ // @ts-ignore: decorator
34
+ @external("env", "data.get")
35
+ export declare function get(handle: u32, keyPtr: usize, keyLen: i32): i32;
36
+
37
+ // record bounded multi-get. Input at keysPtr: u32 count + per key (u32 len +
38
+ // bytes). Result (stashed): u32 count + per item u8 present (+ u32 len + bytes
39
+ // when present), in request order. Returns the stashed length | negative error.
40
+ // @ts-ignore: decorator
41
+ @external("env", "data.get_many")
42
+ export declare function getMany(handle: u32, keysPtr: usize, keysLen: i32): i32;
43
+
44
+ // record.exists -> 1 | 0 | negative error.
45
+ // @ts-ignore: decorator
46
+ @external("env", "data.exists")
47
+ export declare function exists(handle: u32, keyPtr: usize, keyLen: i32): i32;
48
+
49
+ // record.create -> 0 ok | AlreadyExists/typed code.
50
+ // @ts-ignore: decorator
51
+ @external("env", "data.create")
52
+ export declare function create(
53
+ handle: u32,
54
+ keyPtr: usize,
55
+ keyLen: i32,
56
+ valPtr: usize,
57
+ valLen: i32,
58
+ idemPtr: usize
59
+ ): i32;
60
+
61
+ // record.patch -> length of the new record (stashed) | negative error.
62
+ // @ts-ignore: decorator
63
+ @external("env", "data.patch")
64
+ export declare function patch(
65
+ handle: u32,
66
+ keyPtr: usize,
67
+ keyLen: i32,
68
+ patchPtr: usize,
69
+ patchLen: i32,
70
+ idemPtr: usize
71
+ ): i32;
72
+
73
+ // record.delete -> 0 ok | negative error.
74
+ // @ts-ignore: decorator
75
+ @external("env", "data.delete")
76
+ export declare function del(handle: u32, keyPtr: usize, keyLen: i32, idemPtr: usize): i32;
77
+
78
+ // record consume-once fetch-and-delete -> prior value length (stashed) | -2.
79
+ // @ts-ignore: decorator
80
+ @external("env", "data.get_delete")
81
+ export declare function getDelete(handle: u32, keyPtr: usize, keyLen: i32, idemPtr: usize): i32;
82
+
83
+ // unique.lookup -> owner value length (stashed) | -2 absent.
84
+ // @ts-ignore: decorator
85
+ @external("env", "data.unique_lookup")
86
+ export declare function uniqueLookup(handle: u32, keyPtr: usize, keyLen: i32): i32;
87
+
88
+ // unique.claim -> 0 Claimed | 1 AlreadyClaimed (owner stashed) | 2 owned-by-caller | neg.
89
+ // @ts-ignore: decorator
90
+ @external("env", "data.unique_claim")
91
+ export declare function uniqueClaim(
92
+ handle: u32,
93
+ keyPtr: usize,
94
+ keyLen: i32,
95
+ valPtr: usize,
96
+ valLen: i32,
97
+ idemPtr: usize
98
+ ): i32;
99
+
100
+ // unique.release -> 0 ok | neg (Conflict if not the owner).
101
+ // @ts-ignore: decorator
102
+ @external("env", "data.unique_release")
103
+ export declare function uniqueRelease(
104
+ handle: u32,
105
+ keyPtr: usize,
106
+ keyLen: i32,
107
+ valPtr: usize,
108
+ valLen: i32,
109
+ idemPtr: usize
110
+ ): i32;
111
+
112
+ // view.get -> view value length (stashed) | -2 absent | negative error.
113
+ // @ts-ignore: decorator
114
+ @external("env", "data.view_get")
115
+ export declare function viewGet(handle: u32, keyPtr: usize, keyLen: i32): i32;
116
+
117
+ // view.publish -> 0 ok | negative error (derive/job only; the host gate enforces).
118
+ // @ts-ignore: decorator
119
+ @external("env", "data.view_publish")
120
+ export declare function viewPublish(
121
+ handle: u32,
122
+ keyPtr: usize,
123
+ keyLen: i32,
124
+ valPtr: usize,
125
+ valLen: i32,
126
+ idemPtr: usize
127
+ ): i32;
128
+
129
+ // counter.get -> 8 (the i64 sum stashed as 8 LE bytes) | negative error.
130
+ // @ts-ignore: decorator
131
+ @external("env", "data.counter_get")
132
+ export declare function counterGet(handle: u32, keyPtr: usize, keyLen: i32): i32;
133
+
134
+ // counter.add(delta: i64) -> 0 ok | negative error.
135
+ // @ts-ignore: decorator
136
+ @external("env", "data.counter_add")
137
+ export declare function counterAdd(
138
+ handle: u32,
139
+ keyPtr: usize,
140
+ keyLen: i32,
141
+ delta: i64,
142
+ idemPtr: usize
143
+ ): i32;
144
+
145
+ // events.append -> 0 ok | negative error.
146
+ // @ts-ignore: decorator
147
+ @external("env", "data.append")
148
+ export declare function append(
149
+ handle: u32,
150
+ keyPtr: usize,
151
+ keyLen: i32,
152
+ evPtr: usize,
153
+ evLen: i32,
154
+ idemPtr: usize
155
+ ): i32;
156
+
157
+ // events.latest(limit) -> framed-list length (stashed) | negative error.
158
+ // The blob is `u32 count` then per event `u32 len + bytes`, newest first.
159
+ // @ts-ignore: decorator
160
+ @external("env", "data.latest")
161
+ export declare function latest(handle: u32, keyPtr: usize, keyLen: i32, limit: i32): i32;
162
+
163
+ // Copy the last stashed variable-length result into outPtr (outLen must equal
164
+ // the length the producing op returned). Returns bytes written, or -1 if the
165
+ // buffer is too small.
166
+ // @ts-ignore: decorator
167
+ @external("env", "data.take_result")
168
+ export declare function takeResult(outPtr: usize, outLen: i32): i32;
169
+ }
@@ -7,12 +7,11 @@ import { webcrypto } from "bindings/webcrypto";
7
7
  import { CryptoKey } from "crypto/key";
8
8
  import {
9
9
  AlgorithmParams,
10
- algId,
11
- formatId,
12
10
  cryptoError,
13
- FMT_JWK,
11
+ FMT_RAW,
14
12
  FMT_PKCS8,
15
13
  FMT_SPKI,
14
+ FMT_JWK,
16
15
  ALG_AES_GCM,
17
16
  ALG_AES_CBC,
18
17
  ALG_AES_CTR,
@@ -43,26 +42,24 @@ function isSymmetricAlg(alg: i32): bool {
43
42
  }
44
43
 
45
44
  export class SubtleCrypto {
46
- digest(algorithm: string, data: Uint8Array): Uint8Array {
47
- let id = algId(algorithm);
48
- if (id == 0) throw new Error("Unrecognized algorithm: " + algorithm);
49
- return drain(webcrypto.digest(id, data.dataStart, data.byteLength));
45
+ // `algorithm` is a hash-id selector: an `ALG_SHA_*` const (NOT a magic string).
46
+ digest(algorithm: i32, data: Uint8Array): Uint8Array {
47
+ return drain(webcrypto.digest(algorithm, data.dataStart, data.byteLength));
50
48
  }
51
49
 
50
+ // `format` is an `FMT_*` selector const (FMT_RAW / FMT_PKCS8 / FMT_SPKI).
52
51
  importKey(
53
- format: string,
52
+ format: i32,
54
53
  keyData: Uint8Array,
55
54
  algorithm: AlgorithmParams,
56
55
  extractable: bool,
57
56
  usages: i32
58
57
  ): CryptoKey {
59
- let fmt = formatId(format);
60
- if (fmt < 0) throw new Error("Unknown key format: " + format);
61
- if (fmt == FMT_JWK) throw new Error("jwk key format is not supported");
58
+ if (format == FMT_JWK) throw new Error("jwk key format is not supported");
62
59
  let p = algorithm.pack();
63
60
  let alg = load<i32>(p.dataStart); // first packed field is the alg id
64
61
  let handle = webcrypto.importKey(
65
- fmt,
62
+ format,
66
63
  keyData.dataStart,
67
64
  keyData.byteLength,
68
65
  p.dataStart,
@@ -73,18 +70,17 @@ export class SubtleCrypto {
73
70
  if (handle < 0) throw new Error(cryptoError(handle));
74
71
 
75
72
  let type: string;
76
- if (fmt == FMT_PKCS8) type = "private";
77
- else if (fmt == FMT_SPKI) type = "public";
73
+ if (format == FMT_PKCS8) type = "private";
74
+ else if (format == FMT_SPKI) type = "public";
78
75
  else type = isSymmetricAlg(alg) ? "secret" : "public";
79
76
 
80
77
  return new CryptoKey(handle, type, extractable, alg, usages);
81
78
  }
82
79
 
83
- exportKey(format: string, key: CryptoKey): Uint8Array {
84
- let fmt = formatId(format);
85
- if (fmt < 0) throw new Error("Unknown key format: " + format);
86
- if (fmt == FMT_JWK) throw new Error("jwk key format is not supported");
87
- return drain(webcrypto.exportKey(fmt, key.handle));
80
+ // `format` is an `FMT_*` selector const (FMT_RAW / FMT_PKCS8 / FMT_SPKI).
81
+ exportKey(format: i32, key: CryptoKey): Uint8Array {
82
+ if (format == FMT_JWK) throw new Error("jwk key format is not supported");
83
+ return drain(webcrypto.exportKey(format, key.handle));
88
84
  }
89
85
 
90
86
  encrypt(algorithm: AlgorithmParams, key: CryptoKey, data: Uint8Array): Uint8Array {
@@ -146,6 +142,6 @@ export class SubtleCrypto {
146
142
  usages: i32
147
143
  ): CryptoKey {
148
144
  let bits = this.deriveBits(algorithm, baseKey, lengthBits);
149
- return this.importKey("raw", bits, derivedKeyAlgorithm, extractable, usages);
145
+ return this.importKey(FMT_RAW, bits, derivedKeyAlgorithm, extractable, usages);
150
146
  }
151
147
  }
@@ -11,7 +11,16 @@
11
11
 
12
12
  import { webcrypto } from "bindings/webcrypto";
13
13
  import { SubtleCrypto } from "crypto/subtle";
14
- import { HmacImportParams, HmacParams, ALG_SHA_256, USAGE_SIGN } from "crypto/algorithms";
14
+ import {
15
+ HmacImportParams,
16
+ HmacParams,
17
+ ALG_SHA_1,
18
+ ALG_SHA_256,
19
+ ALG_SHA_384,
20
+ ALG_SHA_512,
21
+ FMT_RAW,
22
+ USAGE_SIGN,
23
+ } from "crypto/algorithms";
15
24
  import { Encoding } from "encoding";
16
25
 
17
26
  // Re-export the public surface so guests can import everything from "crypto".
@@ -51,6 +60,13 @@ export {
51
60
  ALG_X25519,
52
61
  ALG_HKDF,
53
62
  ALG_PBKDF2,
63
+ ALG_SHA3_256,
64
+ ALG_SHA3_384,
65
+ ALG_SHA3_512,
66
+ FMT_RAW,
67
+ FMT_PKCS8,
68
+ FMT_SPKI,
69
+ FMT_JWK,
54
70
  CURVE_P256,
55
71
  CURVE_P384,
56
72
  USAGE_ENCRYPT,
@@ -101,16 +117,16 @@ export namespace crypto {
101
117
 
102
118
  // --- Ergonomic digest helpers (thin wrappers over subtle.digest) ----------
103
119
  export function sha1(data: Uint8Array): Uint8Array {
104
- return subtle.digest("SHA-1", data);
120
+ return subtle.digest(ALG_SHA_1, data);
105
121
  }
106
122
  export function sha256(data: Uint8Array): Uint8Array {
107
- return subtle.digest("SHA-256", data);
123
+ return subtle.digest(ALG_SHA_256, data);
108
124
  }
109
125
  export function sha384(data: Uint8Array): Uint8Array {
110
- return subtle.digest("SHA-384", data);
126
+ return subtle.digest(ALG_SHA_384, data);
111
127
  }
112
128
  export function sha512(data: Uint8Array): Uint8Array {
113
- return subtle.digest("SHA-512", data);
129
+ return subtle.digest(ALG_SHA_512, data);
114
130
  }
115
131
 
116
132
  // String-input variants (UTF-8 encode, then hash).
@@ -129,7 +145,7 @@ export namespace crypto {
129
145
 
130
146
  /// One-shot HMAC-SHA-256 over raw key + message bytes.
131
147
  export function hmacSha256(key: Uint8Array, msg: Uint8Array): Uint8Array {
132
- let k = subtle.importKey("raw", key, new HmacImportParams(ALG_SHA_256), false, USAGE_SIGN);
148
+ let k = subtle.importKey(FMT_RAW, key, new HmacImportParams(ALG_SHA_256), false, USAGE_SIGN);
133
149
  return subtle.sign(new HmacParams(), k, msg);
134
150
  }
135
151
  export function hmacSha256Text(key: Uint8Array, msg: string): Uint8Array {
@@ -2853,11 +2853,14 @@ declare class X25519ImportParams extends AlgorithmParams {}
2853
2853
  declare class EcdhParams extends AlgorithmParams {
2854
2854
  constructor(alg: i32, publicKeyHandle: i32);
2855
2855
  }
2856
- /** Synchronous SubtleCrypto (no Promises). Returns values directly. */
2856
+ /** Synchronous SubtleCrypto (no Promises). Returns values directly. The hash and
2857
+ * key-format arguments are typed ABI ids (no magic strings): pass an `ALG_SHA_*`
2858
+ * const to `digest`, and an `FMT_*` const (`FMT_RAW` / `FMT_PKCS8` / `FMT_SPKI`)
2859
+ * to `importKey` / `exportKey`. */
2857
2860
  declare class SubtleCrypto {
2858
- digest(algorithm: string, data: Uint8Array): Uint8Array;
2859
- importKey(format: string, keyData: Uint8Array, algorithm: AlgorithmParams, extractable: bool, usages: i32): CryptoKey;
2860
- exportKey(format: string, key: CryptoKey): Uint8Array;
2861
+ digest(algorithm: i32, data: Uint8Array): Uint8Array;
2862
+ importKey(format: i32, keyData: Uint8Array, algorithm: AlgorithmParams, extractable: bool, usages: i32): CryptoKey;
2863
+ exportKey(format: i32, key: CryptoKey): Uint8Array;
2861
2864
  encrypt(algorithm: AlgorithmParams, key: CryptoKey, data: Uint8Array): Uint8Array;
2862
2865
  decrypt(algorithm: AlgorithmParams, key: CryptoKey, data: Uint8Array): Uint8Array;
2863
2866
  sign(algorithm: AlgorithmParams, key: CryptoKey, data: Uint8Array): Uint8Array;
@@ -2868,6 +2871,8 @@ declare class SubtleCrypto {
2868
2871
 
2869
2872
  // Algorithm / format / curve / usage ids (the Web Crypto ABI contract).
2870
2873
  declare const ALG_SHA_1: i32, ALG_SHA_256: i32, ALG_SHA_384: i32, ALG_SHA_512: i32;
2874
+ declare const ALG_SHA3_256: i32, ALG_SHA3_384: i32, ALG_SHA3_512: i32;
2875
+ declare const FMT_RAW: i32, FMT_PKCS8: i32, FMT_SPKI: i32, FMT_JWK: i32;
2871
2876
  declare const ALG_AES_GCM: i32, ALG_AES_CBC: i32, ALG_AES_CTR: i32, ALG_AES_KW: i32;
2872
2877
  declare const ALG_HMAC: i32, ALG_ECDSA: i32, ALG_ED25519: i32, ALG_ECDH: i32, ALG_X25519: i32, ALG_HKDF: i32, ALG_PBKDF2: i32;
2873
2878
  declare const CURVE_P256: i32, CURVE_P384: i32;
@@ -0,0 +1,311 @@
1
+ // ToilDB: the developer-facing edge-database API.
2
+ //
3
+ // `@database class App { @collection(...) users!: Record<User, UserId>; ... }`
4
+ // declares logical collections; the compiler (see parser `injectDatabaseBinding`)
5
+ // synthesizes a `@global App` singleton whose fields are these typed handles,
6
+ // resolved to numeric host handles once at module init.
7
+ //
8
+ // The handles marshal `@data` keys/values to the `env.data.*` host imports
9
+ // (`bindings/toildb`). Keys and values are `@data` types (they have the injected
10
+ // `encode(): Uint8Array` + `decodeInto(buf): void` instance methods). Both
11
+ // encode and decode go through INSTANCE methods on the generic type parameter,
12
+ // which AssemblyScript resolves at specialization (it cannot call the `decode`
13
+ // static through a type parameter, but `instantiate<V>()` + `v.decodeInto(buf)`
14
+ // works). Value types must be default-constructible.
15
+
16
+ import { toildbHost } from "bindings/toildb";
17
+ import { DataWriter } from "data";
18
+
19
+ /// Resolve a `"<db>/<collection>"` name to its numeric host handle. Called once
20
+ /// per collection at module init by the generated `App` binding.
21
+ export function __toildbResolve(name: string): u32 {
22
+ const nb = Uint8Array.wrap(String.UTF8.encode(name));
23
+ const out = new Uint8Array(4);
24
+ toildbHost.resolveCollection(nb.dataStart, nb.byteLength, out.dataStart);
25
+ return load<u32>(out.dataStart);
26
+ }
27
+
28
+ /// Pull the last stashed variable-length result of `len` bytes into a buffer.
29
+ function __toildbTake(len: i32): Uint8Array {
30
+ const buf = new Uint8Array(len);
31
+ toildbHost.takeResult(buf.dataStart, len);
32
+ return buf;
33
+ }
34
+
35
+ /// Pull a stashed result whose length is NOT known up front (e.g. the owner
36
+ /// returned by a failed `claim`): grow the buffer until `take_result` fits.
37
+ function __toildbTakeGrow(): Uint8Array {
38
+ let cap = 256;
39
+ let buf = new Uint8Array(cap);
40
+ let n = toildbHost.takeResult(buf.dataStart, cap);
41
+ while (n == -1) {
42
+ cap = cap * 2;
43
+ buf = new Uint8Array(cap);
44
+ n = toildbHost.takeResult(buf.dataStart, cap);
45
+ }
46
+ return buf.subarray(0, n);
47
+ }
48
+
49
+ /// A mutable keyed-entity collection (spec 7.1). `V` is the `@data` value type,
50
+ /// `K` the `@data` key type.
51
+ export class Record<V, K> {
52
+ private __handle: u32;
53
+
54
+ constructor(handle: u32) {
55
+ this.__handle = handle;
56
+ }
57
+
58
+ /// Return the record, or `null` if it does not exist.
59
+ get(key: K): V | null {
60
+ const kb = key.encode();
61
+ const status = toildbHost.get(this.__handle, kb.dataStart, kb.byteLength);
62
+ if (status < 0) return null;
63
+ const v = instantiate<V>();
64
+ v.decodeInto(__toildbTake(status));
65
+ return v;
66
+ }
67
+
68
+ /// Like `get`, but traps if the record is absent.
69
+ require(key: K): V {
70
+ const v = this.get(key);
71
+ if (v == null) unreachable();
72
+ return v!;
73
+ }
74
+
75
+ /// Bounded multi-get: one op, one result per key (in order), each the value
76
+ /// or `null` if absent. The key count is capped by the request budget.
77
+ getMany(keys: K[]): Array<V | null> {
78
+ const w = new DataWriter();
79
+ w.writeU32(<u32>keys.length);
80
+ for (let i = 0, n = keys.length; i < n; i++) {
81
+ w.writeBytes(keys[i].encode());
82
+ }
83
+ const blob = w.toBytes();
84
+ const status = toildbHost.getMany(this.__handle, blob.dataStart, blob.byteLength);
85
+ if (status < 0) unreachable();
86
+ const out = __toildbTake(status);
87
+ const results = new Array<V | null>();
88
+ let off: i32 = 0;
89
+ const count = load<u32>(out.dataStart + off);
90
+ off += 4;
91
+ for (let i: u32 = 0; i < count; i++) {
92
+ const present = load<u8>(out.dataStart + off);
93
+ off += 1;
94
+ if (present == 0) {
95
+ results.push(null);
96
+ continue;
97
+ }
98
+ const len = <i32>load<u32>(out.dataStart + off);
99
+ off += 4;
100
+ const v = instantiate<V>();
101
+ v.decodeInto(out.subarray(off, off + len));
102
+ off += len;
103
+ results.push(v);
104
+ }
105
+ return results;
106
+ }
107
+
108
+ /// Whether the record exists.
109
+ exists(key: K): bool {
110
+ const kb = key.encode();
111
+ return toildbHost.exists(this.__handle, kb.dataStart, kb.byteLength) == 1;
112
+ }
113
+
114
+ /// Create the record if absent. Returns false if it already existed.
115
+ create(key: K, value: V): bool {
116
+ const kb = key.encode();
117
+ const vb = value.encode();
118
+ return toildbHost.create(
119
+ this.__handle, kb.dataStart, kb.byteLength, vb.dataStart, vb.byteLength, 0
120
+ ) == 0;
121
+ }
122
+
123
+ /// Apply a write through the key's home cell; returns the stored record.
124
+ patch(key: K, value: V): V {
125
+ const kb = key.encode();
126
+ const vb = value.encode();
127
+ const status = toildbHost.patch(
128
+ this.__handle, kb.dataStart, kb.byteLength, vb.dataStart, vb.byteLength, 0
129
+ );
130
+ if (status < 0) unreachable();
131
+ const v = instantiate<V>();
132
+ v.decodeInto(__toildbTake(status));
133
+ return v;
134
+ }
135
+
136
+ /// Delete the record (idempotent).
137
+ delete(key: K): void {
138
+ const kb = key.encode();
139
+ toildbHost.del(this.__handle, kb.dataStart, kb.byteLength, 0);
140
+ }
141
+
142
+ /// Atomic fetch-and-delete (consume-once); returns the prior value or `null`.
143
+ getDelete(key: K): V | null {
144
+ const kb = key.encode();
145
+ const status = toildbHost.getDelete(this.__handle, kb.dataStart, kb.byteLength, 0);
146
+ if (status < 0) return null;
147
+ const v = instantiate<V>();
148
+ v.decodeInto(__toildbTake(status));
149
+ return v;
150
+ }
151
+ }
152
+
153
+ /// A precomputed, read-optimized projection (spec 7.2): home pages,
154
+ /// leaderboards, rendered fragments. Read by any function kind; PUBLISHED only
155
+ /// by a `@derive`/`@job` (the host kind gate enforces it). `V` is the `@data`
156
+ /// value type, `K` the `@data` key type.
157
+ export class View<V, K> {
158
+ private __handle: u32;
159
+
160
+ constructor(handle: u32) {
161
+ this.__handle = handle;
162
+ }
163
+
164
+ /// The published view for `key`, or `null` if none has been published.
165
+ get(key: K): V | null {
166
+ const kb = key.encode();
167
+ const status = toildbHost.viewGet(this.__handle, kb.dataStart, kb.byteLength);
168
+ if (status < 0) return null;
169
+ const v = instantiate<V>();
170
+ v.decodeInto(__toildbTake(status));
171
+ return v;
172
+ }
173
+
174
+ /// Like `get`, but traps if no view is published.
175
+ require(key: K): V {
176
+ const v = this.get(key);
177
+ if (v == null) unreachable();
178
+ return v!;
179
+ }
180
+
181
+ /// Publish (overwrite) the view for `key`. Derive/job only; the host assigns
182
+ /// the version so a later publish always supersedes an earlier one.
183
+ publish(key: K, value: V): void {
184
+ const kb = key.encode();
185
+ const vb = value.encode();
186
+ toildbHost.viewPublish(
187
+ this.__handle, kb.dataStart, kb.byteLength, vb.dataStart, vb.byteLength, 0
188
+ );
189
+ }
190
+ }
191
+
192
+ /// The result of a `unique.claim` (spec 8.6). `claimed` is true when the caller
193
+ /// owns the key (a fresh claim or an idempotent re-claim of its own); when
194
+ /// false, `owner` is the value that currently holds the key.
195
+ export class ClaimResult<V> {
196
+ constructor(public claimed: bool, public owner: V | null) {}
197
+ }
198
+
199
+ /// A globally-unique claim collection (spec 7.6): username, email, slug, ...
200
+ /// `V` is the `@data` OWNER value type, `K` the `@data` claim-key type.
201
+ export class Unique<V, K> {
202
+ private __handle: u32;
203
+
204
+ constructor(handle: u32) {
205
+ this.__handle = handle;
206
+ }
207
+
208
+ /// The value that owns `key`, or `null` if unclaimed.
209
+ lookup(key: K): V | null {
210
+ const kb = key.encode();
211
+ const status = toildbHost.uniqueLookup(this.__handle, kb.dataStart, kb.byteLength);
212
+ if (status < 0) return null;
213
+ const v = instantiate<V>();
214
+ v.decodeInto(__toildbTake(status));
215
+ return v;
216
+ }
217
+
218
+ /// Claim `key` for `value`. Returns whether the caller owns it, and (when
219
+ /// another owns it) who.
220
+ claim(key: K, value: V): ClaimResult<V> {
221
+ const kb = key.encode();
222
+ const vb = value.encode();
223
+ const tag = toildbHost.uniqueClaim(
224
+ this.__handle, kb.dataStart, kb.byteLength, vb.dataStart, vb.byteLength, 0
225
+ );
226
+ if (tag < 0) unreachable();
227
+ if (tag == 1) {
228
+ // AlreadyClaimed: the current owner is stashed.
229
+ const owner = instantiate<V>();
230
+ owner.decodeInto(__toildbTakeGrow());
231
+ return new ClaimResult<V>(false, owner);
232
+ }
233
+ // 0 Claimed, 2 AlreadyOwnedByCaller -> the caller owns it.
234
+ return new ClaimResult<V>(true, null);
235
+ }
236
+
237
+ /// Release `key` (only the current owner may; a non-owner release traps).
238
+ release(key: K, value: V): void {
239
+ const kb = key.encode();
240
+ const vb = value.encode();
241
+ toildbHost.uniqueRelease(
242
+ this.__handle, kb.dataStart, kb.byteLength, vb.dataStart, vb.byteLength, 0
243
+ );
244
+ }
245
+ }
246
+
247
+ /// A commutative integer counter (spec 7.4): likes, view counts, inventory.
248
+ /// `K` is the `@data` key type; the value is a host-aggregated i64 rollup (there
249
+ /// is no `set`, only `add` and `get`, so concurrent deltas never lose writes).
250
+ export class Counter<K> {
251
+ private __handle: u32;
252
+
253
+ constructor(handle: u32) {
254
+ this.__handle = handle;
255
+ }
256
+
257
+ /// The current sum (0 if no deltas have been applied).
258
+ get(key: K): i64 {
259
+ const kb = key.encode();
260
+ const status = toildbHost.counterGet(this.__handle, kb.dataStart, kb.byteLength);
261
+ if (status < 0) unreachable();
262
+ const buf = __toildbTake(status);
263
+ return load<i64>(buf.dataStart);
264
+ }
265
+
266
+ /// Apply a (possibly negative) delta; saturates at the i64 bounds.
267
+ add(key: K, delta: i64): void {
268
+ const kb = key.encode();
269
+ toildbHost.counterAdd(this.__handle, kb.dataStart, kb.byteLength, delta, 0);
270
+ }
271
+ }
272
+
273
+ /// An append-only event log (spec 7.5): activity feeds, audit trails, the
274
+ /// fact stream a `@derive` consumes. `V` is the `@data` event type, `K` the
275
+ /// `@data` stream-key type.
276
+ export class Events<V, K> {
277
+ private __handle: u32;
278
+
279
+ constructor(handle: u32) {
280
+ this.__handle = handle;
281
+ }
282
+
283
+ /// Append an event to the stream.
284
+ append(key: K, event: V): void {
285
+ const kb = key.encode();
286
+ const eb = event.encode();
287
+ toildbHost.append(this.__handle, kb.dataStart, kb.byteLength, eb.dataStart, eb.byteLength, 0);
288
+ }
289
+
290
+ /// The newest `limit` events, newest first. Decodes each framed event into a
291
+ /// `V`. The host frames them as `u32 count` then per event `u32 len + bytes`.
292
+ latest(key: K, limit: i32): V[] {
293
+ const kb = key.encode();
294
+ const status = toildbHost.latest(this.__handle, kb.dataStart, kb.byteLength, limit);
295
+ if (status < 0) unreachable();
296
+ const blob = __toildbTake(status);
297
+ const out = new Array<V>();
298
+ let off: i32 = 0;
299
+ const count = load<u32>(blob.dataStart + off);
300
+ off += 4;
301
+ for (let i: u32 = 0; i < count; i++) {
302
+ const len = <i32>load<u32>(blob.dataStart + off);
303
+ off += 4;
304
+ const ev = instantiate<V>();
305
+ ev.decodeInto(blob.subarray(off, off + len));
306
+ out.push(ev);
307
+ off += len;
308
+ }
309
+ return out;
310
+ }
311
+ }