toiljs 0.0.55 → 0.0.56

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.
Files changed (87) hide show
  1. package/build/backend/.tsbuildinfo +1 -1
  2. package/build/cli/.tsbuildinfo +1 -1
  3. package/build/cli/index.js +9 -5
  4. package/build/client/.tsbuildinfo +1 -1
  5. package/build/client/auth.js +1 -1
  6. package/build/client/components/Image.d.ts +1 -1
  7. package/build/client/dev/devtools.js +3 -1
  8. package/build/client/index.d.ts +2 -2
  9. package/build/client/index.js +2 -2
  10. package/build/client/routing/Router.js +1 -1
  11. package/build/client/routing/mount.js +1 -1
  12. package/build/compiler/.tsbuildinfo +1 -1
  13. package/build/compiler/docs.js +1 -1
  14. package/build/compiler/seo.js +1 -3
  15. package/build/compiler/template-build.js +1 -1
  16. package/build/devserver/.tsbuildinfo +1 -1
  17. package/build/devserver/cache.js +0 -0
  18. package/build/devserver/crypto.js +45 -17
  19. package/build/devserver/database.js +82 -0
  20. package/build/devserver/email/caps.js +0 -0
  21. package/build/devserver/email/config.js +7 -2
  22. package/build/devserver/email/validate.js +1 -4
  23. package/build/devserver/index.d.ts +1 -1
  24. package/build/devserver/index.js +3 -2
  25. package/build/devserver/module.js +51 -12
  26. package/build/devserver/proxy.js +2 -1
  27. package/build/io/.tsbuildinfo +1 -1
  28. package/build/io/codec.d.ts +5 -5
  29. package/build/io/codec.js +193 -77
  30. package/examples/basic/client/components/HoneycombBackground.tsx +1 -1
  31. package/examples/basic/client/public/images/logo.svg +37 -34
  32. package/examples/basic/client/public/index.html +14 -14
  33. package/examples/basic/client/routes/auth.tsx +18 -10
  34. package/examples/basic/client/routes/cookies.tsx +15 -24
  35. package/examples/basic/client/routes/crypto.tsx +4 -5
  36. package/examples/basic/client/routes/features/template/template.tsx +1 -1
  37. package/examples/basic/client/routes/hello.tsx +1 -1
  38. package/examples/basic/client/routes/pq.tsx +14 -14
  39. package/examples/basic/client/routes/rest.tsx +1 -3
  40. package/examples/basic/client/styles/main.css +25 -22
  41. package/examples/basic/client/toil.tsx +1 -1
  42. package/examples/basic/server/README.md +8 -8
  43. package/examples/basic/server/core/AppHandler.ts +4 -7
  44. package/examples/basic/server/routes/Auth.ts +11 -3
  45. package/examples/basic/server/routes/EnvDemo.ts +9 -3
  46. package/package.json +1 -1
  47. package/src/backend/index.ts +4 -2
  48. package/src/cli/doctor.ts +10 -3
  49. package/src/cli/notify.ts +1 -6
  50. package/src/cli/ui.ts +3 -3
  51. package/src/cli/version-check.ts +5 -1
  52. package/src/client/auth.ts +33 -10
  53. package/src/client/components/Form.tsx +2 -2
  54. package/src/client/components/Image.tsx +1 -1
  55. package/src/client/components/Script.tsx +1 -1
  56. package/src/client/components/Slot.tsx +1 -1
  57. package/src/client/dev/devtools.tsx +121 -54
  58. package/src/client/dev/error-overlay.tsx +7 -1
  59. package/src/client/head/metadata.ts +1 -1
  60. package/src/client/index.ts +13 -2
  61. package/src/client/routing/Router.tsx +2 -2
  62. package/src/client/routing/error-boundary.tsx +1 -1
  63. package/src/client/routing/loader.ts +2 -2
  64. package/src/client/routing/mount.tsx +5 -6
  65. package/src/compiler/docs.ts +1 -1
  66. package/src/compiler/email-preview.ts +1 -1
  67. package/src/compiler/generate.ts +1 -1
  68. package/src/compiler/seo.ts +1 -3
  69. package/src/compiler/ssg.ts +10 -4
  70. package/src/compiler/template-build.ts +2 -7
  71. package/src/compiler/template.ts +1 -4
  72. package/src/compiler/vite.ts +1 -1
  73. package/src/devserver/cache.ts +0 -0
  74. package/src/devserver/crypto.ts +140 -51
  75. package/src/devserver/database.ts +149 -8
  76. package/src/devserver/dotenv.ts +10 -2
  77. package/src/devserver/email/caps.ts +0 -0
  78. package/src/devserver/email/config.ts +8 -2
  79. package/src/devserver/email/index.ts +3 -3
  80. package/src/devserver/email/validate.ts +1 -4
  81. package/src/devserver/envelope.ts +3 -3
  82. package/src/devserver/host.ts +14 -5
  83. package/src/devserver/index.ts +15 -6
  84. package/src/devserver/module.ts +56 -14
  85. package/src/devserver/proxy.ts +5 -7
  86. package/src/io/codec.ts +226 -83
  87. package/test/devserver-database.test.ts +60 -0
package/src/io/codec.ts CHANGED
@@ -34,60 +34,81 @@ export class DataWriter {
34
34
  this.view = new DataView(this.buf.buffer);
35
35
  }
36
36
 
37
- /**
38
- * Ensures room for `extra` more bytes and returns the offset to write at. Grows
39
- * (doubling) when needed, reassigning `buf`/`view`. Callers MUST read the returned
40
- * offset into a local before touching `this.view`/`this.buf`, since a grow swaps
41
- * them out from under a stale receiver.
42
- */
43
- private reserve(extra: number): number {
44
- const need = this.off + extra;
45
- if (need > this.buf.length) {
46
- let n = this.buf.length;
47
- while (n < need) n <<= 1;
48
- const bigger = new Uint8Array(n);
49
- bigger.set(this.buf.subarray(0, this.off));
50
- this.buf = bigger;
51
- this.view = new DataView(this.buf.buffer);
52
- }
53
- const at = this.off;
54
- this.off += extra;
55
- return at;
37
+ /** Writes an unsigned 8-bit byte (the low 8 bits of `v`). */
38
+ writeU8(v: number): this {
39
+ const at = this.reserve(1);
40
+ this.view.setUint8(at, v & 0xff);
41
+ return this;
56
42
  }
57
43
 
58
- /** Writes an unsigned 8-bit byte (the low 8 bits of `v`). */
59
- writeU8(v: number): this { const at = this.reserve(1); this.view.setUint8(at, v & 0xff); return this; }
60
44
  /** Writes an unsigned 16-bit integer. @param be - big-endian if true (default little-endian). */
61
- writeU16(v: number, be?: boolean): this { const at = this.reserve(2); this.view.setUint16(at, v & 0xffff, !be); return this; }
45
+ writeU16(v: number, be?: boolean): this {
46
+ const at = this.reserve(2);
47
+ this.view.setUint16(at, v & 0xffff, !be);
48
+ return this;
49
+ }
50
+
62
51
  /** Writes an unsigned 32-bit integer. @param be - big-endian if true (default little-endian). */
63
- writeU32(v: number, be?: boolean): this { const at = this.reserve(4); this.view.setUint32(at, v >>> 0, !be); return this; }
52
+ writeU32(v: number, be?: boolean): this {
53
+ const at = this.reserve(4);
54
+ this.view.setUint32(at, v >>> 0, !be);
55
+ return this;
56
+ }
57
+
64
58
  /** Writes an unsigned 64-bit integer (low 64 bits of `v`). @param be - big-endian if true. */
65
- writeU64(v: bigint, be?: boolean): this { const at = this.reserve(8); this.view.setBigUint64(at, v & MASK64, !be); return this; }
59
+ writeU64(v: bigint, be?: boolean): this {
60
+ const at = this.reserve(8);
61
+ this.view.setBigUint64(at, v & MASK64, !be);
62
+ return this;
63
+ }
64
+
66
65
  /** Writes a signed 8-bit integer. */
67
- writeI8(v: number): this { const at = this.reserve(1); this.view.setInt8(at, v); return this; }
66
+ writeI8(v: number): this {
67
+ const at = this.reserve(1);
68
+ this.view.setInt8(at, v);
69
+ return this;
70
+ }
71
+
68
72
  /** Writes a signed 16-bit integer. @param be - big-endian if true (default little-endian). */
69
- writeI16(v: number, be?: boolean): this { const at = this.reserve(2); this.view.setInt16(at, v, !be); return this; }
73
+ writeI16(v: number, be?: boolean): this {
74
+ const at = this.reserve(2);
75
+ this.view.setInt16(at, v, !be);
76
+ return this;
77
+ }
78
+
70
79
  /** Writes a signed 32-bit integer. @param be - big-endian if true (default little-endian). */
71
- writeI32(v: number, be?: boolean): this { const at = this.reserve(4); this.view.setInt32(at, v | 0, !be); return this; }
80
+ writeI32(v: number, be?: boolean): this {
81
+ const at = this.reserve(4);
82
+ this.view.setInt32(at, v | 0, !be);
83
+ return this;
84
+ }
85
+
72
86
  /** Writes a signed 64-bit integer. @param be - big-endian if true (default little-endian). */
73
- writeI64(v: bigint, be?: boolean): this { const at = this.reserve(8); this.view.setBigInt64(at, BigInt.asIntN(64, v), !be); return this; }
87
+ writeI64(v: bigint, be?: boolean): this {
88
+ const at = this.reserve(8);
89
+ this.view.setBigInt64(at, BigInt.asIntN(64, v), !be);
90
+ return this;
91
+ }
92
+
74
93
  /** Writes a 32-bit float. @param be - big-endian if true (default little-endian). */
75
- writeF32(v: number, be?: boolean): this { const at = this.reserve(4); this.view.setFloat32(at, v, !be); return this; }
76
- /** Writes a 64-bit float. @param be - big-endian if true (default little-endian). */
77
- writeF64(v: number, be?: boolean): this { const at = this.reserve(8); this.view.setFloat64(at, v, !be); return this; }
78
- /** Writes a boolean as one byte (1 or 0). */
79
- writeBool(v: boolean): this { return this.writeU8(v ? 1 : 0); }
94
+ writeF32(v: number, be?: boolean): this {
95
+ const at = this.reserve(4);
96
+ this.view.setFloat32(at, v, !be);
97
+ return this;
98
+ }
80
99
 
81
- /** Writes the `count` 64-bit limbs of `u` (low limb first in LE, high limb first in BE). */
82
- private writeLimbs(u: bigint, count: number, be: boolean): this {
83
- if (be) {
84
- for (let i = count - 1; i >= 0; i--) this.writeU64((u >> BigInt(i * 64)) & MASK64, true);
85
- } else {
86
- for (let i = 0; i < count; i++) this.writeU64((u >> BigInt(i * 64)) & MASK64, false);
87
- }
100
+ /** Writes a 64-bit float. @param be - big-endian if true (default little-endian). */
101
+ writeF64(v: number, be?: boolean): this {
102
+ const at = this.reserve(8);
103
+ this.view.setFloat64(at, v, !be);
88
104
  return this;
89
105
  }
90
106
 
107
+ /** Writes a boolean as one byte (1 or 0). */
108
+ writeBool(v: boolean): this {
109
+ return this.writeU8(v ? 1 : 0);
110
+ }
111
+
91
112
  /** Writes a `u32` length prefix followed by the raw bytes. @param be - endianness of the prefix. */
92
113
  writeBytes(bytes: Uint8Array, be?: boolean): this {
93
114
  this.writeU32(bytes.length, be);
@@ -110,19 +131,66 @@ export class DataWriter {
110
131
  }
111
132
 
112
133
  /** Writes an unsigned 128-bit integer as two 64-bit limbs. @param be - big-endian if true. */
113
- writeU128(v: bigint, be?: boolean): this { return this.writeLimbs(BigInt.asUintN(128, v), 2, !!be); }
134
+ writeU128(v: bigint, be?: boolean): this {
135
+ return this.writeLimbs(BigInt.asUintN(128, v), 2, !!be);
136
+ }
137
+
114
138
  /** Writes a signed 128-bit integer as two 64-bit limbs (two's complement). @param be - big-endian if true. */
115
- writeI128(v: bigint, be?: boolean): this { return this.writeLimbs(BigInt.asUintN(128, v), 2, !!be); }
139
+ writeI128(v: bigint, be?: boolean): this {
140
+ return this.writeLimbs(BigInt.asUintN(128, v), 2, !!be);
141
+ }
142
+
116
143
  /** Writes an unsigned 256-bit integer as four 64-bit limbs. @param be - big-endian if true. */
117
- writeU256(v: bigint, be?: boolean): this { return this.writeLimbs(BigInt.asUintN(256, v), 4, !!be); }
144
+ writeU256(v: bigint, be?: boolean): this {
145
+ return this.writeLimbs(BigInt.asUintN(256, v), 4, !!be);
146
+ }
147
+
118
148
  /** Writes a signed 256-bit integer as four 64-bit limbs (two's complement). @param be - big-endian if true. */
119
- writeI256(v: bigint, be?: boolean): this { return this.writeLimbs(BigInt.asUintN(256, v), 4, !!be); }
149
+ writeI256(v: bigint, be?: boolean): this {
150
+ return this.writeLimbs(BigInt.asUintN(256, v), 4, !!be);
151
+ }
120
152
 
121
153
  /** Number of bytes written so far. */
122
- length(): number { return this.off; }
154
+ length(): number {
155
+ return this.off;
156
+ }
123
157
 
124
158
  /** A fresh copy of exactly the bytes written. */
125
- toBytes(): Uint8Array<ArrayBuffer> { return this.buf.slice(0, this.off); }
159
+ toBytes(): Uint8Array<ArrayBuffer> {
160
+ return this.buf.slice(0, this.off);
161
+ }
162
+
163
+ /**
164
+ * Ensures room for `extra` more bytes and returns the offset to write at. Grows
165
+ * (doubling) when needed, reassigning `buf`/`view`. Callers MUST read the returned
166
+ * offset into a local before touching `this.view`/`this.buf`, since a grow swaps
167
+ * them out from under a stale receiver.
168
+ */
169
+ private reserve(extra: number): number {
170
+ const need = this.off + extra;
171
+ if (need > this.buf.length) {
172
+ let n = this.buf.length;
173
+ while (n < need) n <<= 1;
174
+ const bigger = new Uint8Array(n);
175
+ bigger.set(this.buf.subarray(0, this.off));
176
+ this.buf = bigger;
177
+ this.view = new DataView(this.buf.buffer);
178
+ }
179
+ const at = this.off;
180
+ this.off += extra;
181
+ return at;
182
+ }
183
+
184
+ /** Writes the `count` 64-bit limbs of `u` (low limb first in LE, high limb first in BE). */
185
+ private writeLimbs(u: bigint, count: number, be: boolean): this {
186
+ if (be) {
187
+ for (let i = count - 1; i >= 0; i--)
188
+ this.writeU64((u >> BigInt(i * 64)) & MASK64, true);
189
+ } else {
190
+ for (let i = 0; i < count; i++) this.writeU64((u >> BigInt(i * 64)) & MASK64, false);
191
+ }
192
+ return this;
193
+ }
126
194
  }
127
195
 
128
196
  /**
@@ -132,11 +200,11 @@ export class DataWriter {
132
200
  * big-endian writer.
133
201
  */
134
202
  export class DataReader {
203
+ /** Cleared to false if any read ran past the end of the buffer. */
204
+ ok = true;
135
205
  private buf: Uint8Array;
136
206
  private view: DataView;
137
207
  private off = 0;
138
- /** Cleared to false if any read ran past the end of the buffer. */
139
- ok = true;
140
208
 
141
209
  /** @param bytes - the buffer to read from (its byteOffset/length are respected). */
142
210
  constructor(bytes: Uint8Array) {
@@ -144,47 +212,89 @@ export class DataReader {
144
212
  this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
145
213
  }
146
214
 
147
- /** Returns true (and leaves `off` advanceable) if `n` more bytes are available; else clears `ok`. */
148
- private has(n: number): boolean {
149
- if (n < 0 || this.off + n > this.buf.length) {
150
- this.ok = false;
151
- return false;
152
- }
153
- return true;
215
+ /** Reads an unsigned 8-bit byte (0 past end). */
216
+ readU8(): number {
217
+ if (!this.has(1)) return 0;
218
+ const v = this.view.getUint8(this.off);
219
+ this.off += 1;
220
+ return v;
154
221
  }
155
222
 
156
- /** Reads an unsigned 8-bit byte (0 past end). */
157
- readU8(): number { if (!this.has(1)) return 0; const v = this.view.getUint8(this.off); this.off += 1; return v; }
158
223
  /** Reads an unsigned 16-bit integer. @param be - big-endian if true (default little-endian). */
159
- readU16(be?: boolean): number { if (!this.has(2)) return 0; const v = this.view.getUint16(this.off, !be); this.off += 2; return v; }
224
+ readU16(be?: boolean): number {
225
+ if (!this.has(2)) return 0;
226
+ const v = this.view.getUint16(this.off, !be);
227
+ this.off += 2;
228
+ return v;
229
+ }
230
+
160
231
  /** Reads an unsigned 32-bit integer. @param be - big-endian if true (default little-endian). */
161
- readU32(be?: boolean): number { if (!this.has(4)) return 0; const v = this.view.getUint32(this.off, !be); this.off += 4; return v >>> 0; }
232
+ readU32(be?: boolean): number {
233
+ if (!this.has(4)) return 0;
234
+ const v = this.view.getUint32(this.off, !be);
235
+ this.off += 4;
236
+ return v >>> 0;
237
+ }
238
+
162
239
  /** Reads an unsigned 64-bit integer. @param be - big-endian if true (default little-endian). */
163
- readU64(be?: boolean): bigint { if (!this.has(8)) return 0n; const v = this.view.getBigUint64(this.off, !be); this.off += 8; return v; }
240
+ readU64(be?: boolean): bigint {
241
+ if (!this.has(8)) return 0n;
242
+ const v = this.view.getBigUint64(this.off, !be);
243
+ this.off += 8;
244
+ return v;
245
+ }
246
+
164
247
  /** Reads a signed 8-bit integer (0 past end). */
165
- readI8(): number { if (!this.has(1)) return 0; const v = this.view.getInt8(this.off); this.off += 1; return v; }
248
+ readI8(): number {
249
+ if (!this.has(1)) return 0;
250
+ const v = this.view.getInt8(this.off);
251
+ this.off += 1;
252
+ return v;
253
+ }
254
+
166
255
  /** Reads a signed 16-bit integer. @param be - big-endian if true (default little-endian). */
167
- readI16(be?: boolean): number { if (!this.has(2)) return 0; const v = this.view.getInt16(this.off, !be); this.off += 2; return v; }
256
+ readI16(be?: boolean): number {
257
+ if (!this.has(2)) return 0;
258
+ const v = this.view.getInt16(this.off, !be);
259
+ this.off += 2;
260
+ return v;
261
+ }
262
+
168
263
  /** Reads a signed 32-bit integer. @param be - big-endian if true (default little-endian). */
169
- readI32(be?: boolean): number { if (!this.has(4)) return 0; const v = this.view.getInt32(this.off, !be); this.off += 4; return v; }
264
+ readI32(be?: boolean): number {
265
+ if (!this.has(4)) return 0;
266
+ const v = this.view.getInt32(this.off, !be);
267
+ this.off += 4;
268
+ return v;
269
+ }
270
+
170
271
  /** Reads a signed 64-bit integer. @param be - big-endian if true (default little-endian). */
171
- readI64(be?: boolean): bigint { if (!this.has(8)) return 0n; const v = this.view.getBigInt64(this.off, !be); this.off += 8; return v; }
272
+ readI64(be?: boolean): bigint {
273
+ if (!this.has(8)) return 0n;
274
+ const v = this.view.getBigInt64(this.off, !be);
275
+ this.off += 8;
276
+ return v;
277
+ }
278
+
172
279
  /** Reads a 32-bit float. @param be - big-endian if true (default little-endian). */
173
- readF32(be?: boolean): number { if (!this.has(4)) return 0; const v = this.view.getFloat32(this.off, !be); this.off += 4; return v; }
280
+ readF32(be?: boolean): number {
281
+ if (!this.has(4)) return 0;
282
+ const v = this.view.getFloat32(this.off, !be);
283
+ this.off += 4;
284
+ return v;
285
+ }
286
+
174
287
  /** Reads a 64-bit float. @param be - big-endian if true (default little-endian). */
175
- readF64(be?: boolean): number { if (!this.has(8)) return 0; const v = this.view.getFloat64(this.off, !be); this.off += 8; return v; }
176
- /** Reads a boolean (any non-zero byte is true). */
177
- readBool(): boolean { return this.readU8() !== 0; }
288
+ readF64(be?: boolean): number {
289
+ if (!this.has(8)) return 0;
290
+ const v = this.view.getFloat64(this.off, !be);
291
+ this.off += 8;
292
+ return v;
293
+ }
178
294
 
179
- /** Reads `count` 64-bit limbs and recombines them (low limb first in LE, high first in BE). */
180
- private readLimbs(count: number, be: boolean): bigint {
181
- let result = 0n;
182
- if (be) {
183
- for (let i = count - 1; i >= 0; i--) result |= this.readU64(true) << BigInt(i * 64);
184
- } else {
185
- for (let i = 0; i < count; i++) result |= this.readU64(false) << BigInt(i * 64);
186
- }
187
- return result;
295
+ /** Reads a boolean (any non-zero byte is true). */
296
+ readBool(): boolean {
297
+ return this.readU8() !== 0;
188
298
  }
189
299
 
190
300
  /** Reads a `u32`-length-prefixed byte blob (empty past end). @param be - endianness of the prefix. */
@@ -199,21 +309,54 @@ export class DataReader {
199
309
  /** Reads a `u32`-byte-length-prefixed UTF-8 string (empty past end). @param be - endianness of the prefix. */
200
310
  readString(be?: boolean): string {
201
311
  const len = this.readU32(be);
202
- if (!this.has(len)) return "";
312
+ if (!this.has(len)) return '';
203
313
  const s = utf8Decoder.decode(this.buf.subarray(this.off, this.off + len));
204
314
  this.off += len;
205
315
  return s;
206
316
  }
207
317
 
208
318
  /** Reads an unsigned 128-bit integer. @param be - big-endian if true (default little-endian). */
209
- readU128(be?: boolean): bigint { return this.readLimbs(2, !!be); }
319
+ readU128(be?: boolean): bigint {
320
+ return this.readLimbs(2, !!be);
321
+ }
322
+
210
323
  /** Reads a signed 128-bit integer (two's complement). @param be - big-endian if true. */
211
- readI128(be?: boolean): bigint { return BigInt.asIntN(128, this.readLimbs(2, !!be)); }
324
+ readI128(be?: boolean): bigint {
325
+ return BigInt.asIntN(128, this.readLimbs(2, !!be));
326
+ }
327
+
212
328
  /** Reads an unsigned 256-bit integer. @param be - big-endian if true (default little-endian). */
213
- readU256(be?: boolean): bigint { return this.readLimbs(4, !!be); }
329
+ readU256(be?: boolean): bigint {
330
+ return this.readLimbs(4, !!be);
331
+ }
332
+
214
333
  /** Reads a signed 256-bit integer (two's complement). @param be - big-endian if true. */
215
- readI256(be?: boolean): bigint { return BigInt.asIntN(256, this.readLimbs(4, !!be)); }
334
+ readI256(be?: boolean): bigint {
335
+ return BigInt.asIntN(256, this.readLimbs(4, !!be));
336
+ }
216
337
 
217
338
  /** Bytes left to read. */
218
- remaining(): number { return this.buf.length - this.off; }
339
+ remaining(): number {
340
+ return this.buf.length - this.off;
341
+ }
342
+
343
+ /** Returns true (and leaves `off` advanceable) if `n` more bytes are available; else clears `ok`. */
344
+ private has(n: number): boolean {
345
+ if (n < 0 || this.off + n > this.buf.length) {
346
+ this.ok = false;
347
+ return false;
348
+ }
349
+ return true;
350
+ }
351
+
352
+ /** Reads `count` 64-bit limbs and recombines them (low limb first in LE, high first in BE). */
353
+ private readLimbs(count: number, be: boolean): bigint {
354
+ let result = 0n;
355
+ if (be) {
356
+ for (let i = count - 1; i >= 0; i--) result |= this.readU64(true) << BigInt(i * 64);
357
+ } else {
358
+ for (let i = 0; i < count; i++) result |= this.readU64(false) << BigInt(i * 64);
359
+ }
360
+ return result;
361
+ }
219
362
  }
@@ -302,3 +302,63 @@ describe('toildb dev emulator (record family)', () => {
302
302
  expect(imports['data.get'](posts, kPtr, kLen)).toBe(-2);
303
303
  });
304
304
  });
305
+
306
+ type Imports = Record<string, (...args: number[]) => number>;
307
+
308
+ describe('toildb dev emulator (capacity family)', () => {
309
+ // Pull the last stashed i64 available into the buffer and read it.
310
+ function avail(imports: Imports, buf: Buffer, h: number, kPtr: number, kLen: number): bigint {
311
+ expect(imports['data.capacity_available'](h, kPtr, kLen)).toBe(8);
312
+ expect(imports['data.take_result'](512, 16)).toBe(8);
313
+ return buf.readBigInt64LE(512);
314
+ }
315
+
316
+ it('set_total / reserve / confirm / cancel keep the ledger consistent', () => {
317
+ const { imports, buf } = setup();
318
+ const h = resolve(imports, buf, 'App/seats');
319
+ const [kPtr, kLen] = put(buf, 32, 'showA');
320
+
321
+ // an empty ledger reads 0 available, never absent.
322
+ expect(avail(imports, buf, h, kPtr, kLen)).toBe(0n);
323
+
324
+ // restock to 10.
325
+ expect(imports['data.capacity_set_total'](h, kPtr, kLen, 10, 0)).toBe(0);
326
+ expect(avail(imports, buf, h, kPtr, kLen)).toBe(10n);
327
+
328
+ // reserve 3 -> a u64 id (> 0) is stashed, available drops to 7.
329
+ expect(imports['data.capacity_reserve'](h, kPtr, kLen, 3, 60000, 0)).toBe(8);
330
+ expect(imports['data.take_result'](512, 16)).toBe(8);
331
+ const id = buf.readBigUInt64LE(512);
332
+ expect(id > 0n).toBe(true);
333
+ expect(avail(imports, buf, h, kPtr, kLen)).toBe(7n);
334
+
335
+ // cancel returns the hold -> back to 10; a double-cancel is a no-op.
336
+ expect(imports['data.capacity_cancel'](h, kPtr, kLen, Number(id), 0)).toBe(1);
337
+ expect(avail(imports, buf, h, kPtr, kLen)).toBe(10n);
338
+ expect(imports['data.capacity_cancel'](h, kPtr, kLen, Number(id), 0)).toBe(0);
339
+
340
+ // reserve 4 then confirm -> a permanent consume; available holds at 6.
341
+ expect(imports['data.capacity_reserve'](h, kPtr, kLen, 4, 60000, 0)).toBe(8);
342
+ imports['data.take_result'](512, 16);
343
+ const id2 = Number(buf.readBigUInt64LE(512));
344
+ expect(imports['data.capacity_confirm'](h, kPtr, kLen, id2, 0)).toBe(1);
345
+ expect(avail(imports, buf, h, kPtr, kLen)).toBe(6n);
346
+ // a confirmed hold cannot be cancelled nor re-confirmed.
347
+ expect(imports['data.capacity_cancel'](h, kPtr, kLen, id2, 0)).toBe(0);
348
+ expect(imports['data.capacity_confirm'](h, kPtr, kLen, id2, 0)).toBe(0);
349
+ });
350
+
351
+ it('never oversells (a reserve beyond available is refused)', () => {
352
+ const { imports, buf } = setup();
353
+ const h = resolve(imports, buf, 'App/tickets');
354
+ const [kPtr, kLen] = put(buf, 32, 'vip');
355
+ imports['data.capacity_set_total'](h, kPtr, kLen, 5, 0);
356
+
357
+ // a hold for all 5 succeeds; a further hold for 1 is refused (-2 -> guest 0).
358
+ expect(imports['data.capacity_reserve'](h, kPtr, kLen, 5, 60000, 0)).toBe(8);
359
+ expect(imports['data.capacity_reserve'](h, kPtr, kLen, 1, 60000, 0)).toBe(-2);
360
+ // a non-positive amount is refused, and an invalid handle is rejected.
361
+ expect(imports['data.capacity_reserve'](h, kPtr, kLen, 0, 60000, 0)).toBe(-2);
362
+ expect(imports['data.capacity_available'](999, kPtr, kLen)).toBe(-1001);
363
+ });
364
+ });