knitting 0.1.55 → 0.1.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +254 -40
  2. package/knitting.d.ts +7 -797
  3. package/knitting.js +6 -9780
  4. package/map.md +31 -15
  5. package/package.json +20 -6
  6. package/process-shared-buffer.d.ts +1 -0
  7. package/process-shared-buffer.js +1 -0
  8. package/scripts/build-compiled-worker.ts +276 -0
  9. package/scripts/compiled-worker/porffor.d.ts +5 -0
  10. package/scripts/compiled-worker/runtime.ts +529 -0
  11. package/scripts/compiled-worker/task-shim.ts +32 -0
  12. package/shared-memory.d.ts +1 -155
  13. package/shared-memory.js +1 -1342
  14. package/src/api.d.ts +92 -0
  15. package/src/api.js +798 -0
  16. package/src/common/envelope.d.ts +17 -0
  17. package/src/common/envelope.js +22 -0
  18. package/src/common/module-url.d.ts +2 -0
  19. package/src/common/module-url.js +52 -0
  20. package/src/common/node-compat.d.ts +22 -0
  21. package/src/common/node-compat.js +24 -0
  22. package/src/common/path-canonical.d.ts +6 -0
  23. package/src/common/path-canonical.js +55 -0
  24. package/src/common/runtime.d.ts +16 -0
  25. package/src/common/runtime.js +105 -0
  26. package/src/common/shared-buffer-region.d.ts +11 -0
  27. package/src/common/shared-buffer-region.js +21 -0
  28. package/src/common/shared-buffer-text.d.ts +16 -0
  29. package/src/common/shared-buffer-text.js +65 -0
  30. package/src/common/task-source.d.ts +5 -0
  31. package/src/common/task-source.js +100 -0
  32. package/src/common/task-symbol.d.ts +1 -0
  33. package/src/common/task-symbol.js +1 -0
  34. package/src/common/with-resolvers.d.ts +9 -0
  35. package/src/common/with-resolvers.js +23 -0
  36. package/src/common/worker-runtime.d.ts +42 -0
  37. package/src/common/worker-runtime.js +70 -0
  38. package/src/connections/buffer-reference-native.d.ts +56 -0
  39. package/src/connections/buffer-reference-native.js +241 -0
  40. package/src/connections/buffer-reference.d.ts +99 -0
  41. package/src/connections/buffer-reference.js +575 -0
  42. package/src/connections/bun.d.ts +22 -0
  43. package/src/connections/bun.js +225 -0
  44. package/src/connections/deno.d.ts +24 -0
  45. package/src/connections/deno.js +226 -0
  46. package/src/connections/external-array-buffer.d.ts +2 -0
  47. package/src/connections/external-array-buffer.js +33 -0
  48. package/src/connections/file-descriptor.d.ts +39 -0
  49. package/src/connections/file-descriptor.js +161 -0
  50. package/src/connections/index.d.ts +4 -0
  51. package/src/connections/index.js +4 -0
  52. package/src/connections/node-addons.d.ts +16 -0
  53. package/src/connections/node-addons.js +92 -0
  54. package/src/connections/node-buffer-pointer.d.ts +20 -0
  55. package/src/connections/node-buffer-pointer.js +16 -0
  56. package/src/connections/node-ffi-api.d.ts +19 -0
  57. package/src/connections/node-ffi-api.js +28 -0
  58. package/src/connections/node-ffi.d.ts +27 -0
  59. package/src/connections/node-ffi.js +262 -0
  60. package/src/connections/node.d.ts +30 -0
  61. package/src/connections/node.js +62 -0
  62. package/src/connections/numeric-array.d.ts +5 -0
  63. package/src/connections/numeric-array.js +17 -0
  64. package/src/connections/package-assets.d.ts +2 -0
  65. package/src/connections/package-assets.js +55 -0
  66. package/src/connections/posix.d.ts +74 -0
  67. package/src/connections/posix.js +178 -0
  68. package/src/connections/process-shared-buffer.d.ts +76 -0
  69. package/src/connections/process-shared-buffer.js +245 -0
  70. package/src/connections/shared-array-buffer-payload.d.ts +36 -0
  71. package/src/connections/shared-array-buffer-payload.js +235 -0
  72. package/src/connections/types.d.ts +54 -0
  73. package/src/connections/types.js +37 -0
  74. package/src/connections/windows.d.ts +37 -0
  75. package/src/connections/windows.js +245 -0
  76. package/src/debug/env-diff.d.ts +26 -0
  77. package/src/debug/env-diff.js +49 -0
  78. package/src/debug/gate.d.ts +18 -0
  79. package/src/debug/gate.js +69 -0
  80. package/src/debug/handle.d.ts +23 -0
  81. package/src/debug/handle.js +48 -0
  82. package/src/error.d.ts +14 -0
  83. package/src/error.js +50 -0
  84. package/src/ipc/tools/ring-queue.d.ts +33 -0
  85. package/src/ipc/tools/ring-queue.js +159 -0
  86. package/src/ipc/transport/shared-memory.d.ts +23 -0
  87. package/src/ipc/transport/shared-memory.js +35 -0
  88. package/src/memory/byte-carpet.d.ts +73 -0
  89. package/src/memory/byte-carpet.js +157 -0
  90. package/src/memory/lock.d.ts +213 -0
  91. package/src/memory/lock.js +760 -0
  92. package/src/memory/payload-config.d.ts +31 -0
  93. package/src/memory/payload-config.js +67 -0
  94. package/src/memory/payloadCodec.d.ts +55 -0
  95. package/src/memory/payloadCodec.js +1473 -0
  96. package/src/memory/regionRegistry.d.ts +17 -0
  97. package/src/memory/regionRegistry.js +285 -0
  98. package/src/memory/shared-buffer-io.d.ts +55 -0
  99. package/src/memory/shared-buffer-io.js +403 -0
  100. package/src/permission/compatibility.d.ts +28 -0
  101. package/src/permission/compatibility.js +239 -0
  102. package/src/permission/index.d.ts +4 -0
  103. package/src/permission/index.js +3 -0
  104. package/src/permission/protocol.d.ts +167 -0
  105. package/src/permission/protocol.js +690 -0
  106. package/src/runtime/balancer.d.ts +19 -0
  107. package/src/runtime/balancer.js +149 -0
  108. package/src/runtime/compiled-artifact.d.ts +21 -0
  109. package/src/runtime/compiled-artifact.js +260 -0
  110. package/src/runtime/compiled-builder.d.ts +8 -0
  111. package/src/runtime/compiled-builder.js +42 -0
  112. package/src/runtime/compiled-worker.d.ts +27 -0
  113. package/src/runtime/compiled-worker.js +562 -0
  114. package/src/runtime/dispatcher.d.ts +40 -0
  115. package/src/runtime/dispatcher.js +142 -0
  116. package/src/runtime/inline-executor.d.ts +10 -0
  117. package/src/runtime/inline-executor.js +294 -0
  118. package/src/runtime/pool.d.ts +44 -0
  119. package/src/runtime/pool.js +472 -0
  120. package/src/runtime/process-worker.d.ts +92 -0
  121. package/src/runtime/process-worker.js +713 -0
  122. package/src/runtime/tx-queue.d.ts +28 -0
  123. package/src/runtime/tx-queue.js +158 -0
  124. package/src/shared/abortSignal.d.ts +23 -0
  125. package/src/shared/abortSignal.js +134 -0
  126. package/src/types.d.ts +399 -0
  127. package/src/types.js +2 -0
  128. package/src/utils/http.d.ts +29 -0
  129. package/src/utils/http.js +100 -0
  130. package/src/worker/bootstrap.d.ts +5 -0
  131. package/src/worker/bootstrap.js +78 -0
  132. package/src/worker/composable-runners.d.ts +12 -0
  133. package/src/worker/composable-runners.js +98 -0
  134. package/src/worker/loop.d.ts +3 -0
  135. package/src/worker/loop.js +395 -0
  136. package/src/worker/process-worker-bootstrap.d.ts +8 -0
  137. package/src/worker/process-worker-bootstrap.js +160 -0
  138. package/src/worker/rx-queue.d.ts +25 -0
  139. package/src/worker/rx-queue.js +173 -0
  140. package/src/worker/safety/index.d.ts +4 -0
  141. package/src/worker/safety/index.js +4 -0
  142. package/src/worker/safety/performance.d.ts +1 -0
  143. package/src/worker/safety/performance.js +17 -0
  144. package/src/worker/safety/process.d.ts +2 -0
  145. package/src/worker/safety/process.js +79 -0
  146. package/src/worker/safety/startup.d.ts +16 -0
  147. package/src/worker/safety/startup.js +30 -0
  148. package/src/worker/safety/worker-data.d.ts +2 -0
  149. package/src/worker/safety/worker-data.js +36 -0
  150. package/src/worker/task-loader.d.ts +28 -0
  151. package/src/worker/task-loader.js +76 -0
  152. package/src/worker/timers.d.ts +18 -0
  153. package/src/worker/timers.js +118 -0
  154. package/unsafe.d.ts +1 -71
  155. package/unsafe.js +1 -878
  156. package/utils.d.ts +1 -31
  157. package/utils.js +1 -116
@@ -0,0 +1,575 @@
1
+ import { RUNTIME } from "../common/runtime.js";
2
+ import { getNodeProcess } from "../common/node-compat.js";
3
+ import { RUNTIME_IS_MAIN_THREAD } from "../common/worker-runtime.js";
4
+ import { loadNodeBufferPointerAddon } from "./node-buffer-pointer.js";
5
+ import { getBufferReferenceCapabilities, } from "./buffer-reference-native.js";
6
+ export const BUFFER_REFERENCE_KIND = "knitting.bufferReference";
7
+ /** Named values for the `unsafe.BufferReferenceReturn` pool option. */
8
+ export const BufferReferenceReturn = {
9
+ /** Safe default: Deno/Bun copy the returned bytes so they outlive the worker. */
10
+ Copy: "copy",
11
+ /** Zero-copy: borrow the worker's backing store until the reference is released. */
12
+ Borrow: "borrow",
13
+ };
14
+ export const BUFFER_REFERENCE_NUMERIC_TRANSFER = Symbol.for("knitting.bufferReference.numericTransfer");
15
+ export const BUFFER_REFERENCE_RETURN_RELEASE_TOKEN = Symbol.for("knitting.bufferReference.returnReleaseToken");
16
+ const EXTERNAL_PAYLOAD_BRAND = Symbol.for("knitting.payloadCodec");
17
+ export const BUFFER_REFERENCE_CODEC_ID = BUFFER_REFERENCE_KIND;
18
+ const BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY = "__knittingBufferReferenceRelease";
19
+ const getRuntime = () => {
20
+ if (RUNTIME === "deno" || RUNTIME === "bun" || RUNTIME === "node") {
21
+ return RUNTIME;
22
+ }
23
+ throw new Error(`BufferReference cannot run in runtime "${RUNTIME}"`);
24
+ };
25
+ const getProcessId = () => {
26
+ const proc = getNodeProcess();
27
+ if (proc !== undefined && typeof proc.pid === "number")
28
+ return proc.pid;
29
+ const deno = globalThis.Deno;
30
+ if (typeof deno?.pid === "number")
31
+ return deno.pid;
32
+ return 0;
33
+ };
34
+ const PROCESS_ORIGIN = `${RUNTIME}:${getProcessId()}`;
35
+ const NUMERIC_WORD_MASK = 0xffffffffn;
36
+ const RUNTIME_NODE = 1;
37
+ const RUNTIME_DENO = 2;
38
+ const RUNTIME_BUN = 3;
39
+ const PAYLOAD_TRANSPORT_FINALIZER = Symbol.for("knitting.payloadCodec.transportFinalizer");
40
+ let currentReturnHooks;
41
+ const bufferReferenceInstances = new WeakSet();
42
+ const encodeRuntime = (runtime) => {
43
+ switch (runtime) {
44
+ case "node":
45
+ return RUNTIME_NODE;
46
+ case "deno":
47
+ return RUNTIME_DENO;
48
+ case "bun":
49
+ return RUNTIME_BUN;
50
+ }
51
+ };
52
+ const decodeRuntime = (runtime) => {
53
+ switch (runtime) {
54
+ case RUNTIME_NODE:
55
+ return "node";
56
+ case RUNTIME_DENO:
57
+ return "deno";
58
+ case RUNTIME_BUN:
59
+ return "bun";
60
+ default:
61
+ return undefined;
62
+ }
63
+ };
64
+ const splitU64 = (value) => [
65
+ Number(value & NUMERIC_WORD_MASK) >>> 0,
66
+ Number((value >> 32n) & NUMERIC_WORD_MASK) >>> 0,
67
+ ];
68
+ const joinU64 = (low, high) => (BigInt(high >>> 0) << 32n) | BigInt(low >>> 0);
69
+ const readOriginPid = (origin) => {
70
+ const separator = origin.indexOf(":");
71
+ if (separator < 0)
72
+ return undefined;
73
+ const pid = Number(origin.slice(separator + 1));
74
+ return Number.isInteger(pid) && pid >= 0 && pid <= 0xffffffff
75
+ ? pid >>> 0
76
+ : undefined;
77
+ };
78
+ const numericMetadataToMetadata = (words) => {
79
+ const runtime = decodeRuntime(words[6] ?? 0);
80
+ if (runtime === undefined) {
81
+ throw new TypeError("Invalid BufferReference numeric runtime");
82
+ }
83
+ const originPid = words[7];
84
+ if (originPid === undefined ||
85
+ !Number.isInteger(originPid) ||
86
+ originPid < 0) {
87
+ throw new TypeError("Invalid BufferReference numeric origin");
88
+ }
89
+ return {
90
+ kind: BUFFER_REFERENCE_KIND,
91
+ origin: `${runtime}:${originPid >>> 0}`,
92
+ runtime,
93
+ pointer: joinU64(words[0] ?? 0, words[1] ?? 0).toString(),
94
+ token: joinU64(words[2] ?? 0, words[3] ?? 0).toString(),
95
+ byteOffset: words[4] ?? 0,
96
+ byteLength: words[5] ?? 0,
97
+ };
98
+ };
99
+ const toReturnHooks = (releaser) => typeof releaser === "function" ? { release: releaser } : releaser;
100
+ export const withBufferReferenceReturnReleaser = (releaser, run) => {
101
+ if (releaser === undefined)
102
+ return run();
103
+ const previous = currentReturnHooks;
104
+ currentReturnHooks = toReturnHooks(releaser);
105
+ try {
106
+ return run();
107
+ }
108
+ finally {
109
+ currentReturnHooks = previous;
110
+ }
111
+ };
112
+ export const createBufferReferenceReturnReleaseMessage = (token) => ({
113
+ [BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY]: token.toString(),
114
+ });
115
+ export const readBufferReferenceReturnReleaseMessage = (value) => {
116
+ if (value === null || typeof value !== "object")
117
+ return undefined;
118
+ const raw = value[BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY];
119
+ if (typeof raw !== "string")
120
+ return undefined;
121
+ try {
122
+ const token = BigInt(raw);
123
+ return token > 0n ? token : undefined;
124
+ }
125
+ catch {
126
+ return undefined;
127
+ }
128
+ };
129
+ const producerFinalizer = typeof FinalizationRegistry === "function"
130
+ ? new FinalizationRegistry((backstop) => {
131
+ backstop.count -= 1;
132
+ if (backstop.count > 0)
133
+ return;
134
+ try {
135
+ backstop.release(backstop.token);
136
+ }
137
+ catch {
138
+ // best effort
139
+ }
140
+ })
141
+ : undefined;
142
+ const borrowedReturnFinalizer = typeof FinalizationRegistry === "function"
143
+ ? new FinalizationRegistry(({ token, release }) => {
144
+ try {
145
+ release(token);
146
+ }
147
+ catch {
148
+ // best effort
149
+ }
150
+ })
151
+ : undefined;
152
+ const isSharedArrayBufferInstance = (value) => typeof SharedArrayBuffer === "function" && value instanceof SharedArrayBuffer;
153
+ const assertMovableSource = (source) => {
154
+ if (isSharedArrayBufferInstance(source)) {
155
+ throw new TypeError("BufferReference expects ArrayBuffer memory; SharedArrayBuffer is already shared and cannot be detached");
156
+ }
157
+ if (ArrayBuffer.isView(source)) {
158
+ if (isSharedArrayBufferInstance(source.buffer)) {
159
+ throw new TypeError("BufferReference expects ArrayBuffer-backed views; SharedArrayBuffer is already shared and cannot be detached");
160
+ }
161
+ return;
162
+ }
163
+ if (!(source instanceof ArrayBuffer)) {
164
+ throw new TypeError("BufferReference expects an ArrayBuffer or typed-array view");
165
+ }
166
+ };
167
+ const isDetached = (buffer) => buffer.detached === true ||
168
+ buffer.byteLength === 0;
169
+ /** Revocation primitive: detach `buffer` so every view over it is neutralized. */
170
+ export const detachArrayBufferBestEffort = (runtime, buffer) => {
171
+ if (isDetached(buffer))
172
+ return true;
173
+ if (runtime === "node") {
174
+ try {
175
+ return loadNodeBufferPointerAddon().detachArrayBuffer(buffer);
176
+ }
177
+ catch {
178
+ // fall through to the JS detach paths
179
+ }
180
+ }
181
+ const transferable = buffer;
182
+ try {
183
+ if (typeof transferable.transferToFixedLength === "function") {
184
+ transferable.transferToFixedLength(0);
185
+ return isDetached(buffer);
186
+ }
187
+ if (typeof transferable.transfer === "function") {
188
+ transferable.transfer(0);
189
+ return isDetached(buffer);
190
+ }
191
+ }
192
+ catch {
193
+ // fall through
194
+ }
195
+ try {
196
+ if (typeof structuredClone === "function") {
197
+ structuredClone(buffer, { transfer: [buffer] });
198
+ return isDetached(buffer);
199
+ }
200
+ }
201
+ catch {
202
+ // give up
203
+ }
204
+ return false;
205
+ };
206
+ export const isBufferReferenceMetadata = (value) => {
207
+ if (value === null || typeof value !== "object")
208
+ return false;
209
+ const meta = value;
210
+ return (meta.kind === BUFFER_REFERENCE_KIND &&
211
+ typeof meta.origin === "string" &&
212
+ (meta.runtime === "node" || meta.runtime === "deno" ||
213
+ meta.runtime === "bun") &&
214
+ typeof meta.pointer === "string" &&
215
+ typeof meta.token === "string" &&
216
+ typeof meta.byteOffset === "number" &&
217
+ Number.isInteger(meta.byteOffset) &&
218
+ meta.byteOffset >= 0 &&
219
+ typeof meta.byteLength === "number" &&
220
+ Number.isInteger(meta.byteLength) &&
221
+ meta.byteLength >= 0);
222
+ };
223
+ /**
224
+ * Zero-copy handle for moving ArrayBuffer bytes to/from **thread** workers.
225
+ *
226
+ * Construction detaches the source. Consumers materialize the moved region in
227
+ * their isolate: owning on Node, alias/copy on Deno/Bun. Return a
228
+ * `BufferReference` for large binary results to avoid copying back through the
229
+ * transport. Use `ProcessSharedBuffer` across process boundaries.
230
+ */
231
+ export class BufferReference {
232
+ [EXTERNAL_PAYLOAD_BRAND] = BUFFER_REFERENCE_CODEC_ID;
233
+ runtime;
234
+ origin;
235
+ pointer;
236
+ byteOffset;
237
+ byteLength;
238
+ #token;
239
+ #isProducer;
240
+ #finalizerToken;
241
+ #producerBackstop;
242
+ #materializedRegions;
243
+ #owned;
244
+ #borrowedReturnReleaser;
245
+ #borrowedReturnTrack;
246
+ #borrowedReturnFinalizerToken;
247
+ #transportRefs = 0;
248
+ #released = false;
249
+ constructor(source, meta) {
250
+ if (meta !== undefined) {
251
+ // Consumer side: rebuilt from metadata, materializes via the pointer/token.
252
+ this.runtime = meta.runtime;
253
+ this.origin = meta.origin;
254
+ this.pointer = BigInt(meta.pointer);
255
+ this.byteOffset = meta.byteOffset;
256
+ this.byteLength = meta.byteLength;
257
+ this.#token = BigInt(meta.token);
258
+ this.#isProducer = false;
259
+ bufferReferenceInstances.add(this);
260
+ return;
261
+ }
262
+ if (source === undefined) {
263
+ throw new TypeError("BufferReference requires a buffer source");
264
+ }
265
+ assertMovableSource(source);
266
+ const caps = getBufferReferenceCapabilities();
267
+ const produced = caps.produce(source); // detaches the source
268
+ this.runtime = getRuntime();
269
+ this.origin = PROCESS_ORIGIN;
270
+ this.pointer = produced.pointer;
271
+ this.byteOffset = produced.byteOffset;
272
+ this.byteLength = produced.byteLength;
273
+ this.#token = produced.token;
274
+ this.#isProducer = true;
275
+ bufferReferenceInstances.add(this);
276
+ if (producerFinalizer !== undefined) {
277
+ this.#finalizerToken = {};
278
+ this.#producerBackstop = {
279
+ count: 1,
280
+ token: this.#token,
281
+ release: caps.release,
282
+ };
283
+ producerFinalizer.register(this, this.#producerBackstop, this.#finalizerToken);
284
+ }
285
+ }
286
+ static fromMetadata(meta) {
287
+ if (!isBufferReferenceMetadata(meta)) {
288
+ throw new TypeError("Invalid BufferReferenceMetadata");
289
+ }
290
+ return new BufferReference(undefined, meta);
291
+ }
292
+ toMetadata() {
293
+ this.#assertActive();
294
+ return {
295
+ kind: BUFFER_REFERENCE_KIND,
296
+ origin: this.origin,
297
+ runtime: this.runtime,
298
+ pointer: this.pointer.toString(),
299
+ token: this.#token.toString(),
300
+ byteOffset: this.byteOffset,
301
+ byteLength: this.byteLength,
302
+ };
303
+ }
304
+ [BUFFER_REFERENCE_NUMERIC_TRANSFER]() {
305
+ this.#assertActive();
306
+ const originPid = readOriginPid(this.origin);
307
+ if (originPid === undefined ||
308
+ this.byteOffset > 0xffffffff ||
309
+ this.byteLength > 0xffffffff) {
310
+ return undefined;
311
+ }
312
+ const [pointerLow, pointerHigh] = splitU64(this.pointer);
313
+ const [tokenLow, tokenHigh] = splitU64(this.#token);
314
+ return [
315
+ pointerLow,
316
+ pointerHigh,
317
+ tokenLow,
318
+ tokenHigh,
319
+ this.byteOffset >>> 0,
320
+ this.byteLength >>> 0,
321
+ encodeRuntime(this.runtime),
322
+ originPid,
323
+ ];
324
+ }
325
+ get isLocal() {
326
+ return this.origin === PROCESS_ORIGIN && this.runtime === RUNTIME;
327
+ }
328
+ #assertLocal() {
329
+ if (this.origin !== PROCESS_ORIGIN) {
330
+ throw new Error(`BufferReference cannot cross a process boundary (origin ${this.origin} ` +
331
+ `!= ${PROCESS_ORIGIN}). Use ProcessSharedBuffer for cross-process memory.`);
332
+ }
333
+ if (this.runtime !== RUNTIME) {
334
+ throw new Error(`BufferReference runtime mismatch: produced on ${this.runtime}, ` +
335
+ `materializing on ${RUNTIME}.`);
336
+ }
337
+ }
338
+ #assertActive() {
339
+ if (this.#released) {
340
+ throw new Error("BufferReference has been released");
341
+ }
342
+ }
343
+ /** Prepare a returned reference before the worker-side producer hold drains. */
344
+ claimOwnership(releaser = currentReturnHooks) {
345
+ if (this.#owned !== undefined || this.#borrowedReturnReleaser !== undefined ||
346
+ this.#released)
347
+ return this;
348
+ this.#assertLocal();
349
+ const caps = getBufferReferenceCapabilities();
350
+ const input = {
351
+ token: this.#token,
352
+ pointer: this.pointer,
353
+ byteOffset: this.byteOffset,
354
+ byteLength: this.byteLength,
355
+ };
356
+ if (releaser !== undefined) {
357
+ const hooks = toReturnHooks(releaser);
358
+ // Borrow: adopt lazily — the alias costs GC-visible external memory on
359
+ // FFI runtimes, so it is only created when the bytes are actually read.
360
+ // Until then no view can exist, so the GC backstop safely watches the
361
+ // reference itself; first materialize migrates it to the alias buffer
362
+ // (views strongly reference their buffer, so the worker pin can only
363
+ // drop once no view can reach the borrowed bytes).
364
+ this.#borrowedReturnReleaser = hooks.release;
365
+ this.#borrowedReturnTrack = hooks.track;
366
+ this.#borrowedReturnFinalizerToken = {};
367
+ borrowedReturnFinalizer?.register(this, { token: this.#token, release: hooks.release }, this.#borrowedReturnFinalizerToken);
368
+ hooks.track?.(this, this.#token);
369
+ return this;
370
+ }
371
+ this.#owned = caps.adopt(input, caps.supportsOwningAdopt ? undefined : { copy: true });
372
+ return this;
373
+ }
374
+ #materialize() {
375
+ this.#assertActive();
376
+ if (this.#owned !== undefined)
377
+ return this.#owned;
378
+ this.#assertLocal();
379
+ const region = getBufferReferenceCapabilities().adopt({
380
+ token: this.#token,
381
+ pointer: this.pointer,
382
+ byteOffset: this.byteOffset,
383
+ byteLength: this.byteLength,
384
+ });
385
+ if (this.#borrowedReturnReleaser !== undefined) {
386
+ // First borrowed read: views over this alias exist from here on, so
387
+ // move the GC backstop from the reference onto the alias buffer.
388
+ this.#owned = region;
389
+ if (this.#borrowedReturnFinalizerToken !== undefined &&
390
+ borrowedReturnFinalizer !== undefined) {
391
+ borrowedReturnFinalizer.unregister(this.#borrowedReturnFinalizerToken);
392
+ borrowedReturnFinalizer.register(region.buffer, { token: this.#token, release: this.#borrowedReturnReleaser }, this.#borrowedReturnFinalizerToken);
393
+ }
394
+ this.#borrowedReturnTrack?.(this, this.#token, region.buffer);
395
+ return region;
396
+ }
397
+ (this.#materializedRegions ??= []).push(region);
398
+ if (this.#producerBackstop !== undefined && producerFinalizer !== undefined) {
399
+ this.#producerBackstop.count += 1;
400
+ producerFinalizer.register(region.buffer, this.#producerBackstop, this.#finalizerToken);
401
+ }
402
+ return region;
403
+ }
404
+ toArrayBuffer() {
405
+ const region = this.#materialize();
406
+ if (region.byteOffset === 0 && region.buffer.byteLength === region.byteLength) {
407
+ return region.buffer;
408
+ }
409
+ return region.buffer.slice(region.byteOffset, region.byteOffset + region.byteLength);
410
+ }
411
+ toUint8Array() {
412
+ const region = this.#materialize();
413
+ return new Uint8Array(region.buffer, region.byteOffset, region.byteLength);
414
+ }
415
+ /** The source is moved on construction, so it is never retained here. */
416
+ get source() {
417
+ return undefined;
418
+ }
419
+ release() {
420
+ if (this.#released)
421
+ return;
422
+ this.#released = true;
423
+ // Revoke every alias this reference handed out before any pin can drop.
424
+ // Leak-not-UAF: if a detach fails, the pin must stay held — the GC
425
+ // backstops remain registered and retry once the buffers are unreachable.
426
+ let allDetached = true;
427
+ const regions = this.#materializedRegions;
428
+ this.#materializedRegions = undefined;
429
+ if (regions !== undefined) {
430
+ for (let i = 0; i < regions.length; i++) {
431
+ allDetached =
432
+ detachArrayBufferBestEffort(this.runtime, regions[i].buffer) &&
433
+ allDetached;
434
+ }
435
+ }
436
+ const borrowedReturnReleaser = this.#borrowedReturnReleaser;
437
+ this.#borrowedReturnReleaser = undefined;
438
+ this.#borrowedReturnTrack = undefined;
439
+ if (borrowedReturnReleaser !== undefined) {
440
+ const owned = this.#owned;
441
+ this.#owned = undefined;
442
+ if (owned !== undefined) {
443
+ allDetached = detachArrayBufferBestEffort(this.runtime, owned.buffer) &&
444
+ allDetached;
445
+ }
446
+ if (allDetached) {
447
+ if (this.#borrowedReturnFinalizerToken !== undefined) {
448
+ borrowedReturnFinalizer?.unregister(this.#borrowedReturnFinalizerToken);
449
+ this.#borrowedReturnFinalizerToken = undefined;
450
+ }
451
+ try {
452
+ borrowedReturnReleaser(this.#token);
453
+ }
454
+ catch {
455
+ // best effort
456
+ }
457
+ }
458
+ // else: the finalizer stays registered on the alias buffer and sends
459
+ // the release once every escaped view is gone.
460
+ }
461
+ // Only the producer owns the registry hold; consumers must not drop it.
462
+ if (this.#isProducer && allDetached) {
463
+ if (this.#finalizerToken !== undefined) {
464
+ producerFinalizer?.unregister(this.#finalizerToken);
465
+ this.#finalizerToken = undefined;
466
+ }
467
+ this.#producerBackstop = undefined;
468
+ try {
469
+ getBufferReferenceCapabilities().release(this.#token);
470
+ }
471
+ catch {
472
+ // best effort
473
+ }
474
+ }
475
+ // else on failed detach: the countdown backstop drops the pin when the
476
+ // reference and every materialized alias are unreachable.
477
+ }
478
+ /**
479
+ * Revoke a borrowed return before its producing worker goes away: detach
480
+ * the borrowed alias so no view can dangle. With `copyBytes` the reference
481
+ * itself survives on an owned copy; without it (worker bytes already gone)
482
+ * later reads fail loud instead of aliasing freed memory.
483
+ */
484
+ revokeBorrow(copyBytes) {
485
+ const releaser = this.#borrowedReturnReleaser;
486
+ if (releaser === undefined || this.#released)
487
+ return;
488
+ const owned = this.#owned;
489
+ let nextOwned;
490
+ let detached = true;
491
+ if (owned !== undefined) {
492
+ if (copyBytes) {
493
+ nextOwned = {
494
+ buffer: owned.buffer.slice(owned.byteOffset, owned.byteOffset + owned.byteLength),
495
+ byteOffset: 0,
496
+ byteLength: owned.byteLength,
497
+ };
498
+ }
499
+ // Without a copy, keep the (now detached) region so reads throw
500
+ // instead of re-materializing an alias over freed worker memory.
501
+ detached = detachArrayBufferBestEffort(this.runtime, owned.buffer);
502
+ }
503
+ else if (copyBytes) {
504
+ // Never materialized: take the bytes now, while the worker still pins
505
+ // them, so the reference stays readable after its worker dies.
506
+ const caps = getBufferReferenceCapabilities();
507
+ nextOwned = caps.adopt({
508
+ token: this.#token,
509
+ pointer: this.pointer,
510
+ byteOffset: this.byteOffset,
511
+ byteLength: this.byteLength,
512
+ }, { copy: !caps.supportsOwningAdopt });
513
+ }
514
+ else {
515
+ // Never materialized and the worker bytes are already gone: no view
516
+ // exists, so poison future reads instead of aliasing freed memory.
517
+ this.#released = true;
518
+ }
519
+ if (!detached)
520
+ return;
521
+ if (nextOwned !== undefined)
522
+ this.#owned = nextOwned;
523
+ this.#borrowedReturnReleaser = undefined;
524
+ this.#borrowedReturnTrack = undefined;
525
+ if (this.#borrowedReturnFinalizerToken !== undefined) {
526
+ borrowedReturnFinalizer?.unregister(this.#borrowedReturnFinalizerToken);
527
+ this.#borrowedReturnFinalizerToken = undefined;
528
+ }
529
+ // Best effort: if the worker is still alive it can drop its pin early.
530
+ try {
531
+ releaser(this.#token);
532
+ }
533
+ catch {
534
+ // best effort
535
+ }
536
+ }
537
+ [Symbol.dispose]() {
538
+ this.release();
539
+ }
540
+ [PAYLOAD_TRANSPORT_FINALIZER]() {
541
+ if (this.#released)
542
+ return undefined;
543
+ this.#transportRefs++;
544
+ let finalized = false;
545
+ const finalizer = (() => {
546
+ if (finalized)
547
+ return;
548
+ finalized = true;
549
+ this.#transportRefs--;
550
+ if (this.#transportRefs <= 0)
551
+ this.release();
552
+ });
553
+ if (this.#isProducer) {
554
+ finalizer[BUFFER_REFERENCE_RETURN_RELEASE_TOKEN] = this.#token;
555
+ }
556
+ return finalizer;
557
+ }
558
+ }
559
+ export const isBufferReferenceValue = (value) => value !== null &&
560
+ typeof value === "object" &&
561
+ bufferReferenceInstances.has(value);
562
+ const codecGlobal = globalThis;
563
+ const codecs = codecGlobal.__KNITTING_PAYLOAD_CODECS__ ??= Object.create(null);
564
+ codecs[BUFFER_REFERENCE_CODEC_ID] = {
565
+ decode: (metadata) => {
566
+ const ref = BufferReference.fromMetadata(metadata);
567
+ // Host decodes are return values; claim before the worker drains.
568
+ // Worker decodes are forward inputs and stay lazy for the call.
569
+ return RUNTIME_IS_MAIN_THREAD ? ref.claimOwnership() : ref;
570
+ },
571
+ decodeNumeric: (metadata) => {
572
+ const ref = BufferReference.fromMetadata(numericMetadataToMetadata(metadata));
573
+ return RUNTIME_IS_MAIN_THREAD ? ref.claimOwnership() : ref;
574
+ },
575
+ };
@@ -0,0 +1,22 @@
1
+ import { type CreateSharedMemoryOptions, type MapSharedMemoryOptions, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
2
+ type BunPointer = number;
3
+ type BunLibc = {
4
+ symbols: {
5
+ memfd_create?: (name: Uint8Array, flags: number) => number;
6
+ shm_open?: (name: Uint8Array, flags: number, ...rest: number[]) => number;
7
+ shm_unlink?: (name: Uint8Array) => number;
8
+ ftruncate: (fd: number, length: bigint) => number;
9
+ dup: (fd: number) => number;
10
+ fcntl: (fd: number, cmd: number, arg: number) => number;
11
+ mmap: (address: null, length: number, protection: number, flags: number, fd: number, offset: bigint) => BunPointer;
12
+ munmap: (address: BunPointer, length: number) => number;
13
+ close: (fd: number) => number;
14
+ };
15
+ };
16
+ export declare const openBunLibc: () => BunLibc;
17
+ export declare const mapBunSharedMemory: (options: MapSharedMemoryOptions, libc?: BunLibc) => SharedMemoryMapping<ArrayBuffer>;
18
+ export declare const createBunSharedMemory: (options: number | CreateSharedMemoryOptions, libc?: BunLibc) => SharedMemoryMapping<ArrayBuffer>;
19
+ export declare const unlinkBunSharedMemory: (name: string, libc?: BunLibc) => boolean;
20
+ export declare const createBunPosixConnectionPrimitives: (libc?: BunLibc) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
21
+ export declare const createBunConnectionPrimitives: (libc?: BunLibc) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
22
+ export {};