@nimbus-sh/platform 0.6.0 → 0.7.0
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/composition.d.ts +2 -1
- package/dist/composition.d.ts.map +1 -1
- package/dist/composition.js +33 -1
- package/dist/diag-counters.d.ts +0 -8
- package/dist/diag-counters.d.ts.map +1 -1
- package/dist/diag-counters.js +0 -6
- package/dist/heap-estimate.d.ts +0 -6
- package/dist/heap-estimate.d.ts.map +1 -1
- package/dist/heap-estimate.js +0 -2
- package/dist/heavy-alloc-coord.d.ts +0 -13
- package/dist/heavy-alloc-coord.d.ts.map +1 -1
- package/dist/heavy-alloc-coord.js +2 -19
- package/dist/limits.d.ts +1 -1
- package/dist/limits.js +1 -1
- package/dist/oom-classify.d.ts.map +1 -1
- package/dist/oom-classify.js +6 -5
- package/dist/tracing.d.ts +65 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +107 -0
- package/dist/weighted-credit-pool.d.ts +0 -13
- package/dist/weighted-credit-pool.d.ts.map +1 -1
- package/dist/weighted-credit-pool.js +6 -54
- package/package.json +2 -2
- package/src/composition.ts +34 -1
- package/src/diag-counters.ts +0 -13
- package/src/heap-estimate.ts +0 -8
- package/src/heavy-alloc-coord.ts +1 -26
- package/src/limits.ts +1 -1
- package/src/oom-classify.ts +6 -5
- package/src/tracing.ts +129 -0
- package/src/weighted-credit-pool.ts +6 -61
package/dist/composition.d.ts
CHANGED
|
@@ -66,7 +66,8 @@ export declare const DEFAULT_HOST_DISPATCH_METHOD = "supervisorOp";
|
|
|
66
66
|
* Compose once per isolate. A second call with the same values is a no-op;
|
|
67
67
|
* a second call with different values throws, naming both, so an embedder
|
|
68
68
|
* whose composition lost to an earlier import learns it at startup rather
|
|
69
|
-
* than from a facet that reached the wrong host.
|
|
69
|
+
* than from a facet that reached the wrong host. A host without
|
|
70
|
+
* `enhanced_error_serialization` is refused.
|
|
70
71
|
*/
|
|
71
72
|
export declare function composeFabric(value: FabricComposition): void;
|
|
72
73
|
export declare function adoptCtxExports(value: CtxExports): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../src/composition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC;AAC7F,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC;AAC/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEpF,MAAM,WAAW,iBAAiB;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AACvD,eAAO,MAAM,4BAA4B,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../src/composition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC;AAC7F,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC;AAC/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEpF,MAAM,WAAW,iBAAiB;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AACvD,eAAO,MAAM,4BAA4B,iBAAiB,CAAC;AAmC3D;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAoB5D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAEvD;AAED,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD;AAED,wBAAgB,oBAAoB,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,qBAAoC,GAAG,yBAAyB,GAAG,IAAI,CAMrI;AAED,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAExD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,IAAI,SAAS,GAAG,IAAI,CAO5C;AAED,wBAAgB,mBAAmB,IAAI,mBAAmB,CASzD"}
|
package/dist/composition.js
CHANGED
|
@@ -2,13 +2,45 @@ export const DEFAULT_HOST_NAMESPACE = 'NIMBUS_SESSION';
|
|
|
2
2
|
export const DEFAULT_HOST_DISPATCH_METHOD = 'supervisorOp';
|
|
3
3
|
let composition = null;
|
|
4
4
|
let ctxExports = null;
|
|
5
|
+
/**
|
|
6
|
+
* A program's filesystem errors reach it across workerd RPC, from the host
|
|
7
|
+
* that answers its supervisor binding, and the program branches on the POSIX
|
|
8
|
+
* `code` each one carries. workerd carries an error's own properties across
|
|
9
|
+
* RPC only when the sending AND the receiving isolate have
|
|
10
|
+
* `enhanced_error_serialization`, on by compatibility date from 2026-04-21:
|
|
11
|
+
* the serializer writes a native error as a host object with its own
|
|
12
|
+
* properties only under the flag (src/workerd/jsg/ser.c++,
|
|
13
|
+
* Serializer::IsHostObject), and the receiver reads that detail back only
|
|
14
|
+
* under it (src/workerd/jsg/util.c++, decodeTunneledException); see
|
|
15
|
+
* https://developers.cloudflare.com/workers/runtime-apis/rpc/error-handling/.
|
|
16
|
+
* Programs load at CF_COMPAT_DATE, past it. The host runs at its embedder's
|
|
17
|
+
* date, and below it every ENOENT would reach every program as a bare message
|
|
18
|
+
* it maps to EIO. So composing refuses such a host, wherever it composes
|
|
19
|
+
* (module scope: the Worker fails at startup; NimbusWorkspace.create: that
|
|
20
|
+
* call throws), and names both fixes: the flag alone keeps
|
|
21
|
+
* every other behavior of the embedder's date. Off workerd (bun, node) there
|
|
22
|
+
* is no RPC hop and no `Cloudflare` global to read.
|
|
23
|
+
*/
|
|
24
|
+
function requireEnhancedErrorSerialization() {
|
|
25
|
+
// `typeof` of an undeclared global is 'undefined', not a ReferenceError.
|
|
26
|
+
if (typeof Cloudflare === 'undefined')
|
|
27
|
+
return;
|
|
28
|
+
if (Cloudflare.compatibilityFlags.enhanced_error_serialization === true)
|
|
29
|
+
return;
|
|
30
|
+
throw new Error('fabric: this Worker runs without the enhanced_error_serialization compatibility flag, so the '
|
|
31
|
+
+ "code on a program's filesystem error (ENOENT, EEXIST, ...) would not survive the RPC back "
|
|
32
|
+
+ 'to it. Add "enhanced_error_serialization" to compatibility_flags in your wrangler config, '
|
|
33
|
+
+ 'or set compatibility_date to 2026-04-21 or later.');
|
|
34
|
+
}
|
|
5
35
|
/**
|
|
6
36
|
* Compose once per isolate. A second call with the same values is a no-op;
|
|
7
37
|
* a second call with different values throws, naming both, so an embedder
|
|
8
38
|
* whose composition lost to an earlier import learns it at startup rather
|
|
9
|
-
* than from a facet that reached the wrong host.
|
|
39
|
+
* than from a facet that reached the wrong host. A host without
|
|
40
|
+
* `enhanced_error_serialization` is refused.
|
|
10
41
|
*/
|
|
11
42
|
export function composeFabric(value) {
|
|
43
|
+
requireEnhancedErrorSerialization();
|
|
12
44
|
if (!composition) {
|
|
13
45
|
composition = value;
|
|
14
46
|
return;
|
package/dist/diag-counters.d.ts
CHANGED
|
@@ -75,12 +75,6 @@ export interface DiagCounters {
|
|
|
75
75
|
* Surfaced as `breakdown.prefetchCacheBytes`. A gauge, not a delta.
|
|
76
76
|
*/
|
|
77
77
|
prefetchCacheBytes: number;
|
|
78
|
-
/**
|
|
79
|
-
* Bytes retained by the FacetManager's ESM→CJS transform cache, bounded by
|
|
80
|
-
* ESM_TRANSFORM_CACHE_MAX_BYTES. Surfaced as `breakdown.transformCacheBytes`.
|
|
81
|
-
* A gauge, not a delta.
|
|
82
|
-
*/
|
|
83
|
-
transformCacheBytes: number;
|
|
84
78
|
/** Install-facet counters. Populated by npm-installer after a
|
|
85
79
|
* successful batch-facet dispatch returns. Confirms the install ran
|
|
86
80
|
* in the facet (tarballsCompleted > 0) and surfaces the
|
|
@@ -181,8 +175,6 @@ export declare function prefetchBundleEnd(bytes: number): void;
|
|
|
181
175
|
* owns the number and reports it whenever it admits or evicts an entry.
|
|
182
176
|
*/
|
|
183
177
|
export declare function setPrefetchCacheBytes(bytes: number): void;
|
|
184
|
-
/** Publish the transform cache's live retained total; same contract as above. */
|
|
185
|
-
export declare function setTransformCacheBytes(bytes: number): void;
|
|
186
178
|
/** Fold facet-returned counters into the supervisor's diag state.
|
|
187
179
|
* Called by npm-installer after the batch-facet returns; aggregates
|
|
188
180
|
* rather than replaces so multiple install runs in the same DO
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diag-counters.d.ts","sourceRoot":"","sources":["../src/diag-counters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;;;;;;;;;;;;;;OAiBG;IACH,uBAAuB,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B
|
|
1
|
+
{"version":3,"file":"diag-counters.d.ts","sourceRoot":"","sources":["../src/diag-counters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;;;;;;;;;;;;;;OAiBG;IACH,uBAAuB,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;qCAGiC;IACjC,YAAY,EAAE;QACZ,wEAAwE;QACxE,iBAAiB,EAAE,MAAM,CAAC;QAC1B,sEAAsE;QACtE,sBAAsB,EAAE,MAAM,CAAC;QAC/B,2EAA2E;QAC3E,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF;yEACqE;IACrE,cAAc,EAAE;QACd,4DAA4D;QAC5D,SAAS,EAAE,MAAM,CAAC;QAClB,8DAA8D;QAC9D,gBAAgB,EAAE,MAAM,CAAC;QACzB,8DAA8D;QAC9D,MAAM,EAAE,MAAM,CAAC;QACf,uEAAuE;QACvE,OAAO,EAAE,MAAM,CAAC;QAChB;;;wBAGgB;QAChB,aAAa,EAAE,MAAM,CAAC;QACtB;;;;;;;gEAOwD;QACxD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACxC,CAAC;IACF;2EACuE;IACvE,EAAE,EAAE;QACF;8BACsB;QACtB,wBAAwB,EAAE,MAAM,CAAC;QACjC;6CACqC;QACrC,0BAA0B,EAAE,MAAM,CAAC;QACnC,8CAA8C;QAC9C,0BAA0B,EAAE,MAAM,CAAC;QACnC,gDAAgD;QAChD,4BAA4B,EAAE,MAAM,CAAC;KACtC,CAAC;CACH;AA4BD,0EAA0E;AAC1E,wBAAgB,gBAAgB,IAAI,YAAY,CAE/C;AAED,6BAA6B;AAC7B,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI,CAErD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAInD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAOjD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIvD;AAED,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAKrD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAGzD;AAED;;;qDAGqD;AACrD,wBAAgB,0BAA0B,CAAC,CAAC,EAAE;IAC5C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAMP;AAED;;;;;aAKa;AACb,wBAAgB,sBAAsB,CAAC,CAAC,EAAE;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,GAAG,IAAI,CAiBP;AAED;;sCAEsC;AACtC,wBAAgB,oBAAoB,CAAC,CAAC,EAAE;IACtC,wBAAwB,EAAE,MAAM,CAAC;IACjC,0BAA0B,EAAE,MAAM,CAAC;IACnC,0BAA0B,EAAE,MAAM,CAAC;IACnC,4BAA4B,EAAE,MAAM,CAAC;CACtC,GAAG,IAAI,CAKP"}
|
package/dist/diag-counters.js
CHANGED
|
@@ -23,7 +23,6 @@ const _counters = {
|
|
|
23
23
|
inFlightRpcPayloadBytes: 0,
|
|
24
24
|
prefetchBundleBytes: 0,
|
|
25
25
|
prefetchCacheBytes: 0,
|
|
26
|
-
transformCacheBytes: 0,
|
|
27
26
|
installFacet: {
|
|
28
27
|
tarballsCompleted: 0,
|
|
29
28
|
cumulativeBytesDecoded: 0,
|
|
@@ -123,11 +122,6 @@ export function setPrefetchCacheBytes(bytes) {
|
|
|
123
122
|
const n = Number(bytes);
|
|
124
123
|
_counters.prefetchCacheBytes = Number.isFinite(n) && n > 0 ? n : 0;
|
|
125
124
|
}
|
|
126
|
-
/** Publish the transform cache's live retained total; same contract as above. */
|
|
127
|
-
export function setTransformCacheBytes(bytes) {
|
|
128
|
-
const n = Number(bytes);
|
|
129
|
-
_counters.transformCacheBytes = Number.isFinite(n) && n > 0 ? n : 0;
|
|
130
|
-
}
|
|
131
125
|
/** Fold facet-returned counters into the supervisor's diag state.
|
|
132
126
|
* Called by npm-installer after the batch-facet returns; aggregates
|
|
133
127
|
* rather than replaces so multiple install runs in the same DO
|
package/dist/heap-estimate.d.ts
CHANGED
|
@@ -180,12 +180,6 @@ export interface HeapBreakdown {
|
|
|
180
180
|
* the cache holds rather than a reservation it might use.
|
|
181
181
|
*/
|
|
182
182
|
prefetchCacheBytes: number;
|
|
183
|
-
/**
|
|
184
|
-
* Bytes retained by the FacetManager's ESM→CJS transform cache
|
|
185
|
-
* (diag-counters `transformCacheBytes`), the other cache that persists
|
|
186
|
-
* across execs. Bounded by ESM_TRANSFORM_CACHE_MAX_BYTES.
|
|
187
|
-
*/
|
|
188
|
-
transformCacheBytes: number;
|
|
189
183
|
/**
|
|
190
184
|
* Shared-budget occupancy not already represented by the named transient
|
|
191
185
|
* counters. This covers full-budget owners and keeps cross-DO module-local
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heap-estimate.d.ts","sourceRoot":"","sources":["../src/heap-estimate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wBAAwB,CAAC;AAEhC;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,sFAM1B,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAAO,CAAC;AAE7D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,SAAS,EAAE,aAAa,CAAC;IACzB;;;OAGG;IACH,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IACrC;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,gBAAgB,EAAE,+BAA+B,CAAC;CACnD;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,uBAAuB,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;sEAEkE;IAClE,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;OAYG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,
|
|
1
|
+
{"version":3,"file":"heap-estimate.d.ts","sourceRoot":"","sources":["../src/heap-estimate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wBAAwB,CAAC;AAEhC;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,sFAM1B,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAAO,CAAC;AAE7D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,SAAS,EAAE,aAAa,CAAC;IACzB;;;OAGG;IACH,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IACrC;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,gBAAgB,EAAE,+BAA+B,CAAC;CACnD;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,uBAAuB,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;sEAEkE;IAClE,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;OAYG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,4BAA4B,EAAE,MAAM,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAyBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAQnE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,EAAE,YAAY,EACf,GAAG,EAAE,aAAa,GACjB,YAAY,CAkDd"}
|
package/dist/heap-estimate.js
CHANGED
|
@@ -160,7 +160,6 @@ export function estimateSupervisorHeap(c, vfs) {
|
|
|
160
160
|
streamingBuffersBytes: c.inFlightRpcPayloadBytes,
|
|
161
161
|
prefetchBundleBytes: c.prefetchBundleBytes,
|
|
162
162
|
prefetchCacheBytes: c.prefetchCacheBytes,
|
|
163
|
-
transformCacheBytes: c.transformCacheBytes,
|
|
164
163
|
unattributedReservationBytes: Math.max(0, allocationBudget.current - transientAttributedBytes),
|
|
165
164
|
};
|
|
166
165
|
const estimatedBytes = breakdown.supervisorBaselineBytes +
|
|
@@ -170,7 +169,6 @@ export function estimateSupervisorHeap(c, vfs) {
|
|
|
170
169
|
breakdown.streamingBuffersBytes +
|
|
171
170
|
breakdown.prefetchBundleBytes +
|
|
172
171
|
breakdown.prefetchCacheBytes +
|
|
173
|
-
breakdown.transformCacheBytes +
|
|
174
172
|
breakdown.unattributedReservationBytes;
|
|
175
173
|
const percentOfCeiling = Math.round((estimatedBytes / SUPERVISOR_HEAP_CEILING_BYTES) * 1000) / 10;
|
|
176
174
|
return {
|
|
@@ -13,8 +13,6 @@ export interface SupervisorAllocationBudgetStats {
|
|
|
13
13
|
readonly current: number;
|
|
14
14
|
readonly peak: number;
|
|
15
15
|
readonly queued: number;
|
|
16
|
-
/** Credit held for an owner's whole lifetime; see `acquireResident`. */
|
|
17
|
-
readonly resident: number;
|
|
18
16
|
}
|
|
19
17
|
interface AllocationBudgetLifecycle {
|
|
20
18
|
onActive?: () => void;
|
|
@@ -53,12 +51,6 @@ export declare class SupervisorAllocationBudget {
|
|
|
53
51
|
* stops a large read overlapping a large write.
|
|
54
52
|
*/
|
|
55
53
|
acquireWithoutLifecycle(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease>;
|
|
56
|
-
/**
|
|
57
|
-
* Reserve bytes an owner holds for its whole lifetime, not for one
|
|
58
|
-
* operation. Recorded as a floor: a later claim larger than what remains
|
|
59
|
-
* around it is refused with both numbers rather than parked forever.
|
|
60
|
-
*/
|
|
61
|
-
acquireResidentBytes(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease>;
|
|
62
54
|
private _acquire;
|
|
63
55
|
}
|
|
64
56
|
/**
|
|
@@ -91,11 +83,6 @@ export declare function acquireSupervisorAllocation(bytes: number, signal?: Abor
|
|
|
91
83
|
* there to fill.
|
|
92
84
|
*/
|
|
93
85
|
export declare function acquireSupervisorReadAllocation(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease>;
|
|
94
|
-
/**
|
|
95
|
-
* Reserve bytes for the whole life of the owner that takes them — an image
|
|
96
|
-
* a pool keeps until it is disposed. See `acquireResidentBytes`.
|
|
97
|
-
*/
|
|
98
|
-
export declare function acquireResidentSupervisorAllocation(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease>;
|
|
99
86
|
/**
|
|
100
87
|
* Reserve the full budget for an allocation whose retained size is not known
|
|
101
88
|
* accurately enough to overlap safely with other heavy work.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heavy-alloc-coord.d.ts","sourceRoot":"","sources":["../src/heavy-alloc-coord.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC/B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"heavy-alloc-coord.d.ts","sourceRoot":"","sources":["../src/heavy-alloc-coord.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC/B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,yBAAyB;IACjC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,0BAA0B;IAWnC,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAX5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,MAAM,CAAS;IACvB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAK;gBAGlB,QAAQ,EAAE,MAAM,EACR,SAAS,GAAE,yBAA8B,EAC1D,OAAO,GAAE,yBAA8B;IAKzC,IAAI,KAAK,IAAI,+BAA+B,CAK3C;IAED;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAI3E;;;;;;;;OAQG;IACH,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAI7E,QAAQ;CAmCvB;AAED;;;GAGG;AACH,UAAU,aAAa;IACrB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAiBD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,IAAI,CAWlE;AAqCD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAMjF;AAED,wBAAgB,8BAA8B,IAAI,+BAA+B,CAEhF"}
|
|
@@ -55,18 +55,8 @@ export class SupervisorAllocationBudget {
|
|
|
55
55
|
acquireWithoutLifecycle(bytes, signal) {
|
|
56
56
|
return this._acquire(bytes, signal, false);
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* operation. Recorded as a floor: a later claim larger than what remains
|
|
61
|
-
* around it is refused with both numbers rather than parked forever.
|
|
62
|
-
*/
|
|
63
|
-
acquireResidentBytes(bytes, signal) {
|
|
64
|
-
return this._acquire(bytes, signal, true, true);
|
|
65
|
-
}
|
|
66
|
-
async _acquire(bytes, signal, drivesLifecycle, resident = false) {
|
|
67
|
-
const credit = resident
|
|
68
|
-
? await this.credits.acquireResident(bytes, signal)
|
|
69
|
-
: await this.credits.acquire(bytes, signal);
|
|
58
|
+
async _acquire(bytes, signal, drivesLifecycle) {
|
|
59
|
+
const credit = await this.credits.acquire(bytes, signal);
|
|
70
60
|
if (drivesLifecycle) {
|
|
71
61
|
this.lifecycleHolders++;
|
|
72
62
|
if (!this.active) {
|
|
@@ -178,13 +168,6 @@ export function acquireSupervisorAllocation(bytes, signal) {
|
|
|
178
168
|
export function acquireSupervisorReadAllocation(bytes, signal) {
|
|
179
169
|
return supervisorAllocationBudget.acquireWithoutLifecycle(bytes, signal);
|
|
180
170
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Reserve bytes for the whole life of the owner that takes them — an image
|
|
183
|
-
* a pool keeps until it is disposed. See `acquireResidentBytes`.
|
|
184
|
-
*/
|
|
185
|
-
export function acquireResidentSupervisorAllocation(bytes, signal) {
|
|
186
|
-
return supervisorAllocationBudget.acquireResidentBytes(bytes, signal);
|
|
187
|
-
}
|
|
188
171
|
/**
|
|
189
172
|
* Reserve the full budget for an allocation whose retained size is not known
|
|
190
173
|
* accurately enough to overlap safely with other heavy work.
|
package/dist/limits.d.ts
CHANGED
|
@@ -94,7 +94,7 @@ export declare const RESIDENT_KEEPALIVE_MS = 5000;
|
|
|
94
94
|
export declare const RESIDENT_KEEPALIVE_DETACHED_MS = 60000;
|
|
95
95
|
/**
|
|
96
96
|
* Bytes one hibernatable WebSocket attachment may serialize to. Verified in
|
|
97
|
-
* workerd source at the pinned version (v1.
|
|
97
|
+
* workerd source at the pinned version (v1.20260926.1,
|
|
98
98
|
* src/workerd/api/web-socket.h: `MAX_ATTACHMENT_SIZE = 1024 * 16`). The
|
|
99
99
|
* bound is on the SERIALIZED bytes — workerd re-serializes on every
|
|
100
100
|
* `serializeAttachment` call to check it — not on UTF-8 JSON text, so a
|
package/dist/limits.js
CHANGED
|
@@ -108,7 +108,7 @@ export const RESIDENT_KEEPALIVE_DETACHED_MS = 60_000;
|
|
|
108
108
|
// ── WebSocket attachment bound ──────────────────────────────────────────
|
|
109
109
|
/**
|
|
110
110
|
* Bytes one hibernatable WebSocket attachment may serialize to. Verified in
|
|
111
|
-
* workerd source at the pinned version (v1.
|
|
111
|
+
* workerd source at the pinned version (v1.20260926.1,
|
|
112
112
|
* src/workerd/api/web-socket.h: `MAX_ATTACHMENT_SIZE = 1024 * 16`). The
|
|
113
113
|
* bound is on the SERIALIZED bytes — workerd re-serializes on every
|
|
114
114
|
* `serializeAttachment` call to check it — not on UTF-8 JSON text, so a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oom-classify.d.ts","sourceRoot":"","sources":["../src/oom-classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,QAAA,MAAM,UAAU,kLAYN,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAEjD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAItD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"oom-classify.d.ts","sourceRoot":"","sources":["../src/oom-classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,QAAA,MAAM,UAAU,kLAYN,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAEjD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAItD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAmErD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAO1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEtD;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW;AACrB,8CAA8C;AAC5C,oBAAoB;AACtB,8CAA8C;GAC5C,iBAAiB;AACnB,uEAAuE;GACrE,eAAe;AACjB,wDAAwD;GACtD,gBAAgB;AAClB;kDACkD;GAChD,YAAY;AACd,kEAAkE;GAChE,WAAW,CAAC;AAEhB;2DAC2D;AAC3D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAK7D;AAmBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAsB1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiBpD"}
|
package/dist/oom-classify.js
CHANGED
|
@@ -125,11 +125,12 @@ export function classifyMessage(msg) {
|
|
|
125
125
|
// Subrequest cap (Cloudflare platform)
|
|
126
126
|
if (m.includes('too many subrequests'))
|
|
127
127
|
return 'subrequest_cap';
|
|
128
|
-
// Worker
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
|
|
128
|
+
// Dynamic Worker concurrency limit (Cloudflare platform): a Durable Object
|
|
129
|
+
// may have a fixed number of distinct Dynamic Workers with in-flight
|
|
130
|
+
// requests at once (fabric budgets.ts, DO_DYNAMIC_WORKER_LIMIT). Production
|
|
131
|
+
// words it "Dynamic worker concurrency limit exceeded: each request may
|
|
132
|
+
// have up to 10 concurrent dynamic worker invocations. …".
|
|
133
|
+
if (m.includes('dynamic worker concurrency limit exceeded'))
|
|
133
134
|
return 'dynamic_worker_cap';
|
|
134
135
|
// Memory exhaustion. workerd trace outcome `exceededMemory`; in-band it
|
|
135
136
|
// arrives as a `broken.exceededMemory` actor break, a "…exceeded [its]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tracing.ts — Workers custom spans, for modules that cannot import
|
|
3
|
+
* `cloudflare:workers`, as best-effort telemetry that never changes an
|
|
4
|
+
* outcome.
|
|
5
|
+
*
|
|
6
|
+
* The span API is `tracing` from `cloudflare:workers`
|
|
7
|
+
* (https://developers.cloudflare.com/workers/observability/traces/custom-spans/):
|
|
8
|
+
* `enterSpan(name, callback)` runs the callback in a span that is the active
|
|
9
|
+
* parent of every span and platform call made inside it — automatic RPC
|
|
10
|
+
* spans included — and ends it when the callback returns or its promise
|
|
11
|
+
* settles. A span records nothing unless the invocation was head-sampled
|
|
12
|
+
* (`observability.traces.head_sampling_rate`).
|
|
13
|
+
*
|
|
14
|
+
* `setAttributes` and `recordException` shipped on 2026-09-25
|
|
15
|
+
* (https://developers.cloudflare.com/changelog/post/2026-09-25-custom-span-apis/);
|
|
16
|
+
* a runtime from before then has `isTraced` and `setAttribute` only. So the
|
|
17
|
+
* code being traced never touches the runtime's span. It gets a
|
|
18
|
+
* {@link SpanRecorder}, which records only while the span is traced, uses
|
|
19
|
+
* whichever methods the runtime has, and swallows anything a span method
|
|
20
|
+
* throws.
|
|
21
|
+
*
|
|
22
|
+
* Core and fabric stay importable outside workerd, so they reach the API
|
|
23
|
+
* through this leaf: the Worker's composition root hands it over once, at
|
|
24
|
+
* module scope, with {@link adoptTracing}, exactly as it does `ctx.exports`
|
|
25
|
+
* (./composition.ts). With nothing adopted — unit tests, an embedder that
|
|
26
|
+
* never adopts — {@link traced} runs its callback with {@link untraced}.
|
|
27
|
+
*/
|
|
28
|
+
/** Attribute values a span holds; `undefined` entries are ignored. */
|
|
29
|
+
export type SpanAttributes = Record<string, string | number | boolean | undefined>;
|
|
30
|
+
/** The runtime's `Span`, as any Workers runtime with `tracing` may have it. */
|
|
31
|
+
export interface RuntimeSpan {
|
|
32
|
+
readonly isTraced: boolean;
|
|
33
|
+
setAttribute?(key: string, value?: string | number | boolean): unknown;
|
|
34
|
+
setAttributes?(attributes: SpanAttributes): unknown;
|
|
35
|
+
recordException?(exception: string | {
|
|
36
|
+
code?: string | number;
|
|
37
|
+
name?: string;
|
|
38
|
+
message?: string;
|
|
39
|
+
stack?: string;
|
|
40
|
+
}): void;
|
|
41
|
+
}
|
|
42
|
+
/** The part of `cloudflare:workers`'s `tracing` Nimbus uses. */
|
|
43
|
+
export interface Tracer {
|
|
44
|
+
enterSpan<T>(name: string, callback: (span: RuntimeSpan) => T): T;
|
|
45
|
+
}
|
|
46
|
+
/** What traced code records on its span. Never throws; records nothing unless the span is traced. */
|
|
47
|
+
export interface SpanRecorder {
|
|
48
|
+
set(attributes: SpanAttributes): void;
|
|
49
|
+
/**
|
|
50
|
+
* Record `error` as an exception event. `code`, when given, replaces the
|
|
51
|
+
* error's own; `context` is prefixed to its message.
|
|
52
|
+
*/
|
|
53
|
+
exception(error: unknown, code?: string, context?: string): void;
|
|
54
|
+
}
|
|
55
|
+
/** The recorder of a span that is not traced, or of no span at all. */
|
|
56
|
+
export declare const untraced: SpanRecorder;
|
|
57
|
+
/** Hand this isolate's `tracing` over. The first adoption stands, like `adoptCtxExports`. */
|
|
58
|
+
export declare function adoptTracing(value: Tracer): void;
|
|
59
|
+
/**
|
|
60
|
+
* Run `fn` in a span named `name` carrying `attributes`, and record on it the
|
|
61
|
+
* exception `fn` throws or its promise rejects with. What `fn` returns or
|
|
62
|
+
* throws reaches the caller unchanged, whatever the span does.
|
|
63
|
+
*/
|
|
64
|
+
export declare function traced<T>(name: string, attributes: SpanAttributes, fn: (span: SpanRecorder) => T): T;
|
|
65
|
+
//# sourceMappingURL=tracing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,sEAAsE;AACtE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAEnF,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IACvE,aAAa,CAAC,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAAC;IACpD,eAAe,CAAC,CACd,SAAS,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9F,IAAI,CAAC;CACT;AAED,gEAAgE;AAChE,MAAM,WAAW,MAAM;IACrB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,CAAC,GAAG,CAAC,CAAC;CACnE;AAED,qGAAqG;AACrG,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,UAAU,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClE;AAED,uEAAuE;AACvE,eAAO,MAAM,QAAQ,EAAE,YAA0D,CAAC;AAIlF,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEhD;AAyCD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,CAAC,GAAG,CAAC,CAkBpG"}
|
package/dist/tracing.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tracing.ts — Workers custom spans, for modules that cannot import
|
|
3
|
+
* `cloudflare:workers`, as best-effort telemetry that never changes an
|
|
4
|
+
* outcome.
|
|
5
|
+
*
|
|
6
|
+
* The span API is `tracing` from `cloudflare:workers`
|
|
7
|
+
* (https://developers.cloudflare.com/workers/observability/traces/custom-spans/):
|
|
8
|
+
* `enterSpan(name, callback)` runs the callback in a span that is the active
|
|
9
|
+
* parent of every span and platform call made inside it — automatic RPC
|
|
10
|
+
* spans included — and ends it when the callback returns or its promise
|
|
11
|
+
* settles. A span records nothing unless the invocation was head-sampled
|
|
12
|
+
* (`observability.traces.head_sampling_rate`).
|
|
13
|
+
*
|
|
14
|
+
* `setAttributes` and `recordException` shipped on 2026-09-25
|
|
15
|
+
* (https://developers.cloudflare.com/changelog/post/2026-09-25-custom-span-apis/);
|
|
16
|
+
* a runtime from before then has `isTraced` and `setAttribute` only. So the
|
|
17
|
+
* code being traced never touches the runtime's span. It gets a
|
|
18
|
+
* {@link SpanRecorder}, which records only while the span is traced, uses
|
|
19
|
+
* whichever methods the runtime has, and swallows anything a span method
|
|
20
|
+
* throws.
|
|
21
|
+
*
|
|
22
|
+
* Core and fabric stay importable outside workerd, so they reach the API
|
|
23
|
+
* through this leaf: the Worker's composition root hands it over once, at
|
|
24
|
+
* module scope, with {@link adoptTracing}, exactly as it does `ctx.exports`
|
|
25
|
+
* (./composition.ts). With nothing adopted — unit tests, an embedder that
|
|
26
|
+
* never adopts — {@link traced} runs its callback with {@link untraced}.
|
|
27
|
+
*/
|
|
28
|
+
/** The recorder of a span that is not traced, or of no span at all. */
|
|
29
|
+
export const untraced = Object.freeze({ set() { }, exception() { } });
|
|
30
|
+
let tracer = null;
|
|
31
|
+
/** Hand this isolate's `tracing` over. The first adoption stands, like `adoptCtxExports`. */
|
|
32
|
+
export function adoptTracing(value) {
|
|
33
|
+
tracer ??= value;
|
|
34
|
+
}
|
|
35
|
+
function recorderFor(span) {
|
|
36
|
+
try {
|
|
37
|
+
if (!span.isTraced)
|
|
38
|
+
return untraced;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return untraced;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
set(attributes) {
|
|
45
|
+
try {
|
|
46
|
+
if (typeof span.setAttributes === 'function') {
|
|
47
|
+
span.setAttributes(attributes);
|
|
48
|
+
}
|
|
49
|
+
else if (typeof span.setAttribute === 'function') {
|
|
50
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
51
|
+
if (value !== undefined)
|
|
52
|
+
span.setAttribute(key, value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch { /* telemetry is best-effort */ }
|
|
57
|
+
},
|
|
58
|
+
exception(error, code, context = '') {
|
|
59
|
+
try {
|
|
60
|
+
if (typeof span.recordException !== 'function')
|
|
61
|
+
return;
|
|
62
|
+
if (error instanceof Error) {
|
|
63
|
+
const own = 'code' in error ? error.code : undefined;
|
|
64
|
+
span.recordException({
|
|
65
|
+
code: code ?? (typeof own === 'string' || typeof own === 'number' ? own : undefined),
|
|
66
|
+
name: error.name,
|
|
67
|
+
message: context + error.message,
|
|
68
|
+
stack: error.stack,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
span.recordException(code === undefined && !context
|
|
73
|
+
? String(error)
|
|
74
|
+
: { code, message: context + String(error) });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch { /* telemetry is best-effort */ }
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Run `fn` in a span named `name` carrying `attributes`, and record on it the
|
|
83
|
+
* exception `fn` throws or its promise rejects with. What `fn` returns or
|
|
84
|
+
* throws reaches the caller unchanged, whatever the span does.
|
|
85
|
+
*/
|
|
86
|
+
export function traced(name, attributes, fn) {
|
|
87
|
+
if (!tracer)
|
|
88
|
+
return fn(untraced);
|
|
89
|
+
return tracer.enterSpan(name, (runtimeSpan) => {
|
|
90
|
+
const span = recorderFor(runtimeSpan);
|
|
91
|
+
span.set(attributes);
|
|
92
|
+
let result;
|
|
93
|
+
try {
|
|
94
|
+
result = fn(span);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
span.exception(error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
if (!(result instanceof Promise))
|
|
101
|
+
return result;
|
|
102
|
+
return result.catch((error) => {
|
|
103
|
+
span.exception(error);
|
|
104
|
+
throw error;
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
@@ -9,11 +9,6 @@ export interface WeightedCreditPoolStats {
|
|
|
9
9
|
readonly current: number;
|
|
10
10
|
readonly peak: number;
|
|
11
11
|
readonly queued: number;
|
|
12
|
-
/**
|
|
13
|
-
* Credit held by owners that keep it for their whole lifetime rather than
|
|
14
|
-
* for one operation — the floor no waiter can ever expect to get back.
|
|
15
|
-
*/
|
|
16
|
-
readonly resident: number;
|
|
17
12
|
}
|
|
18
13
|
export interface WeightedCreditPoolOptions {
|
|
19
14
|
/**
|
|
@@ -61,14 +56,6 @@ export declare class WeightedCreditPool {
|
|
|
61
56
|
private isSmall;
|
|
62
57
|
tryAcquire(bytes: number): ResizableCreditLease | null;
|
|
63
58
|
acquire(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease>;
|
|
64
|
-
/**
|
|
65
|
-
* Take credit that will be held for the owner's whole lifetime rather than
|
|
66
|
-
* for one operation — the esbuild pool's wasm image is the case.
|
|
67
|
-
*
|
|
68
|
-
* Recorded as a floor so a later claim that could never fit around it is
|
|
69
|
-
* refused with both numbers instead of parking in the FIFO forever.
|
|
70
|
-
*/
|
|
71
|
-
acquireResident(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease>;
|
|
72
59
|
private validateRequest;
|
|
73
60
|
}
|
|
74
61
|
//# sourceMappingURL=weighted-credit-pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weighted-credit-pool.d.ts","sourceRoot":"","sources":["../src/weighted-credit-pool.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"weighted-credit-pool.d.ts","sourceRoot":"","sources":["../src/weighted-credit-pool.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkHD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,kBAAkB;IAKjB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAJrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAEtB,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,yBAA8B;IAoB9E,IAAI,KAAK,IAAI,uBAAuB,CASnC;IAED,OAAO,CAAC,OAAO;IAIf,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI;IAOtD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAiB3E,OAAO,CAAC,eAAe;CAOxB"}
|
|
@@ -3,52 +3,27 @@ class CreditLane {
|
|
|
3
3
|
capacity;
|
|
4
4
|
current = 0;
|
|
5
5
|
peak = 0;
|
|
6
|
-
resident = 0;
|
|
7
6
|
waiters = [];
|
|
8
7
|
constructor(capacity) {
|
|
9
8
|
this.capacity = capacity;
|
|
10
9
|
}
|
|
11
10
|
get stats() {
|
|
12
|
-
return {
|
|
13
|
-
current: this.current, peak: this.peak, queued: this.waiters.length, resident: this.resident,
|
|
14
|
-
};
|
|
11
|
+
return { current: this.current, peak: this.peak, queued: this.waiters.length };
|
|
15
12
|
}
|
|
16
13
|
tryAcquire(bytes) {
|
|
17
14
|
if (this.waiters.length > 0 || this.current + bytes > this.capacity)
|
|
18
15
|
return null;
|
|
19
|
-
return this.grant(bytes
|
|
16
|
+
return this.grant(bytes);
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
* The largest claim this lane can ever grant: capacity less what resident
|
|
23
|
-
* owners hold for their whole lifetime. A claim above it cannot be
|
|
24
|
-
* satisfied by waiting, because nothing it is waiting for will be
|
|
25
|
-
* released.
|
|
26
|
-
*/
|
|
27
|
-
get grantableCeiling() {
|
|
28
|
-
return this.capacity - this.resident;
|
|
29
|
-
}
|
|
30
|
-
acquire(bytes, signal, resident = false) {
|
|
18
|
+
acquire(bytes, signal) {
|
|
31
19
|
if (signal?.aborted)
|
|
32
20
|
return Promise.reject(abortError(signal));
|
|
33
|
-
|
|
34
|
-
// in the FIFO forever. A parked claim is indistinguishable from a caller
|
|
35
|
-
// that stopped: no error, no timeout, and — because the queue refuses
|
|
36
|
-
// everyone behind it — no further progress anywhere in the isolate. That
|
|
37
|
-
// is what a real-vite launch looked like for four rounds of debugging.
|
|
38
|
-
if (bytes > this.grantableCeiling) {
|
|
39
|
-
return Promise.reject(new RangeError(`weighted credit claim of ${bytes} bytes can never be granted: capacity is ${this.capacity} `
|
|
40
|
-
+ `and ${this.resident} bytes are held by resident owners, leaving ${this.grantableCeiling}`));
|
|
41
|
-
}
|
|
42
|
-
const immediate = resident ? null : this.tryAcquire(bytes);
|
|
21
|
+
const immediate = this.tryAcquire(bytes);
|
|
43
22
|
if (immediate)
|
|
44
23
|
return Promise.resolve(immediate);
|
|
45
|
-
if (resident && this.waiters.length === 0 && this.current + bytes <= this.capacity) {
|
|
46
|
-
return Promise.resolve(this.grant(bytes, true));
|
|
47
|
-
}
|
|
48
24
|
return new Promise((resolve, reject) => {
|
|
49
25
|
const waiter = {
|
|
50
26
|
bytes,
|
|
51
|
-
resident,
|
|
52
27
|
resolve,
|
|
53
28
|
reject,
|
|
54
29
|
signal,
|
|
@@ -73,10 +48,8 @@ class CreditLane {
|
|
|
73
48
|
this.drain();
|
|
74
49
|
});
|
|
75
50
|
}
|
|
76
|
-
grant(bytes
|
|
51
|
+
grant(bytes) {
|
|
77
52
|
this.current += bytes;
|
|
78
|
-
if (resident)
|
|
79
|
-
this.resident += bytes;
|
|
80
53
|
this.peak = Math.max(this.peak, this.current);
|
|
81
54
|
let leasedBytes = bytes;
|
|
82
55
|
let released = false;
|
|
@@ -94,8 +67,6 @@ class CreditLane {
|
|
|
94
67
|
throw new RangeError(`weighted credit lease can only shrink to a positive safe integer no larger than ${leasedBytes}: ${nextBytes}`);
|
|
95
68
|
}
|
|
96
69
|
this.current -= leasedBytes - nextBytes;
|
|
97
|
-
if (resident)
|
|
98
|
-
this.resident -= leasedBytes - nextBytes;
|
|
99
70
|
leasedBytes = nextBytes;
|
|
100
71
|
this.drain();
|
|
101
72
|
},
|
|
@@ -104,8 +75,6 @@ class CreditLane {
|
|
|
104
75
|
return;
|
|
105
76
|
released = true;
|
|
106
77
|
this.current -= leasedBytes;
|
|
107
|
-
if (resident)
|
|
108
|
-
this.resident -= leasedBytes;
|
|
109
78
|
if (this.current < 0) {
|
|
110
79
|
throw new Error('weighted credit accounting underflow');
|
|
111
80
|
}
|
|
@@ -126,7 +95,7 @@ class CreditLane {
|
|
|
126
95
|
return;
|
|
127
96
|
this.waiters.shift();
|
|
128
97
|
waiter.signal?.removeEventListener('abort', waiter.onAbort);
|
|
129
|
-
waiter.resolve(this.grant(waiter.bytes
|
|
98
|
+
waiter.resolve(this.grant(waiter.bytes));
|
|
130
99
|
}
|
|
131
100
|
}
|
|
132
101
|
}
|
|
@@ -185,7 +154,6 @@ export class WeightedCreditPool {
|
|
|
185
154
|
current: general.current + small.current,
|
|
186
155
|
peak: general.peak + small.peak,
|
|
187
156
|
queued: general.queued + small.queued,
|
|
188
|
-
resident: general.resident + small.resident,
|
|
189
157
|
};
|
|
190
158
|
}
|
|
191
159
|
isSmall(bytes) {
|
|
@@ -216,22 +184,6 @@ export class WeightedCreditPool {
|
|
|
216
184
|
}
|
|
217
185
|
return this.general.acquire(bytes, signal);
|
|
218
186
|
}
|
|
219
|
-
/**
|
|
220
|
-
* Take credit that will be held for the owner's whole lifetime rather than
|
|
221
|
-
* for one operation — the esbuild pool's wasm image is the case.
|
|
222
|
-
*
|
|
223
|
-
* Recorded as a floor so a later claim that could never fit around it is
|
|
224
|
-
* refused with both numbers instead of parking in the FIFO forever.
|
|
225
|
-
*/
|
|
226
|
-
acquireResident(bytes, signal) {
|
|
227
|
-
try {
|
|
228
|
-
this.validateRequest(bytes);
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
return Promise.reject(error);
|
|
232
|
-
}
|
|
233
|
-
return this.general.acquire(bytes, signal, true);
|
|
234
|
-
}
|
|
235
187
|
validateRequest(bytes) {
|
|
236
188
|
if (!Number.isSafeInteger(bytes) || bytes <= 0 || bytes > this.capacity) {
|
|
237
189
|
throw new RangeError(`weighted credit request must be a positive safe integer no larger than ${this.capacity}: ${bytes}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimbus-sh/platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "The measured Cloudflare platform truths Nimbus is built on \u2014 error taxonomy, limits tables, budget coordination, and wire-format primitives, with zero runtime dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"typecheck": "tsc --noEmit"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@cloudflare/workers-types": "^
|
|
51
|
+
"@cloudflare/workers-types": "^5.20260928.1",
|
|
52
52
|
"typescript": "^5.7.0"
|
|
53
53
|
},
|
|
54
54
|
"publishConfig": {
|
package/src/composition.ts
CHANGED
|
@@ -66,13 +66,46 @@ export const DEFAULT_HOST_DISPATCH_METHOD = 'supervisorOp';
|
|
|
66
66
|
let composition: FabricComposition | null = null;
|
|
67
67
|
let ctxExports: CtxExports | null = null;
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A program's filesystem errors reach it across workerd RPC, from the host
|
|
71
|
+
* that answers its supervisor binding, and the program branches on the POSIX
|
|
72
|
+
* `code` each one carries. workerd carries an error's own properties across
|
|
73
|
+
* RPC only when the sending AND the receiving isolate have
|
|
74
|
+
* `enhanced_error_serialization`, on by compatibility date from 2026-04-21:
|
|
75
|
+
* the serializer writes a native error as a host object with its own
|
|
76
|
+
* properties only under the flag (src/workerd/jsg/ser.c++,
|
|
77
|
+
* Serializer::IsHostObject), and the receiver reads that detail back only
|
|
78
|
+
* under it (src/workerd/jsg/util.c++, decodeTunneledException); see
|
|
79
|
+
* https://developers.cloudflare.com/workers/runtime-apis/rpc/error-handling/.
|
|
80
|
+
* Programs load at CF_COMPAT_DATE, past it. The host runs at its embedder's
|
|
81
|
+
* date, and below it every ENOENT would reach every program as a bare message
|
|
82
|
+
* it maps to EIO. So composing refuses such a host, wherever it composes
|
|
83
|
+
* (module scope: the Worker fails at startup; NimbusWorkspace.create: that
|
|
84
|
+
* call throws), and names both fixes: the flag alone keeps
|
|
85
|
+
* every other behavior of the embedder's date. Off workerd (bun, node) there
|
|
86
|
+
* is no RPC hop and no `Cloudflare` global to read.
|
|
87
|
+
*/
|
|
88
|
+
function requireEnhancedErrorSerialization(): void {
|
|
89
|
+
// `typeof` of an undeclared global is 'undefined', not a ReferenceError.
|
|
90
|
+
if (typeof Cloudflare === 'undefined') return;
|
|
91
|
+
if (Cloudflare.compatibilityFlags.enhanced_error_serialization === true) return;
|
|
92
|
+
throw new Error(
|
|
93
|
+
'fabric: this Worker runs without the enhanced_error_serialization compatibility flag, so the '
|
|
94
|
+
+ "code on a program's filesystem error (ENOENT, EEXIST, ...) would not survive the RPC back "
|
|
95
|
+
+ 'to it. Add "enhanced_error_serialization" to compatibility_flags in your wrangler config, '
|
|
96
|
+
+ 'or set compatibility_date to 2026-04-21 or later.',
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
69
100
|
/**
|
|
70
101
|
* Compose once per isolate. A second call with the same values is a no-op;
|
|
71
102
|
* a second call with different values throws, naming both, so an embedder
|
|
72
103
|
* whose composition lost to an earlier import learns it at startup rather
|
|
73
|
-
* than from a facet that reached the wrong host.
|
|
104
|
+
* than from a facet that reached the wrong host. A host without
|
|
105
|
+
* `enhanced_error_serialization` is refused.
|
|
74
106
|
*/
|
|
75
107
|
export function composeFabric(value: FabricComposition): void {
|
|
108
|
+
requireEnhancedErrorSerialization();
|
|
76
109
|
if (!composition) {
|
|
77
110
|
composition = value;
|
|
78
111
|
return;
|
package/src/diag-counters.ts
CHANGED
|
@@ -82,12 +82,6 @@ export interface DiagCounters {
|
|
|
82
82
|
* Surfaced as `breakdown.prefetchCacheBytes`. A gauge, not a delta.
|
|
83
83
|
*/
|
|
84
84
|
prefetchCacheBytes: number;
|
|
85
|
-
/**
|
|
86
|
-
* Bytes retained by the FacetManager's ESM→CJS transform cache, bounded by
|
|
87
|
-
* ESM_TRANSFORM_CACHE_MAX_BYTES. Surfaced as `breakdown.transformCacheBytes`.
|
|
88
|
-
* A gauge, not a delta.
|
|
89
|
-
*/
|
|
90
|
-
transformCacheBytes: number;
|
|
91
85
|
/** Install-facet counters. Populated by npm-installer after a
|
|
92
86
|
* successful batch-facet dispatch returns. Confirms the install ran
|
|
93
87
|
* in the facet (tarballsCompleted > 0) and surfaces the
|
|
@@ -147,7 +141,6 @@ const _counters: DiagCounters = {
|
|
|
147
141
|
inFlightRpcPayloadBytes: 0,
|
|
148
142
|
prefetchBundleBytes: 0,
|
|
149
143
|
prefetchCacheBytes: 0,
|
|
150
|
-
transformCacheBytes: 0,
|
|
151
144
|
installFacet: {
|
|
152
145
|
tarballsCompleted: 0,
|
|
153
146
|
cumulativeBytesDecoded: 0,
|
|
@@ -250,12 +243,6 @@ export function setPrefetchCacheBytes(bytes: number): void {
|
|
|
250
243
|
_counters.prefetchCacheBytes = Number.isFinite(n) && n > 0 ? n : 0;
|
|
251
244
|
}
|
|
252
245
|
|
|
253
|
-
/** Publish the transform cache's live retained total; same contract as above. */
|
|
254
|
-
export function setTransformCacheBytes(bytes: number): void {
|
|
255
|
-
const n = Number(bytes);
|
|
256
|
-
_counters.transformCacheBytes = Number.isFinite(n) && n > 0 ? n : 0;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
246
|
/** Fold facet-returned counters into the supervisor's diag state.
|
|
260
247
|
* Called by npm-installer after the batch-facet returns; aggregates
|
|
261
248
|
* rather than replaces so multiple install runs in the same DO
|
package/src/heap-estimate.ts
CHANGED
|
@@ -200,12 +200,6 @@ export interface HeapBreakdown {
|
|
|
200
200
|
* the cache holds rather than a reservation it might use.
|
|
201
201
|
*/
|
|
202
202
|
prefetchCacheBytes: number;
|
|
203
|
-
/**
|
|
204
|
-
* Bytes retained by the FacetManager's ESM→CJS transform cache
|
|
205
|
-
* (diag-counters `transformCacheBytes`), the other cache that persists
|
|
206
|
-
* across execs. Bounded by ESM_TRANSFORM_CACHE_MAX_BYTES.
|
|
207
|
-
*/
|
|
208
|
-
transformCacheBytes: number;
|
|
209
203
|
/**
|
|
210
204
|
* Shared-budget occupancy not already represented by the named transient
|
|
211
205
|
* counters. This covers full-budget owners and keeps cross-DO module-local
|
|
@@ -303,7 +297,6 @@ export function estimateSupervisorHeap(
|
|
|
303
297
|
streamingBuffersBytes: c.inFlightRpcPayloadBytes,
|
|
304
298
|
prefetchBundleBytes: c.prefetchBundleBytes,
|
|
305
299
|
prefetchCacheBytes: c.prefetchCacheBytes,
|
|
306
|
-
transformCacheBytes: c.transformCacheBytes,
|
|
307
300
|
unattributedReservationBytes: Math.max(
|
|
308
301
|
0,
|
|
309
302
|
allocationBudget.current - transientAttributedBytes,
|
|
@@ -318,7 +311,6 @@ export function estimateSupervisorHeap(
|
|
|
318
311
|
breakdown.streamingBuffersBytes +
|
|
319
312
|
breakdown.prefetchBundleBytes +
|
|
320
313
|
breakdown.prefetchCacheBytes +
|
|
321
|
-
breakdown.transformCacheBytes +
|
|
322
314
|
breakdown.unattributedReservationBytes;
|
|
323
315
|
|
|
324
316
|
const percentOfCeiling = Math.round(
|
package/src/heavy-alloc-coord.ts
CHANGED
|
@@ -24,8 +24,6 @@ export interface SupervisorAllocationBudgetStats {
|
|
|
24
24
|
readonly current: number;
|
|
25
25
|
readonly peak: number;
|
|
26
26
|
readonly queued: number;
|
|
27
|
-
/** Credit held for an owner's whole lifetime; see `acquireResident`. */
|
|
28
|
-
readonly resident: number;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
interface AllocationBudgetLifecycle {
|
|
@@ -84,24 +82,12 @@ export class SupervisorAllocationBudget {
|
|
|
84
82
|
return this._acquire(bytes, signal, false);
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
/**
|
|
88
|
-
* Reserve bytes an owner holds for its whole lifetime, not for one
|
|
89
|
-
* operation. Recorded as a floor: a later claim larger than what remains
|
|
90
|
-
* around it is refused with both numbers rather than parked forever.
|
|
91
|
-
*/
|
|
92
|
-
acquireResidentBytes(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease> {
|
|
93
|
-
return this._acquire(bytes, signal, true, true);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
85
|
private async _acquire(
|
|
97
86
|
bytes: number,
|
|
98
87
|
signal: AbortSignal | undefined,
|
|
99
88
|
drivesLifecycle: boolean,
|
|
100
|
-
resident = false,
|
|
101
89
|
): Promise<ResizableCreditLease> {
|
|
102
|
-
const credit =
|
|
103
|
-
? await this.credits.acquireResident(bytes, signal)
|
|
104
|
-
: await this.credits.acquire(bytes, signal);
|
|
90
|
+
const credit = await this.credits.acquire(bytes, signal);
|
|
105
91
|
if (drivesLifecycle) {
|
|
106
92
|
this.lifecycleHolders++;
|
|
107
93
|
if (!this.active) {
|
|
@@ -238,17 +224,6 @@ export function acquireSupervisorReadAllocation(
|
|
|
238
224
|
return supervisorAllocationBudget.acquireWithoutLifecycle(bytes, signal);
|
|
239
225
|
}
|
|
240
226
|
|
|
241
|
-
/**
|
|
242
|
-
* Reserve bytes for the whole life of the owner that takes them — an image
|
|
243
|
-
* a pool keeps until it is disposed. See `acquireResidentBytes`.
|
|
244
|
-
*/
|
|
245
|
-
export function acquireResidentSupervisorAllocation(
|
|
246
|
-
bytes: number,
|
|
247
|
-
signal?: AbortSignal,
|
|
248
|
-
): Promise<ResizableCreditLease> {
|
|
249
|
-
return supervisorAllocationBudget.acquireResidentBytes(bytes, signal);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
227
|
/**
|
|
253
228
|
* Reserve the full budget for an allocation whose retained size is not known
|
|
254
229
|
* accurately enough to overlap safely with other heavy work.
|
package/src/limits.ts
CHANGED
|
@@ -125,7 +125,7 @@ export const RESIDENT_KEEPALIVE_DETACHED_MS = 60_000;
|
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
127
|
* Bytes one hibernatable WebSocket attachment may serialize to. Verified in
|
|
128
|
-
* workerd source at the pinned version (v1.
|
|
128
|
+
* workerd source at the pinned version (v1.20260926.1,
|
|
129
129
|
* src/workerd/api/web-socket.h: `MAX_ATTACHMENT_SIZE = 1024 * 16`). The
|
|
130
130
|
* bound is on the SERIALIZED bytes — workerd re-serializes on every
|
|
131
131
|
* `serializeAttachment` call to check it — not on UTF-8 JSON text, so a
|
package/src/oom-classify.ts
CHANGED
|
@@ -127,11 +127,12 @@ export function classifyMessage(msg: string): OomCause {
|
|
|
127
127
|
// Subrequest cap (Cloudflare platform)
|
|
128
128
|
if (m.includes('too many subrequests')) return 'subrequest_cap';
|
|
129
129
|
|
|
130
|
-
// Worker
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
|
|
130
|
+
// Dynamic Worker concurrency limit (Cloudflare platform): a Durable Object
|
|
131
|
+
// may have a fixed number of distinct Dynamic Workers with in-flight
|
|
132
|
+
// requests at once (fabric budgets.ts, DO_DYNAMIC_WORKER_LIMIT). Production
|
|
133
|
+
// words it "Dynamic worker concurrency limit exceeded: each request may
|
|
134
|
+
// have up to 10 concurrent dynamic worker invocations. …".
|
|
135
|
+
if (m.includes('dynamic worker concurrency limit exceeded')) return 'dynamic_worker_cap';
|
|
135
136
|
|
|
136
137
|
// Memory exhaustion. workerd trace outcome `exceededMemory`; in-band it
|
|
137
138
|
// arrives as a `broken.exceededMemory` actor break, a "…exceeded [its]
|
package/src/tracing.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tracing.ts — Workers custom spans, for modules that cannot import
|
|
3
|
+
* `cloudflare:workers`, as best-effort telemetry that never changes an
|
|
4
|
+
* outcome.
|
|
5
|
+
*
|
|
6
|
+
* The span API is `tracing` from `cloudflare:workers`
|
|
7
|
+
* (https://developers.cloudflare.com/workers/observability/traces/custom-spans/):
|
|
8
|
+
* `enterSpan(name, callback)` runs the callback in a span that is the active
|
|
9
|
+
* parent of every span and platform call made inside it — automatic RPC
|
|
10
|
+
* spans included — and ends it when the callback returns or its promise
|
|
11
|
+
* settles. A span records nothing unless the invocation was head-sampled
|
|
12
|
+
* (`observability.traces.head_sampling_rate`).
|
|
13
|
+
*
|
|
14
|
+
* `setAttributes` and `recordException` shipped on 2026-09-25
|
|
15
|
+
* (https://developers.cloudflare.com/changelog/post/2026-09-25-custom-span-apis/);
|
|
16
|
+
* a runtime from before then has `isTraced` and `setAttribute` only. So the
|
|
17
|
+
* code being traced never touches the runtime's span. It gets a
|
|
18
|
+
* {@link SpanRecorder}, which records only while the span is traced, uses
|
|
19
|
+
* whichever methods the runtime has, and swallows anything a span method
|
|
20
|
+
* throws.
|
|
21
|
+
*
|
|
22
|
+
* Core and fabric stay importable outside workerd, so they reach the API
|
|
23
|
+
* through this leaf: the Worker's composition root hands it over once, at
|
|
24
|
+
* module scope, with {@link adoptTracing}, exactly as it does `ctx.exports`
|
|
25
|
+
* (./composition.ts). With nothing adopted — unit tests, an embedder that
|
|
26
|
+
* never adopts — {@link traced} runs its callback with {@link untraced}.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** Attribute values a span holds; `undefined` entries are ignored. */
|
|
30
|
+
export type SpanAttributes = Record<string, string | number | boolean | undefined>;
|
|
31
|
+
|
|
32
|
+
/** The runtime's `Span`, as any Workers runtime with `tracing` may have it. */
|
|
33
|
+
export interface RuntimeSpan {
|
|
34
|
+
readonly isTraced: boolean;
|
|
35
|
+
setAttribute?(key: string, value?: string | number | boolean): unknown;
|
|
36
|
+
setAttributes?(attributes: SpanAttributes): unknown;
|
|
37
|
+
recordException?(
|
|
38
|
+
exception: string | { code?: string | number; name?: string; message?: string; stack?: string },
|
|
39
|
+
): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The part of `cloudflare:workers`'s `tracing` Nimbus uses. */
|
|
43
|
+
export interface Tracer {
|
|
44
|
+
enterSpan<T>(name: string, callback: (span: RuntimeSpan) => T): T;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** What traced code records on its span. Never throws; records nothing unless the span is traced. */
|
|
48
|
+
export interface SpanRecorder {
|
|
49
|
+
set(attributes: SpanAttributes): void;
|
|
50
|
+
/**
|
|
51
|
+
* Record `error` as an exception event. `code`, when given, replaces the
|
|
52
|
+
* error's own; `context` is prefixed to its message.
|
|
53
|
+
*/
|
|
54
|
+
exception(error: unknown, code?: string, context?: string): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The recorder of a span that is not traced, or of no span at all. */
|
|
58
|
+
export const untraced: SpanRecorder = Object.freeze({ set() {}, exception() {} });
|
|
59
|
+
|
|
60
|
+
let tracer: Tracer | null = null;
|
|
61
|
+
|
|
62
|
+
/** Hand this isolate's `tracing` over. The first adoption stands, like `adoptCtxExports`. */
|
|
63
|
+
export function adoptTracing(value: Tracer): void {
|
|
64
|
+
tracer ??= value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function recorderFor(span: RuntimeSpan): SpanRecorder {
|
|
68
|
+
try {
|
|
69
|
+
if (!span.isTraced) return untraced;
|
|
70
|
+
} catch {
|
|
71
|
+
return untraced;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
set(attributes) {
|
|
75
|
+
try {
|
|
76
|
+
if (typeof span.setAttributes === 'function') {
|
|
77
|
+
span.setAttributes(attributes);
|
|
78
|
+
} else if (typeof span.setAttribute === 'function') {
|
|
79
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
80
|
+
if (value !== undefined) span.setAttribute(key, value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch { /* telemetry is best-effort */ }
|
|
84
|
+
},
|
|
85
|
+
exception(error, code, context = '') {
|
|
86
|
+
try {
|
|
87
|
+
if (typeof span.recordException !== 'function') return;
|
|
88
|
+
if (error instanceof Error) {
|
|
89
|
+
const own = 'code' in error ? error.code : undefined;
|
|
90
|
+
span.recordException({
|
|
91
|
+
code: code ?? (typeof own === 'string' || typeof own === 'number' ? own : undefined),
|
|
92
|
+
name: error.name,
|
|
93
|
+
message: context + error.message,
|
|
94
|
+
stack: error.stack,
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
span.recordException(code === undefined && !context
|
|
98
|
+
? String(error)
|
|
99
|
+
: { code, message: context + String(error) });
|
|
100
|
+
}
|
|
101
|
+
} catch { /* telemetry is best-effort */ }
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Run `fn` in a span named `name` carrying `attributes`, and record on it the
|
|
108
|
+
* exception `fn` throws or its promise rejects with. What `fn` returns or
|
|
109
|
+
* throws reaches the caller unchanged, whatever the span does.
|
|
110
|
+
*/
|
|
111
|
+
export function traced<T>(name: string, attributes: SpanAttributes, fn: (span: SpanRecorder) => T): T {
|
|
112
|
+
if (!tracer) return fn(untraced);
|
|
113
|
+
return tracer.enterSpan(name, (runtimeSpan) => {
|
|
114
|
+
const span = recorderFor(runtimeSpan);
|
|
115
|
+
span.set(attributes);
|
|
116
|
+
let result: T;
|
|
117
|
+
try {
|
|
118
|
+
result = fn(span);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
span.exception(error);
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
if (!(result instanceof Promise)) return result;
|
|
124
|
+
return result.catch((error: unknown) => {
|
|
125
|
+
span.exception(error);
|
|
126
|
+
throw error;
|
|
127
|
+
}) as T;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
@@ -11,11 +11,6 @@ export interface WeightedCreditPoolStats {
|
|
|
11
11
|
readonly current: number;
|
|
12
12
|
readonly peak: number;
|
|
13
13
|
readonly queued: number;
|
|
14
|
-
/**
|
|
15
|
-
* Credit held by owners that keep it for their whole lifetime rather than
|
|
16
|
-
* for one operation — the floor no waiter can ever expect to get back.
|
|
17
|
-
*/
|
|
18
|
-
readonly resident: number;
|
|
19
14
|
}
|
|
20
15
|
|
|
21
16
|
export interface WeightedCreditPoolOptions {
|
|
@@ -34,8 +29,6 @@ export interface WeightedCreditPoolOptions {
|
|
|
34
29
|
|
|
35
30
|
interface CreditWaiter {
|
|
36
31
|
readonly bytes: number;
|
|
37
|
-
/** Whether the granted lease counts toward the resident floor. */
|
|
38
|
-
readonly resident: boolean;
|
|
39
32
|
readonly resolve: (lease: ResizableCreditLease) => void;
|
|
40
33
|
readonly reject: (error: Error) => void;
|
|
41
34
|
readonly signal?: AbortSignal;
|
|
@@ -46,55 +39,27 @@ interface CreditWaiter {
|
|
|
46
39
|
class CreditLane {
|
|
47
40
|
private current = 0;
|
|
48
41
|
private peak = 0;
|
|
49
|
-
private resident = 0;
|
|
50
42
|
private readonly waiters: CreditWaiter[] = [];
|
|
51
43
|
|
|
52
44
|
constructor(readonly capacity: number) {}
|
|
53
45
|
|
|
54
46
|
get stats(): WeightedCreditPoolStats {
|
|
55
|
-
return {
|
|
56
|
-
current: this.current, peak: this.peak, queued: this.waiters.length, resident: this.resident,
|
|
57
|
-
};
|
|
47
|
+
return { current: this.current, peak: this.peak, queued: this.waiters.length };
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
tryAcquire(bytes: number): ResizableCreditLease | null {
|
|
61
51
|
if (this.waiters.length > 0 || this.current + bytes > this.capacity) return null;
|
|
62
|
-
return this.grant(bytes
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* The largest claim this lane can ever grant: capacity less what resident
|
|
67
|
-
* owners hold for their whole lifetime. A claim above it cannot be
|
|
68
|
-
* satisfied by waiting, because nothing it is waiting for will be
|
|
69
|
-
* released.
|
|
70
|
-
*/
|
|
71
|
-
get grantableCeiling(): number {
|
|
72
|
-
return this.capacity - this.resident;
|
|
52
|
+
return this.grant(bytes);
|
|
73
53
|
}
|
|
74
54
|
|
|
75
|
-
acquire(bytes: number, signal?: AbortSignal
|
|
55
|
+
acquire(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease> {
|
|
76
56
|
if (signal?.aborted) return Promise.reject(abortError(signal));
|
|
77
|
-
|
|
78
|
-
// in the FIFO forever. A parked claim is indistinguishable from a caller
|
|
79
|
-
// that stopped: no error, no timeout, and — because the queue refuses
|
|
80
|
-
// everyone behind it — no further progress anywhere in the isolate. That
|
|
81
|
-
// is what a real-vite launch looked like for four rounds of debugging.
|
|
82
|
-
if (bytes > this.grantableCeiling) {
|
|
83
|
-
return Promise.reject(new RangeError(
|
|
84
|
-
`weighted credit claim of ${bytes} bytes can never be granted: capacity is ${this.capacity} `
|
|
85
|
-
+ `and ${this.resident} bytes are held by resident owners, leaving ${this.grantableCeiling}`,
|
|
86
|
-
));
|
|
87
|
-
}
|
|
88
|
-
const immediate = resident ? null : this.tryAcquire(bytes);
|
|
57
|
+
const immediate = this.tryAcquire(bytes);
|
|
89
58
|
if (immediate) return Promise.resolve(immediate);
|
|
90
|
-
if (resident && this.waiters.length === 0 && this.current + bytes <= this.capacity) {
|
|
91
|
-
return Promise.resolve(this.grant(bytes, true));
|
|
92
|
-
}
|
|
93
59
|
|
|
94
60
|
return new Promise<ResizableCreditLease>((resolve, reject) => {
|
|
95
61
|
const waiter: CreditWaiter = {
|
|
96
62
|
bytes,
|
|
97
|
-
resident,
|
|
98
63
|
resolve,
|
|
99
64
|
reject,
|
|
100
65
|
signal,
|
|
@@ -119,9 +84,8 @@ class CreditLane {
|
|
|
119
84
|
});
|
|
120
85
|
}
|
|
121
86
|
|
|
122
|
-
private grant(bytes: number
|
|
87
|
+
private grant(bytes: number): ResizableCreditLease {
|
|
123
88
|
this.current += bytes;
|
|
124
|
-
if (resident) this.resident += bytes;
|
|
125
89
|
this.peak = Math.max(this.peak, this.current);
|
|
126
90
|
let leasedBytes = bytes;
|
|
127
91
|
let released = false;
|
|
@@ -143,7 +107,6 @@ class CreditLane {
|
|
|
143
107
|
);
|
|
144
108
|
}
|
|
145
109
|
this.current -= leasedBytes - nextBytes;
|
|
146
|
-
if (resident) this.resident -= leasedBytes - nextBytes;
|
|
147
110
|
leasedBytes = nextBytes;
|
|
148
111
|
this.drain();
|
|
149
112
|
},
|
|
@@ -151,7 +114,6 @@ class CreditLane {
|
|
|
151
114
|
if (released) return;
|
|
152
115
|
released = true;
|
|
153
116
|
this.current -= leasedBytes;
|
|
154
|
-
if (resident) this.resident -= leasedBytes;
|
|
155
117
|
if (this.current < 0) {
|
|
156
118
|
throw new Error('weighted credit accounting underflow');
|
|
157
119
|
}
|
|
@@ -172,7 +134,7 @@ class CreditLane {
|
|
|
172
134
|
if (this.current + waiter.bytes > this.capacity) return;
|
|
173
135
|
this.waiters.shift();
|
|
174
136
|
waiter.signal?.removeEventListener('abort', waiter.onAbort!);
|
|
175
|
-
waiter.resolve(this.grant(waiter.bytes
|
|
137
|
+
waiter.resolve(this.grant(waiter.bytes));
|
|
176
138
|
}
|
|
177
139
|
}
|
|
178
140
|
}
|
|
@@ -233,7 +195,6 @@ export class WeightedCreditPool {
|
|
|
233
195
|
current: general.current + small.current,
|
|
234
196
|
peak: general.peak + small.peak,
|
|
235
197
|
queued: general.queued + small.queued,
|
|
236
|
-
resident: general.resident + small.resident,
|
|
237
198
|
};
|
|
238
199
|
}
|
|
239
200
|
|
|
@@ -265,22 +226,6 @@ export class WeightedCreditPool {
|
|
|
265
226
|
return this.general.acquire(bytes, signal);
|
|
266
227
|
}
|
|
267
228
|
|
|
268
|
-
/**
|
|
269
|
-
* Take credit that will be held for the owner's whole lifetime rather than
|
|
270
|
-
* for one operation — the esbuild pool's wasm image is the case.
|
|
271
|
-
*
|
|
272
|
-
* Recorded as a floor so a later claim that could never fit around it is
|
|
273
|
-
* refused with both numbers instead of parking in the FIFO forever.
|
|
274
|
-
*/
|
|
275
|
-
acquireResident(bytes: number, signal?: AbortSignal): Promise<ResizableCreditLease> {
|
|
276
|
-
try {
|
|
277
|
-
this.validateRequest(bytes);
|
|
278
|
-
} catch (error) {
|
|
279
|
-
return Promise.reject(error as Error);
|
|
280
|
-
}
|
|
281
|
-
return this.general.acquire(bytes, signal, true);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
229
|
private validateRequest(bytes: number): void {
|
|
285
230
|
if (!Number.isSafeInteger(bytes) || bytes <= 0 || bytes > this.capacity) {
|
|
286
231
|
throw new RangeError(
|