toilscript 0.1.58 → 0.1.59

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "imports": {
3
- "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.58/dist/toilscript.js",
4
- "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.58/dist/cli.js",
3
+ "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.59/dist/toilscript.js",
4
+ "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.59/dist/cli.js",
5
5
  "binaryen": "https://cdn.jsdelivr.net/npm/binaryen@130.0.0-nightly.20260609/index.js",
6
6
  "long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
7
7
  }
package/dist/web.js CHANGED
@@ -1,8 +1,8 @@
1
- var ASSEMBLYSCRIPT_VERSION = "0.1.58";
1
+ var ASSEMBLYSCRIPT_VERSION = "0.1.59";
2
2
  var ASSEMBLYSCRIPT_IMPORTMAP = {
3
3
  "imports": {
4
- "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.58/dist/toilscript.js",
5
- "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.58/dist/cli.js",
4
+ "toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.59/dist/toilscript.js",
5
+ "toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.59/dist/cli.js",
6
6
  "binaryen": "https://cdn.jsdelivr.net/npm/binaryen@130.0.0-nightly.20260609/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.58",
11
+ "version": "0.1.59",
12
12
  "author": "Daniel Wirtz <dcode+assemblyscript@dcode.io>",
13
13
  "license": "Apache-2.0",
14
14
  "homepage": "https://github.com/dacely-cloud/toilscript",
@@ -531,6 +531,14 @@ export enum MetricId {
531
531
  /// section.
532
532
  export const METRIC_COUNTERS: i32 = 42;
533
533
 
534
+ /// The sustained-rate window (seconds): requests are metered over a 3-minute ROLLING window. A plan's
535
+ /// sustained request rate `X` (rps) permits `X * BURST_WINDOW_SECS` requests across it. Instantaneous
536
+ /// spikes ABOVE the sustained rate are NOT tier-capped (only the box firewall's global pps ceiling, the
537
+ /// DDoS backstop, applies); throttling (HTTP 429) begins only once the window's allowance is spent.
538
+ /// The window slides continuously rather than resetting on a boundary, so a spend does not become free
539
+ /// all at once: it ages out gradually over the following 3 minutes.
540
+ export const BURST_WINDOW_SECS: i32 = 180;
541
+
534
542
  /// A dashboard time range for `Analytics.series`. Short ranges (1h/6h) read the per-MINUTE ring; the long
535
543
  /// ranges (60d/90d) read the per-DAY ring (90-day retention); the rest read the per-HOUR ring (30-day).
536
544
  /// Append-only: never renumber (D60/D90 were added after D30, keeping the wire ids stable).
@@ -556,11 +564,14 @@ export class TenantStats {
556
564
  /// Live gauges (current level, not a total).
557
565
  connectedStreams: u64 = 0;
558
566
  committedMemory: u64 = 0;
559
- /// Request windows: current bucket usage + plan cap (0 = unlimited).
560
- reqMinuteUsed: u64 = 0;
561
- reqMinuteCap: u64 = 0;
562
- reqDayUsed: u64 = 0;
563
- reqDayCap: u64 = 0;
567
+ /// Request meters (fleet-global, across every edge location) against the plan (cap 0 = unmetered).
568
+ /// Burst: requests in the trailing 3-minute ROLLING window vs its allowance
569
+ /// (`sustained_rps * BURST_WINDOW_SECS`). 30d: requests in the current 30-day bucket vs the quota
570
+ /// (the quota still tumbles: it is a billing period, not a rate).
571
+ reqBurstUsed: u64 = 0;
572
+ reqBurstCap: u64 = 0;
573
+ req30dUsed: u64 = 0;
574
+ req30dCap: u64 = 0;
564
575
  /// LIVE per-second gauges (rates), computed edge-side from the last completed minute bucket so the
565
576
  /// guest is STATELESS (no client diffing of two snapshots). Idle -> 0.0. `f64` (a rate is fractional
566
577
  /// and bounded; f64 cannot overflow for any real value).
@@ -636,6 +647,13 @@ export class TenantStats {
636
647
  const total = hits + this.cacheMisses;
637
648
  return total > 0 ? <f64>hits / <f64>total : 0.0;
638
649
  }
650
+
651
+ /// The plan's SUSTAINED request rate (requests/second), derived from the 3-minute burst cap
652
+ /// (`reqBurstCap / BURST_WINDOW_SECS`). 0.0 means unmetered. Spikes above this rate are allowed;
653
+ /// throttling only starts once `reqBurstUsed` passes `reqBurstCap` in the current bucket.
654
+ get sustainedRpsCap(): f64 {
655
+ return this.reqBurstCap == 0 ? 0.0 : <f64>this.reqBurstCap / <f64>BURST_WINDOW_SECS;
656
+ }
639
657
  }
640
658
 
641
659
  /// One metric's time series for a range: `points` oldest→newest (per-bucket totals, `u64` — non-negative),
@@ -678,7 +696,7 @@ export class Analytics {
678
696
  const r = new DataReader(buf);
679
697
  const stats = new TenantStats();
680
698
  const version = r.readU16();
681
- if (version < 2) return stats; // pre-v2 edge: refuse rather than mis-decode
699
+ if (version < 3) return stats; // pre-v3 edge: refuse rather than mis-decode (clean break, no v2)
682
700
  stats.nowMs = r.readU64();
683
701
  const count = <i32>r.readU32();
684
702
  for (let i: i32 = 0; i < count && r.ok; i++) {
@@ -688,10 +706,10 @@ export class Analytics {
688
706
  }
689
707
  stats.connectedStreams = r.readU64();
690
708
  stats.committedMemory = r.readU64();
691
- stats.reqMinuteUsed = r.readU64();
692
- stats.reqMinuteCap = r.readU64();
693
- stats.reqDayUsed = r.readU64();
694
- stats.reqDayCap = r.readU64();
709
+ stats.reqBurstUsed = r.readU64();
710
+ stats.reqBurstCap = r.readU64();
711
+ stats.req30dUsed = r.readU64();
712
+ stats.req30dCap = r.readU64();
695
713
  // LIVE per-second rates (f64), appended after the windows. VERSION-GATE by LENGTH: a pre-rates
696
714
  // (shorter) frame from an older edge exhausts the buffer here, so `readF64` returns 0.0 for each
697
715
  // missing rate (DataReader is bounds-checked). No frame-version bump needed.
@@ -709,7 +727,7 @@ export class Analytics {
709
727
  const r = new DataReader(buf);
710
728
  const out = new Series();
711
729
  const version = r.readU16();
712
- if (version < 2) return out;
730
+ if (version < 3) return out; // pre-v3 edge: refuse rather than mis-decode (clean break, no v2)
713
731
  out.metric = <MetricId>r.readU16();
714
732
  out.bucketSecs = r.readU32();
715
733
  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 windows (current usage + plan cap; cap 0 = unlimited). Read a
338
- * value by typed getter (`stats.requests`) or by id (`stats.metric(MetricId.Requests)`). */
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
- reqMinuteUsed: u64;
389
- reqMinuteCap: u64;
390
- reqDayUsed: u64;
391
- reqDayCap: u64;
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;