@nimbus-sh/platform 0.5.1 → 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.
@@ -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
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"w7-frame.d.ts","sourceRoot":"","sources":["../src/w7-frame.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"w7-frame.d.ts","sourceRoot":"","sources":["../src/w7-frame.ts"],"names":[],"mappings":"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"}
package/dist/w7-frame.js CHANGED
@@ -2,6 +2,7 @@
2
2
  * W7 v3 — incremental typed records for streamed bulk filesystem writes.
3
3
  * The format is internal: every producer and consumer deploys together.
4
4
  */
5
+ import { crc32 } from './crc32.js';
5
6
  import { CHUNK_SIZE } from './limits.js';
6
7
  export const W7_MAGIC = new Uint8Array([0x4e, 0x57, 0x37, 0x03]);
7
8
  const ENCODER_QUEUE_HWM = 0;
@@ -101,7 +102,7 @@ export async function decodeWriteBatchStream(stream, options = {}) {
101
102
  }
102
103
  const beginPayload = await buffer.readExact(beginEnvelope.length, 'batch-begin payload');
103
104
  const begin = parseBatchBegin(beginPayload);
104
- const initialCheck = updateRecordCheck(CRC_SEED, beginEnvelope.header, beginPayload);
105
+ const initialCheck = updateRecordCheck(0, beginEnvelope.header, beginPayload);
105
106
  handedOff = true;
106
107
  return {
107
108
  batchId: begin.id,
@@ -180,7 +181,7 @@ async function* decodeRecords(stream, reader, buffer, options, initialCheck) {
180
181
  throwIfAborted(options.signal);
181
182
  const data = await buffer.readExact(dataLength, 'file-chunk data');
182
183
  batchCheck = updateRecordCheck(batchCheck, envelope.header, headerPayload, data);
183
- active.check = crc32Update(active.check, data);
184
+ active.check = crc32(data, active.check);
184
185
  active.nextChunkId++;
185
186
  active.receivedBytes += dataLength;
186
187
  summary.recordCount++;
@@ -242,7 +243,7 @@ async function* decodeRecords(stream, reader, buffer, options, initialCheck) {
242
243
  inode,
243
244
  nextChunkId: 0,
244
245
  receivedBytes: 0,
245
- check: CRC_SEED,
246
+ check: 0,
246
247
  };
247
248
  yield { type: 'file-begin', streamContentId: metadata.contentId, inode };
248
249
  break;
@@ -251,7 +252,7 @@ async function* decodeRecords(stream, reader, buffer, options, initialCheck) {
251
252
  if (!active)
252
253
  throw new Error('w7-frame: file-end without active file');
253
254
  const metadata = parseFileEnd(payload);
254
- const actualCheck = crc32Finish(active.check);
255
+ const actualCheck = active.check;
255
256
  if (metadata.contentId !== active.metadata.contentId) {
256
257
  throw new Error(`w7-frame: file-end content id mismatch for ${active.inode.path}`);
257
258
  }
@@ -281,7 +282,7 @@ async function* decodeRecords(stream, reader, buffer, options, initialCheck) {
281
282
  if (active)
282
283
  throw new Error(`w7-frame: batch-end while file ${active.inode.path} is active`);
283
284
  const actual = parseBatchEnd(payload);
284
- const expected = { ...summary, check: crc32Finish(batchCheck) };
285
+ const expected = { ...summary, check: batchCheck };
285
286
  if (!sameSummary(actual, expected)) {
286
287
  throw new Error(`w7-frame: batch-end summary mismatch; expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
287
288
  }
@@ -315,7 +316,7 @@ async function* decodeRecords(stream, reader, buffer, options, initialCheck) {
315
316
  }
316
317
  function* encodeRecords(batchId, deletes, directories, files) {
317
318
  const state = {
318
- batchCheck: CRC_SEED,
319
+ batchCheck: 0,
319
320
  summary: {
320
321
  recordCount: 0,
321
322
  pathCount: 0,
@@ -350,7 +351,7 @@ function* encodeRecords(batchId, deletes, directories, files) {
350
351
  size: file.inode.size,
351
352
  chunkCount: file.inode.chunkCount,
352
353
  }, state);
353
- let fileCheck = CRC_SEED;
354
+ let fileCheck = 0;
354
355
  for (const chunk of file.chunks) {
355
356
  const data = chunk.data;
356
357
  const contentBytes = new TextEncoder().encode(file.contentId);
@@ -364,19 +365,19 @@ function* encodeRecords(batchId, deletes, directories, files) {
364
365
  state.summary.recordCount++;
365
366
  state.summary.chunkCount++;
366
367
  state.summary.byteCount += data.byteLength;
367
- fileCheck = crc32Update(fileCheck, data);
368
+ fileCheck = crc32(data, fileCheck);
368
369
  yield [concatBytes(header, prefix), data];
369
370
  }
370
371
  yield encodeMetadataRecord(RecordTag.FileEnd, {
371
372
  contentId: file.contentId,
372
373
  size: file.inode.size,
373
374
  chunkCount: file.inode.chunkCount,
374
- check: crc32Finish(fileCheck),
375
+ check: fileCheck,
375
376
  }, state);
376
377
  }
377
378
  const end = {
378
379
  ...state.summary,
379
- check: crc32Finish(state.batchCheck),
380
+ check: state.batchCheck,
380
381
  };
381
382
  yield encodeMetadataRecord(RecordTag.BatchEnd, end);
382
383
  }
@@ -718,33 +719,9 @@ function readU32LE(bytes, offset) {
718
719
  function updateRecordCheck(seed, ...parts) {
719
720
  let check = seed;
720
721
  for (const part of parts)
721
- check = crc32Update(check, part);
722
+ check = crc32(part, check);
722
723
  return check;
723
724
  }
724
- const CRC_SEED = 0xffff_ffff;
725
- let crcTable = null;
726
- function crc32Update(check, bytes) {
727
- crcTable ??= createCrcTable();
728
- const table = crcTable;
729
- let value = check;
730
- for (const byte of bytes)
731
- value = table[(value ^ byte) & 0xff] ^ (value >>> 8);
732
- return value >>> 0;
733
- }
734
- function crc32Finish(check) {
735
- return (check ^ 0xffff_ffff) >>> 0;
736
- }
737
- function createCrcTable() {
738
- const table = new Uint32Array(256);
739
- for (let index = 0; index < table.length; index++) {
740
- let value = index;
741
- for (let bit = 0; bit < 8; bit++) {
742
- value = (value & 1) !== 0 ? 0xedb8_8320 ^ (value >>> 1) : value >>> 1;
743
- }
744
- table[index] = value >>> 0;
745
- }
746
- return table;
747
- }
748
725
  function noopRetention(bytes) {
749
726
  return { bytes, release() { } };
750
727
  }
@@ -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;IACxB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;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;AAmJD;;;;;;;;;;;;;;;;;;;;;;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,CAUnC;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;;;;;;OAMG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IASnF,OAAO,CAAC,eAAe;CAOxB"}
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, false);
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
- // Refuse a claim no amount of waiting can satisfy, rather than parking it
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, resident) {
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, waiter.resident));
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.5.1",
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",
@@ -43,12 +43,12 @@
43
43
  "LICENSE"
44
44
  ],
45
45
  "scripts": {
46
- "build": "tsc -p tsconfig.json --noCheck --noEmit false --declaration true --declarationMap true --outDir dist --rootDir src",
47
- "prepack": "npm run build",
46
+ "build": "node ../../scripts/clean-dist.mjs && tsc -p tsconfig.json --noCheck --noEmit false --declaration true --declarationMap true --outDir dist --rootDir src",
47
+ "prepublishOnly": "bun ../../scripts/dist-integrity.mjs --publish",
48
48
  "typecheck": "tsc --noEmit"
49
49
  },
50
50
  "devDependencies": {
51
- "@cloudflare/workers-types": "^4.20250327.0",
51
+ "@cloudflare/workers-types": "^5.20260928.1",
52
52
  "typescript": "^5.7.0"
53
53
  },
54
54
  "publishConfig": {
@@ -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/crc32.ts ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * CRC-32 (IEEE 802.3, the zip/gzip/PNG polynomial), the one implementation
3
+ * every Nimbus caller uses: the W7 write-batch framing and zip archives.
4
+ *
5
+ * `crc32(bytes, previous)` has node:zlib's contract: `previous` is the
6
+ * finished CRC of the bytes before these, so a checksum computed in pieces
7
+ * equals the checksum of the whole.
8
+ *
9
+ * Measured 2026-09-24 (5k files x 2 KiB; 64 MiB in 1 MiB buffers):
10
+ * node:zlib's crc32 is 5-40x faster than any JavaScript loop in bun, node and
11
+ * workerd (compat 2026-04-01, nodejs_compat), but each call costs ~0.14 us in
12
+ * workerd, so below 128 bytes the table loop wins there. It is reached through
13
+ * `process.getBuiltinModule`, like core's incremental SHA-256, so this module
14
+ * imports nothing and still loads where node:zlib is absent (a browser,
15
+ * workerd without nodejs_compat). The JavaScript path is slicing-by-8: 2-3x
16
+ * the byte-at-a-time table loop in bun and node, and level with it in workerd.
17
+ */
18
+
19
+ type NodeZlib = { crc32?(data: Uint8Array, value?: number): number };
20
+ type BuiltinModules = { process?: { getBuiltinModule?(id: 'node:zlib'): NodeZlib | undefined } };
21
+
22
+ /** Below this many bytes the native call's overhead exceeds the JavaScript loop's work (workerd). */
23
+ const CRC_NATIVE_MIN_BYTES = 128;
24
+
25
+ const nativeCrc32 = (() => {
26
+ try {
27
+ const crc32 = (globalThis as BuiltinModules).process?.getBuiltinModule?.('node:zlib')?.crc32;
28
+ return typeof crc32 === 'function' ? crc32 : null;
29
+ } catch {
30
+ return null;
31
+ }
32
+ })();
33
+
34
+ /** Eight 256-entry tables: table k maps a byte to its CRC after k further zero bytes. */
35
+ let crcTables: Uint32Array | null = null;
36
+
37
+ function createCrcTables(): Uint32Array {
38
+ const table = new Uint32Array(256 * 8);
39
+ for (let index = 0; index < 256; index++) {
40
+ let value = index;
41
+ for (let bit = 0; bit < 8; bit++) value = (value & 1) !== 0 ? 0xedb8_8320 ^ (value >>> 1) : value >>> 1;
42
+ table[index] = value;
43
+ }
44
+ for (let index = 0; index < 256; index++) {
45
+ let value = table[index];
46
+ for (let k = 1; k < 8; k++) {
47
+ value = table[value & 0xff] ^ (value >>> 8);
48
+ table[k * 256 + index] = value;
49
+ }
50
+ }
51
+ return table;
52
+ }
53
+
54
+ export function crc32(bytes: Uint8Array, previous = 0): number {
55
+ if (nativeCrc32 !== null && bytes.length >= CRC_NATIVE_MIN_BYTES) return nativeCrc32(bytes, previous) >>> 0;
56
+ const t = crcTables ??= createCrcTables();
57
+ let value = ~previous;
58
+ const length = bytes.length;
59
+ const whole = length - (length & 7);
60
+ let i = 0;
61
+ for (; i < whole; i += 8) {
62
+ const low = value ^ (bytes[i] | (bytes[i + 1] << 8) | (bytes[i + 2] << 16) | (bytes[i + 3] << 24));
63
+ const high = bytes[i + 4] | (bytes[i + 5] << 8) | (bytes[i + 6] << 16) | (bytes[i + 7] << 24);
64
+ value = t[1792 + (low & 0xff)] ^ t[1536 + ((low >>> 8) & 0xff)]
65
+ ^ t[1280 + ((low >>> 16) & 0xff)] ^ t[1024 + (low >>> 24)]
66
+ ^ t[768 + (high & 0xff)] ^ t[512 + ((high >>> 8) & 0xff)]
67
+ ^ t[256 + ((high >>> 16) & 0xff)] ^ t[high >>> 24];
68
+ }
69
+ for (; i < length; i++) value = t[(value ^ bytes[i]) & 0xff] ^ (value >>> 8);
70
+ return ~value >>> 0;
71
+ }
@@ -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
@@ -6,7 +6,7 @@
6
6
  * `process.memoryUsage()` returns 0 for every field inside a Durable
7
7
  * Object class context (only dynamic-worker isolates under nodejs_compat
8
8
  * get the real implementation). The previous `readNodeMem` /
9
- * `sampleMemory` helpers in nimbus-session-diag.ts called it anyway and
9
+ * `sampleMemory` helpers in session/diag.ts called it anyway and
10
10
  * therefore reported zero forever — useless for verifying memory-
11
11
  * containment work in plan §3 Track A'.
12
12
  *
@@ -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(