toilscript 0.1.58 → 0.1.60
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.js +615 -20
- package/dist/cli.js.map +2 -2
- package/dist/importmap.json +3 -3
- package/dist/web.js +4 -4
- package/package.json +11 -9
- package/std/assembly/bindings/daemon.ts +69 -0
- package/std/assembly/bindings/toildb.ts +1 -1
- package/std/assembly/bindings/webcrypto.ts +48 -0
- package/std/assembly/daemon.ts +315 -0
- package/std/assembly/toildb.ts +178 -15
- package/std/assembly/toilscript.d.ts +130 -6
package/std/assembly/toildb.ts
CHANGED
|
@@ -72,6 +72,151 @@ function __toildbTakeGrow(): Uint8Array {
|
|
|
72
72
|
return buf.subarray(0, n);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/// A typed ToilDB failure, the `TDLnnn` diagnostic of spec 27.1. The host bridges
|
|
76
|
+
/// one as `-(1000 + nnn)`; the two small sentinels are NOT failures (`-1` output
|
|
77
|
+
/// buffer too small, `-2` absent), so both decode to `None`.
|
|
78
|
+
///
|
|
79
|
+
/// Only a SUBSET can ever reach guest code. A hard fault (budget/quota, storage,
|
|
80
|
+
/// integrity, capability/schema) is indistinguishable from a benign outcome at the
|
|
81
|
+
/// ABI, so returning it would silently drop the op; the host TRAPS the request
|
|
82
|
+
/// instead and the guest never runs again (toil-backend `status_is_hard_fault`).
|
|
83
|
+
/// The members below say which is which. Every code is still listed, because
|
|
84
|
+
/// `Db.errorOf` decodes any raw status.
|
|
85
|
+
///
|
|
86
|
+
/// So after a read returns `null`, `Db.lastError()` is either `None` (the row is
|
|
87
|
+
/// absent) or one of the four RETRYABLE availability signals. `AlreadyExists` and
|
|
88
|
+
/// `Conflict` are write outcomes, not read ones.
|
|
89
|
+
@global
|
|
90
|
+
export enum DbError {
|
|
91
|
+
/// No failure: the operation succeeded, or the row was simply absent.
|
|
92
|
+
None = 0,
|
|
93
|
+
/// TDL001: the collection handle is not one the host issued. HARD FAULT: traps.
|
|
94
|
+
InvalidHandle = 1,
|
|
95
|
+
/// TDL002: wrong tenant, or the row's MAC did not verify. HARD FAULT: traps.
|
|
96
|
+
TenantMismatch = 2,
|
|
97
|
+
/// TDL003: a `create` found the key already present. A write outcome.
|
|
98
|
+
AlreadyExists = 3,
|
|
99
|
+
/// TDL004: a compare-and-set lost, or a `@unique` value is owned elsewhere. A
|
|
100
|
+
/// write outcome.
|
|
101
|
+
Conflict = 4,
|
|
102
|
+
/// TDL005: the operation is not supported here. HARD FAULT: traps.
|
|
103
|
+
Unsupported = 5,
|
|
104
|
+
/// TDL006: the value did not decode against its schema. HARD FAULT: traps.
|
|
105
|
+
Codec = 6,
|
|
106
|
+
/// TDL007: an internal storage failure. HARD FAULT: traps.
|
|
107
|
+
Backend = 7,
|
|
108
|
+
/// TDL010: the op does not exist for this collection family. HARD FAULT: traps.
|
|
109
|
+
OpNotAllowedForFamily = 10,
|
|
110
|
+
/// TDL011: the op is not allowed for this kind, or at this tier. HARD FAULT: traps.
|
|
111
|
+
OpNotAllowedInKind = 11,
|
|
112
|
+
/// TDL020: too many ops, or too many keys in one multi-op. HARD FAULT: traps.
|
|
113
|
+
OpBudget = 20,
|
|
114
|
+
/// TDL021: the request or its result exceeds the byte budget. HARD FAULT: traps.
|
|
115
|
+
ByteBudget = 21,
|
|
116
|
+
/// TDL022: the value nests too deeply. HARD FAULT: traps.
|
|
117
|
+
DepthBudget = 22,
|
|
118
|
+
/// TDL025: the tenant is over its plan quota. HARD FAULT: traps, so a guest can
|
|
119
|
+
/// never observe this. It is here only to decode a raw status.
|
|
120
|
+
QuotaExceeded = 25,
|
|
121
|
+
/// TDL030: a backfill is throttling this read. RETRYABLE.
|
|
122
|
+
FillThrottled = 30,
|
|
123
|
+
/// TDL031: the home region, or the data, is temporarily unavailable. RETRYABLE.
|
|
124
|
+
Unavailable = 31,
|
|
125
|
+
/// TDL040: this key is a hot partition. RETRYABLE.
|
|
126
|
+
HotPartition = 40,
|
|
127
|
+
/// TDL041: too many cold reads at once. RETRYABLE.
|
|
128
|
+
ColdStorm = 41,
|
|
129
|
+
/// TDL070: the schema is temporarily unavailable. HARD FAULT: traps.
|
|
130
|
+
SchemaUnavailable = 70,
|
|
131
|
+
/// TDL090: a catalog op outside an admin context. HARD FAULT: traps.
|
|
132
|
+
CatalogOutsideAdmin = 90,
|
|
133
|
+
/// A negative status that carries no known `TDLnnn`.
|
|
134
|
+
Unknown = -1,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// The typed failure of the last read that returned `null`/empty. See `Db`.
|
|
138
|
+
// @ts-ignore: decorator
|
|
139
|
+
@lazy
|
|
140
|
+
var __toildbLastError: DbError = DbError.None;
|
|
141
|
+
|
|
142
|
+
/// Decode a raw host status into a typed failure. Non-negative, `-1` (buffer too
|
|
143
|
+
/// small) and `-2` (absent) are all `None`: only the `<= -1000` band is a failure.
|
|
144
|
+
function __toildbErrorOf(status: i32): DbError {
|
|
145
|
+
if (status >= -2) return DbError.None;
|
|
146
|
+
const code = -status - 1000;
|
|
147
|
+
if (code < 0) return DbError.Unknown;
|
|
148
|
+
switch (code) {
|
|
149
|
+
case 1: return DbError.InvalidHandle;
|
|
150
|
+
case 2: return DbError.TenantMismatch;
|
|
151
|
+
case 3: return DbError.AlreadyExists;
|
|
152
|
+
case 4: return DbError.Conflict;
|
|
153
|
+
case 5: return DbError.Unsupported;
|
|
154
|
+
case 6: return DbError.Codec;
|
|
155
|
+
case 7: return DbError.Backend;
|
|
156
|
+
case 10: return DbError.OpNotAllowedForFamily;
|
|
157
|
+
case 11: return DbError.OpNotAllowedInKind;
|
|
158
|
+
case 20: return DbError.OpBudget;
|
|
159
|
+
case 21: return DbError.ByteBudget;
|
|
160
|
+
case 22: return DbError.DepthBudget;
|
|
161
|
+
case 25: return DbError.QuotaExceeded;
|
|
162
|
+
case 30: return DbError.FillThrottled;
|
|
163
|
+
case 31: return DbError.Unavailable;
|
|
164
|
+
case 40: return DbError.HotPartition;
|
|
165
|
+
case 41: return DbError.ColdStorm;
|
|
166
|
+
case 70: return DbError.SchemaUnavailable;
|
|
167
|
+
case 90: return DbError.CatalogOutsideAdmin;
|
|
168
|
+
default: return DbError.Unknown;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// Record the outcome of a read that is about to return `null`. An absent row
|
|
173
|
+
/// records `None`, so `null` + `None` means "not found" and `null` + anything else
|
|
174
|
+
/// means "failed".
|
|
175
|
+
function __toildbMiss(status: i32): void {
|
|
176
|
+
__toildbLastError = __toildbErrorOf(status);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// ToilDB diagnostics. Read `lastError()` immediately after a read returns `null`
|
|
180
|
+
/// to tell an absent row from a failed one; the next read overwrites it.
|
|
181
|
+
///
|
|
182
|
+
/// ```ts
|
|
183
|
+
/// const u = App.users.get(id);
|
|
184
|
+
/// if (u == null && Db.isRetryable(Db.lastError())) {
|
|
185
|
+
/// // The row may well exist: the store is briefly unavailable. Retry or 503.
|
|
186
|
+
/// }
|
|
187
|
+
/// ```
|
|
188
|
+
///
|
|
189
|
+
/// ONLY `Documents.get`, `Documents.getDelete`, `View.get` and `Unique.lookup`
|
|
190
|
+
/// record into this. Every other ToilDB op either succeeds or traps. The `Analytics`
|
|
191
|
+
/// reads also answer `null`, but on their own `-2 absent` / `-3 forbidden` statuses
|
|
192
|
+
/// rather than a `TDLnnn`, so they leave `lastError()` untouched: do not consult it
|
|
193
|
+
/// after one, or you will read a stale value from an earlier ToilDB read.
|
|
194
|
+
@global
|
|
195
|
+
export class Db {
|
|
196
|
+
/// The failure recorded by the most recent read that returned `null`. `None`
|
|
197
|
+
/// when that read simply found nothing.
|
|
198
|
+
static lastError(): DbError {
|
|
199
|
+
return __toildbLastError;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Decode a raw negative host status into its typed failure.
|
|
203
|
+
static errorOf(status: i32): DbError {
|
|
204
|
+
return __toildbErrorOf(status);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Whether `err` is a transient availability signal, so the same read may well
|
|
208
|
+
/// succeed later. These are the ONLY failures a read can hand back: everything
|
|
209
|
+
/// else traps the request before the guest resumes.
|
|
210
|
+
static isRetryable(err: DbError): bool {
|
|
211
|
+
return (
|
|
212
|
+
err == DbError.FillThrottled ||
|
|
213
|
+
err == DbError.Unavailable ||
|
|
214
|
+
err == DbError.HotPartition ||
|
|
215
|
+
err == DbError.ColdStorm
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
75
220
|
/// A mutable keyed-entity collection (spec 7.1). `V` is the `@data` value type,
|
|
76
221
|
/// `K` the `@data` key type.
|
|
77
222
|
@global
|
|
@@ -98,7 +243,7 @@ export class Documents<K, V> {
|
|
|
98
243
|
get(key: K): V | null {
|
|
99
244
|
const kb = key.encode();
|
|
100
245
|
const status = toildbHost.get(this.__handle, kb.dataStart, kb.byteLength);
|
|
101
|
-
if (status < 0) return null;
|
|
246
|
+
if (status < 0) { __toildbMiss(status); return null; }
|
|
102
247
|
__toildbResetMigrated();
|
|
103
248
|
const v = instantiate<V>();
|
|
104
249
|
v.decodeInto(__toildbTake(status));
|
|
@@ -226,7 +371,7 @@ export class Documents<K, V> {
|
|
|
226
371
|
getDelete(key: K): V | null {
|
|
227
372
|
const kb = key.encode();
|
|
228
373
|
const status = toildbHost.getDelete(this.__handle, kb.dataStart, kb.byteLength, 0);
|
|
229
|
-
if (status < 0) return null;
|
|
374
|
+
if (status < 0) { __toildbMiss(status); return null; }
|
|
230
375
|
const v = instantiate<V>();
|
|
231
376
|
v.decodeInto(__toildbTake(status));
|
|
232
377
|
return v;
|
|
@@ -249,7 +394,7 @@ export class View<K, V> {
|
|
|
249
394
|
get(key: K): V | null {
|
|
250
395
|
const kb = key.encode();
|
|
251
396
|
const status = toildbHost.viewGet(this.__handle, kb.dataStart, kb.byteLength);
|
|
252
|
-
if (status < 0) return null;
|
|
397
|
+
if (status < 0) { __toildbMiss(status); return null; }
|
|
253
398
|
const v = instantiate<V>();
|
|
254
399
|
v.decodeInto(__toildbTake(status));
|
|
255
400
|
return v;
|
|
@@ -295,7 +440,7 @@ export class Unique<K, V> {
|
|
|
295
440
|
lookup(key: K): V | null {
|
|
296
441
|
const kb = key.encode();
|
|
297
442
|
const status = toildbHost.uniqueLookup(this.__handle, kb.dataStart, kb.byteLength);
|
|
298
|
-
if (status < 0) return null;
|
|
443
|
+
if (status < 0) { __toildbMiss(status); return null; }
|
|
299
444
|
const v = instantiate<V>();
|
|
300
445
|
v.decodeInto(__toildbTake(status));
|
|
301
446
|
return v;
|
|
@@ -531,6 +676,14 @@ export enum MetricId {
|
|
|
531
676
|
/// section.
|
|
532
677
|
export const METRIC_COUNTERS: i32 = 42;
|
|
533
678
|
|
|
679
|
+
/// The sustained-rate window (seconds): requests are metered over a 3-minute ROLLING window. A plan's
|
|
680
|
+
/// sustained request rate `X` (rps) permits `X * BURST_WINDOW_SECS` requests across it. Instantaneous
|
|
681
|
+
/// spikes ABOVE the sustained rate are NOT tier-capped (only the box firewall's global pps ceiling, the
|
|
682
|
+
/// DDoS backstop, applies); throttling (HTTP 429) begins only once the window's allowance is spent.
|
|
683
|
+
/// The window slides continuously rather than resetting on a boundary, so a spend does not become free
|
|
684
|
+
/// all at once: it ages out gradually over the following 3 minutes.
|
|
685
|
+
export const BURST_WINDOW_SECS: i32 = 180;
|
|
686
|
+
|
|
534
687
|
/// A dashboard time range for `Analytics.series`. Short ranges (1h/6h) read the per-MINUTE ring; the long
|
|
535
688
|
/// ranges (60d/90d) read the per-DAY ring (90-day retention); the rest read the per-HOUR ring (30-day).
|
|
536
689
|
/// Append-only: never renumber (D60/D90 were added after D30, keeping the wire ids stable).
|
|
@@ -556,11 +709,14 @@ export class TenantStats {
|
|
|
556
709
|
/// Live gauges (current level, not a total).
|
|
557
710
|
connectedStreams: u64 = 0;
|
|
558
711
|
committedMemory: u64 = 0;
|
|
559
|
-
/// Request
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
712
|
+
/// Request meters (fleet-global, across every edge location) against the plan (cap 0 = unmetered).
|
|
713
|
+
/// Burst: requests in the trailing 3-minute ROLLING window vs its allowance
|
|
714
|
+
/// (`sustained_rps * BURST_WINDOW_SECS`). 30d: requests in the current 30-day bucket vs the quota
|
|
715
|
+
/// (the quota still tumbles: it is a billing period, not a rate).
|
|
716
|
+
reqBurstUsed: u64 = 0;
|
|
717
|
+
reqBurstCap: u64 = 0;
|
|
718
|
+
req30dUsed: u64 = 0;
|
|
719
|
+
req30dCap: u64 = 0;
|
|
564
720
|
/// LIVE per-second gauges (rates), computed edge-side from the last completed minute bucket so the
|
|
565
721
|
/// guest is STATELESS (no client diffing of two snapshots). Idle -> 0.0. `f64` (a rate is fractional
|
|
566
722
|
/// and bounded; f64 cannot overflow for any real value).
|
|
@@ -636,6 +792,13 @@ export class TenantStats {
|
|
|
636
792
|
const total = hits + this.cacheMisses;
|
|
637
793
|
return total > 0 ? <f64>hits / <f64>total : 0.0;
|
|
638
794
|
}
|
|
795
|
+
|
|
796
|
+
/// The plan's SUSTAINED request rate (requests/second), derived from the 3-minute burst cap
|
|
797
|
+
/// (`reqBurstCap / BURST_WINDOW_SECS`). 0.0 means unmetered. Spikes above this rate are allowed;
|
|
798
|
+
/// throttling only starts once `reqBurstUsed` passes `reqBurstCap` in the current bucket.
|
|
799
|
+
get sustainedRpsCap(): f64 {
|
|
800
|
+
return this.reqBurstCap == 0 ? 0.0 : <f64>this.reqBurstCap / <f64>BURST_WINDOW_SECS;
|
|
801
|
+
}
|
|
639
802
|
}
|
|
640
803
|
|
|
641
804
|
/// One metric's time series for a range: `points` oldest→newest (per-bucket totals, `u64` — non-negative),
|
|
@@ -678,7 +841,7 @@ export class Analytics {
|
|
|
678
841
|
const r = new DataReader(buf);
|
|
679
842
|
const stats = new TenantStats();
|
|
680
843
|
const version = r.readU16();
|
|
681
|
-
if (version <
|
|
844
|
+
if (version < 3) return stats; // pre-v3 edge: refuse rather than mis-decode (clean break, no v2)
|
|
682
845
|
stats.nowMs = r.readU64();
|
|
683
846
|
const count = <i32>r.readU32();
|
|
684
847
|
for (let i: i32 = 0; i < count && r.ok; i++) {
|
|
@@ -688,10 +851,10 @@ export class Analytics {
|
|
|
688
851
|
}
|
|
689
852
|
stats.connectedStreams = r.readU64();
|
|
690
853
|
stats.committedMemory = r.readU64();
|
|
691
|
-
stats.
|
|
692
|
-
stats.
|
|
693
|
-
stats.
|
|
694
|
-
stats.
|
|
854
|
+
stats.reqBurstUsed = r.readU64();
|
|
855
|
+
stats.reqBurstCap = r.readU64();
|
|
856
|
+
stats.req30dUsed = r.readU64();
|
|
857
|
+
stats.req30dCap = r.readU64();
|
|
695
858
|
// LIVE per-second rates (f64), appended after the windows. VERSION-GATE by LENGTH: a pre-rates
|
|
696
859
|
// (shorter) frame from an older edge exhausts the buffer here, so `readF64` returns 0.0 for each
|
|
697
860
|
// missing rate (DataReader is bounds-checked). No frame-version bump needed.
|
|
@@ -709,7 +872,7 @@ export class Analytics {
|
|
|
709
872
|
const r = new DataReader(buf);
|
|
710
873
|
const out = new Series();
|
|
711
874
|
const version = r.readU16();
|
|
712
|
-
if (version <
|
|
875
|
+
if (version < 3) return out; // pre-v3 edge: refuse rather than mis-decode (clean break, no v2)
|
|
713
876
|
out.metric = <MetricId>r.readU16();
|
|
714
877
|
out.bucketSecs = r.readU32();
|
|
715
878
|
out.headMs = r.readU64();
|
|
@@ -333,9 +333,14 @@ declare const enum AnalyticsRange {
|
|
|
333
333
|
H1 = 0, H6 = 1, H12 = 2, H24 = 3, D3 = 4, D7 = 5, D14 = 6, D30 = 7, D60 = 8, D90 = 9,
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
+
/** The sustained-rate window (seconds): requests are metered over a 3-minute ROLLING window, so a plan's
|
|
337
|
+
* sustained rate `X` (rps) permits `X * BURST_WINDOW_SECS` requests across it. */
|
|
338
|
+
declare const BURST_WINDOW_SECS: i32;
|
|
339
|
+
|
|
336
340
|
/** One tenant's analytics snapshot: lifetime totals (indexed by `MetricId`, no string keys), the two
|
|
337
|
-
* live gauge levels, and the request
|
|
338
|
-
* value by typed getter (`stats.requests`) or by id
|
|
341
|
+
* live gauge levels, and the request meters (burst 3-min bucket + 30-day quota, each with its cap;
|
|
342
|
+
* cap 0 = unmetered). Read a value by typed getter (`stats.requests`) or by id
|
|
343
|
+
* (`stats.metric(MetricId.Requests)`). */
|
|
339
344
|
declare class TenantStats {
|
|
340
345
|
metric(id: MetricId): u64;
|
|
341
346
|
readonly requests: u64;
|
|
@@ -385,10 +390,15 @@ declare class TenantStats {
|
|
|
385
390
|
readonly cacheRatio: f64;
|
|
386
391
|
connectedStreams: u64;
|
|
387
392
|
committedMemory: u64;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
393
|
+
/** Fleet-global requests in the trailing 3-minute ROLLING window, and its allowance
|
|
394
|
+
* (`sustained_rps * BURST_WINDOW_SECS`; 0 = unmetered). Spikes are NOT tier-capped. */
|
|
395
|
+
reqBurstUsed: u64;
|
|
396
|
+
reqBurstCap: u64;
|
|
397
|
+
/** Fleet-global requests in the current 30-day tumbling bucket, and the 30-day quota (0 = unmetered). */
|
|
398
|
+
req30dUsed: u64;
|
|
399
|
+
req30dCap: u64;
|
|
400
|
+
/** Derived: the plan's SUSTAINED request rate (`reqBurstCap / BURST_WINDOW_SECS`), 0.0 when unmetered. */
|
|
401
|
+
readonly sustainedRpsCap: f64;
|
|
392
402
|
/** LIVE per-second gauges (rates), computed edge-side from the last completed minute bucket (idle 0.0),
|
|
393
403
|
* so no client diffing is needed. `f64` cannot overflow for any real value. */
|
|
394
404
|
readonly rps: f64;
|
|
@@ -431,6 +441,120 @@ declare class Analytics {
|
|
|
431
441
|
static listSites(cursor?: string, limit?: i32): SiteList;
|
|
432
442
|
}
|
|
433
443
|
|
|
444
|
+
/**
|
|
445
|
+
* A typed ToilDB failure (`TDLnnn`, spec 27.1). A read that returns `null` either
|
|
446
|
+
* found nothing (`Db.lastError() == DbError.None`) or hit one of the four RETRYABLE
|
|
447
|
+
* availability signals. A HARD FAULT never reaches guest code: the host traps the
|
|
448
|
+
* request rather than hand back a status the ABI would mistake for a benign result.
|
|
449
|
+
*/
|
|
450
|
+
declare const enum DbError {
|
|
451
|
+
None = 0,
|
|
452
|
+
/** TDL001. Hard fault: traps. */ InvalidHandle = 1,
|
|
453
|
+
/** TDL002. Hard fault: traps. */ TenantMismatch = 2,
|
|
454
|
+
/** TDL003. A write outcome. */ AlreadyExists = 3,
|
|
455
|
+
/** TDL004. A write outcome. */ Conflict = 4,
|
|
456
|
+
/** TDL005. Hard fault: traps. */ Unsupported = 5,
|
|
457
|
+
/** TDL006. Hard fault: traps. */ Codec = 6,
|
|
458
|
+
/** TDL007. Hard fault: traps. */ Backend = 7,
|
|
459
|
+
/** TDL010. Hard fault: traps. */ OpNotAllowedForFamily = 10,
|
|
460
|
+
/** TDL011. Hard fault: traps. */ OpNotAllowedInKind = 11,
|
|
461
|
+
/** TDL020. Hard fault: traps. */ OpBudget = 20,
|
|
462
|
+
/** TDL021. Hard fault: traps. */ ByteBudget = 21,
|
|
463
|
+
/** TDL022. Hard fault: traps. */ DepthBudget = 22,
|
|
464
|
+
/** TDL025. Hard fault: traps, so a guest never observes it. */ QuotaExceeded = 25,
|
|
465
|
+
/** TDL030. Retryable. */ FillThrottled = 30,
|
|
466
|
+
/** TDL031. Retryable. */ Unavailable = 31,
|
|
467
|
+
/** TDL040. Retryable. */ HotPartition = 40,
|
|
468
|
+
/** TDL041. Retryable. */ ColdStorm = 41,
|
|
469
|
+
/** TDL070. Hard fault: traps. */ SchemaUnavailable = 70,
|
|
470
|
+
/** TDL090. Hard fault: traps. */ CatalogOutsideAdmin = 90,
|
|
471
|
+
/** A negative status carrying no known `TDLnnn`. */ Unknown = -1,
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/** ToilDB diagnostics: tell an absent row from a failed read. */
|
|
475
|
+
declare class Db {
|
|
476
|
+
/** The failure recorded by the most recent read that returned `null`. */
|
|
477
|
+
static lastError(): DbError;
|
|
478
|
+
/** Decode a raw negative host status into its typed failure. */
|
|
479
|
+
static errorOf(status: i32): DbError;
|
|
480
|
+
/** Whether `err` is a transient availability signal worth retrying. */
|
|
481
|
+
static isRetryable(err: DbError): bool;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// The daemon (L4 cold box) scheduler surface is an ambient global, like the
|
|
485
|
+
// ToilDB handles above (`std/assembly/daemon`). It resolves ONLY inside a
|
|
486
|
+
// `@daemon` cold box: its host imports live in the `daemon` wasm module, which a
|
|
487
|
+
// request handler's module does not get.
|
|
488
|
+
|
|
489
|
+
/** A typed daemon failure. Positive values are the host's u16 subsystem codes. */
|
|
490
|
+
declare const enum DaemonError {
|
|
491
|
+
None = 0,
|
|
492
|
+
/** This node does not hold the lease; the host refused without acting. */
|
|
493
|
+
NotLeader = 0x0401,
|
|
494
|
+
/** The lease was lost while the call was in flight. */
|
|
495
|
+
LeaseLost = 0x0402,
|
|
496
|
+
/** The outbound request did not complete (SSRF guard, timeout, transport). */
|
|
497
|
+
CallFailed = 0x0405,
|
|
498
|
+
/** The response did not fit the buffer. The call ALREADY happened: do not retry. */
|
|
499
|
+
ResponseTooLarge = -1,
|
|
500
|
+
/** A request over a host cap (nothing sent), or a malformed response envelope. */
|
|
501
|
+
BadEnvelope = -2,
|
|
502
|
+
/** A negative status outside every band above. */
|
|
503
|
+
Unknown = -3,
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** One `name: value` header of an outbound request or an inbound response. */
|
|
507
|
+
declare class DaemonHttpHeader {
|
|
508
|
+
name: string;
|
|
509
|
+
value: string;
|
|
510
|
+
constructor(name: string, value: string);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** An outbound HTTP request. `url` must be an absolute `http(s)` URL. */
|
|
514
|
+
declare class DaemonHttpRequest {
|
|
515
|
+
method: string;
|
|
516
|
+
url: string;
|
|
517
|
+
headers: DaemonHttpHeader[];
|
|
518
|
+
body: Uint8Array;
|
|
519
|
+
constructor(method?: string, url?: string);
|
|
520
|
+
/** Append a header. Returns `this` so calls chain. */
|
|
521
|
+
header(name: string, value: string): DaemonHttpRequest;
|
|
522
|
+
/** The wire envelope, or `null` when a field breaks a host cap. */
|
|
523
|
+
encode(): Uint8Array | null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/** An inbound HTTP response. `status` is 0 when the host failed before a reply. */
|
|
527
|
+
declare class DaemonHttpResponse {
|
|
528
|
+
status: u16;
|
|
529
|
+
headers: DaemonHttpHeader[];
|
|
530
|
+
body: Uint8Array;
|
|
531
|
+
/** A header's value (case-sensitive, first match), or `null`. */
|
|
532
|
+
header(name: string): string | null;
|
|
533
|
+
/** The body decoded as UTF-8. */
|
|
534
|
+
text(): string;
|
|
535
|
+
static decode(buf: Uint8Array, len: i32): DaemonHttpResponse | null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** The daemon scheduler surface, available inside a `@daemon` class. */
|
|
539
|
+
declare class Daemon {
|
|
540
|
+
/** Whether THIS node currently holds the lease. A snapshot: re-check it. */
|
|
541
|
+
static isLeader(): bool;
|
|
542
|
+
/** The monotonic fencing token, or -1 when this node does not hold the lease. */
|
|
543
|
+
static epoch(): i64;
|
|
544
|
+
/** The number of registered `@scheduled` tasks. */
|
|
545
|
+
static taskCount(): i32;
|
|
546
|
+
/** The next fire time for `taskId` in epoch ms, or -1 when it never fires. */
|
|
547
|
+
static nextFireMs(taskId: i32): i64;
|
|
548
|
+
/** Let the host observe a lost lease. `None` means still leader. */
|
|
549
|
+
static yieldNow(): DaemonError;
|
|
550
|
+
/** Park for `ms` (host-clamped to 3s). `LeaseLost` means the task should stop. */
|
|
551
|
+
static sleep(ms: i64): DaemonError;
|
|
552
|
+
/** Make an outbound HTTP request, or `null` on failure (see `lastError()`). */
|
|
553
|
+
static httpCall(request: DaemonHttpRequest, responseCap?: i32): DaemonHttpResponse | null;
|
|
554
|
+
/** The failure recorded by the most recent `Daemon` call. */
|
|
555
|
+
static lastError(): DaemonError;
|
|
556
|
+
}
|
|
557
|
+
|
|
434
558
|
// Big integers, native globals implemented in std/assembly/bignum. The
|
|
435
559
|
// arithmetic/bitwise/comparison operators
|
|
436
560
|
// (+ - * / % & | ^ << >> == != < > <= >=) are operator overloads resolved by
|