knitting 0.1.50 → 0.1.52

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 (72) hide show
  1. package/README.md +268 -75
  2. package/knitting.d.ts +1 -0
  3. package/map.md +56 -6
  4. package/package.json +16 -4
  5. package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
  6. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  7. package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
  8. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  9. package/prebuilds/darwin-x64-node-127/knitting_buffer_pointer.node +0 -0
  10. package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
  11. package/prebuilds/darwin-x64-node-137/knitting_buffer_pointer.node +0 -0
  12. package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
  13. package/prebuilds/linux-x64-node-127/knitting_buffer_pointer.node +0 -0
  14. package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
  15. package/prebuilds/linux-x64-node-137/knitting_buffer_pointer.node +0 -0
  16. package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
  17. package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
  18. package/prebuilds/win32-x64-node-127/knitting_buffer_pointer.node +0 -0
  19. package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
  20. package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
  21. package/prebuilds/win32-x64-node-137/knitting_buffer_pointer.node +0 -0
  22. package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
  23. package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
  24. package/scripts/build-native-addons.ts +5 -0
  25. package/src/api.d.ts +5 -11
  26. package/src/api.js +103 -22
  27. package/src/common/envelope.d.ts +9 -3
  28. package/src/common/envelope.js +14 -0
  29. package/src/common/task-source.js +4 -0
  30. package/src/common/worker-runtime.d.ts +2 -0
  31. package/src/common/worker-runtime.js +9 -0
  32. package/src/connections/buffer-reference-native.d.ts +56 -0
  33. package/src/connections/buffer-reference-native.js +217 -0
  34. package/src/connections/buffer-reference.d.ts +76 -0
  35. package/src/connections/buffer-reference.js +459 -0
  36. package/src/connections/index.d.ts +1 -0
  37. package/src/connections/index.js +1 -0
  38. package/src/connections/node-addons.d.ts +1 -1
  39. package/src/connections/node-buffer-pointer.d.ts +20 -0
  40. package/src/connections/node-buffer-pointer.js +16 -0
  41. package/src/connections/process-shared-buffer.js +2 -0
  42. package/src/connections/shared-array-buffer-payload.d.ts +36 -0
  43. package/src/connections/shared-array-buffer-payload.js +235 -0
  44. package/src/knitting_buffer_pointer.cc +425 -0
  45. package/src/knitting_shared_memory.cc +9 -2
  46. package/src/memory/lock.d.ts +12 -1
  47. package/src/memory/lock.js +55 -172
  48. package/src/memory/payloadCodec.js +241 -65
  49. package/src/memory/shared-buffer-io.d.ts +2 -0
  50. package/src/memory/shared-buffer-io.js +23 -0
  51. package/src/runtime/inline-executor.d.ts +2 -2
  52. package/src/runtime/inline-executor.js +15 -3
  53. package/src/runtime/pool.d.ts +3 -1
  54. package/src/runtime/pool.js +9 -1
  55. package/src/runtime/process-worker.js +3 -1
  56. package/src/runtime/tx-queue.d.ts +3 -2
  57. package/src/runtime/tx-queue.js +18 -13
  58. package/src/types.d.ts +39 -18
  59. package/src/utils/http.d.ts +21 -0
  60. package/src/utils/http.js +93 -0
  61. package/src/worker/loop.js +26 -4
  62. package/src/worker/process-worker-bootstrap.js +1 -0
  63. package/src/worker/rx-queue.d.ts +4 -1
  64. package/src/worker/rx-queue.js +53 -4
  65. package/src/worker/safety/startup.d.ts +2 -1
  66. package/src/worker/safety/startup.js +5 -2
  67. package/src/worker/task-loader.d.ts +2 -1
  68. package/src/worker/task-loader.js +14 -2
  69. package/unsafe.d.ts +1 -0
  70. package/unsafe.js +1 -0
  71. package/utils.d.ts +1 -0
  72. package/utils.js +1 -0
@@ -0,0 +1,459 @@
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 currentReturnReleaser;
41
+ const encodeRuntime = (runtime) => {
42
+ switch (runtime) {
43
+ case "node":
44
+ return RUNTIME_NODE;
45
+ case "deno":
46
+ return RUNTIME_DENO;
47
+ case "bun":
48
+ return RUNTIME_BUN;
49
+ }
50
+ };
51
+ const decodeRuntime = (runtime) => {
52
+ switch (runtime) {
53
+ case RUNTIME_NODE:
54
+ return "node";
55
+ case RUNTIME_DENO:
56
+ return "deno";
57
+ case RUNTIME_BUN:
58
+ return "bun";
59
+ default:
60
+ return undefined;
61
+ }
62
+ };
63
+ const splitU64 = (value) => [
64
+ Number(value & NUMERIC_WORD_MASK) >>> 0,
65
+ Number((value >> 32n) & NUMERIC_WORD_MASK) >>> 0,
66
+ ];
67
+ const joinU64 = (low, high) => (BigInt(high >>> 0) << 32n) | BigInt(low >>> 0);
68
+ const readOriginPid = (origin) => {
69
+ const separator = origin.indexOf(":");
70
+ if (separator < 0)
71
+ return undefined;
72
+ const pid = Number(origin.slice(separator + 1));
73
+ return Number.isInteger(pid) && pid >= 0 && pid <= 0xffffffff
74
+ ? pid >>> 0
75
+ : undefined;
76
+ };
77
+ const numericMetadataToMetadata = (words) => {
78
+ const runtime = decodeRuntime(words[6] ?? 0);
79
+ if (runtime === undefined) {
80
+ throw new TypeError("Invalid BufferReference numeric runtime");
81
+ }
82
+ const originPid = words[7];
83
+ if (originPid === undefined ||
84
+ !Number.isInteger(originPid) ||
85
+ originPid < 0) {
86
+ throw new TypeError("Invalid BufferReference numeric origin");
87
+ }
88
+ return {
89
+ kind: BUFFER_REFERENCE_KIND,
90
+ origin: `${runtime}:${originPid >>> 0}`,
91
+ runtime,
92
+ pointer: joinU64(words[0] ?? 0, words[1] ?? 0).toString(),
93
+ token: joinU64(words[2] ?? 0, words[3] ?? 0).toString(),
94
+ byteOffset: words[4] ?? 0,
95
+ byteLength: words[5] ?? 0,
96
+ };
97
+ };
98
+ export const withBufferReferenceReturnReleaser = (releaser, run) => {
99
+ if (releaser === undefined)
100
+ return run();
101
+ const previous = currentReturnReleaser;
102
+ currentReturnReleaser = releaser;
103
+ try {
104
+ return run();
105
+ }
106
+ finally {
107
+ currentReturnReleaser = previous;
108
+ }
109
+ };
110
+ export const createBufferReferenceReturnReleaseMessage = (token) => ({
111
+ [BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY]: token.toString(),
112
+ });
113
+ export const readBufferReferenceReturnReleaseMessage = (value) => {
114
+ if (value === null || typeof value !== "object")
115
+ return undefined;
116
+ const raw = value[BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY];
117
+ if (typeof raw !== "string")
118
+ return undefined;
119
+ try {
120
+ const token = BigInt(raw);
121
+ return token > 0n ? token : undefined;
122
+ }
123
+ catch {
124
+ return undefined;
125
+ }
126
+ };
127
+ // Backstop for references that never flow through the transport.
128
+ const producerFinalizer = typeof FinalizationRegistry === "function"
129
+ ? new FinalizationRegistry(({ token, release }) => {
130
+ try {
131
+ release(token);
132
+ }
133
+ catch {
134
+ // best effort
135
+ }
136
+ })
137
+ : undefined;
138
+ const borrowedReturnFinalizer = typeof FinalizationRegistry === "function"
139
+ ? new FinalizationRegistry(({ token, release }) => {
140
+ try {
141
+ release(token);
142
+ }
143
+ catch {
144
+ // best effort
145
+ }
146
+ })
147
+ : undefined;
148
+ const isSharedArrayBufferInstance = (value) => typeof SharedArrayBuffer === "function" && value instanceof SharedArrayBuffer;
149
+ const assertMovableSource = (source) => {
150
+ if (isSharedArrayBufferInstance(source)) {
151
+ throw new TypeError("BufferReference expects ArrayBuffer memory; SharedArrayBuffer is already shared and cannot be detached");
152
+ }
153
+ if (ArrayBuffer.isView(source)) {
154
+ if (isSharedArrayBufferInstance(source.buffer)) {
155
+ throw new TypeError("BufferReference expects ArrayBuffer-backed views; SharedArrayBuffer is already shared and cannot be detached");
156
+ }
157
+ return;
158
+ }
159
+ if (!(source instanceof ArrayBuffer)) {
160
+ throw new TypeError("BufferReference expects an ArrayBuffer or typed-array view");
161
+ }
162
+ };
163
+ const isDetached = (buffer) => buffer.detached === true ||
164
+ buffer.byteLength === 0;
165
+ const detachArrayBufferBestEffort = (runtime, buffer) => {
166
+ if (isDetached(buffer))
167
+ return true;
168
+ if (runtime === "node") {
169
+ try {
170
+ return loadNodeBufferPointerAddon().detachArrayBuffer(buffer);
171
+ }
172
+ catch {
173
+ // fall through to the JS detach paths
174
+ }
175
+ }
176
+ const transferable = buffer;
177
+ try {
178
+ if (typeof transferable.transferToFixedLength === "function") {
179
+ transferable.transferToFixedLength(0);
180
+ return isDetached(buffer);
181
+ }
182
+ if (typeof transferable.transfer === "function") {
183
+ transferable.transfer(0);
184
+ return isDetached(buffer);
185
+ }
186
+ }
187
+ catch {
188
+ // fall through
189
+ }
190
+ try {
191
+ if (typeof structuredClone === "function") {
192
+ structuredClone(buffer, { transfer: [buffer] });
193
+ return isDetached(buffer);
194
+ }
195
+ }
196
+ catch {
197
+ // give up
198
+ }
199
+ return false;
200
+ };
201
+ export const isBufferReferenceMetadata = (value) => {
202
+ if (value === null || typeof value !== "object")
203
+ return false;
204
+ const meta = value;
205
+ return (meta.kind === BUFFER_REFERENCE_KIND &&
206
+ typeof meta.origin === "string" &&
207
+ (meta.runtime === "node" || meta.runtime === "deno" ||
208
+ meta.runtime === "bun") &&
209
+ typeof meta.pointer === "string" &&
210
+ typeof meta.token === "string" &&
211
+ typeof meta.byteOffset === "number" &&
212
+ Number.isInteger(meta.byteOffset) &&
213
+ meta.byteOffset >= 0 &&
214
+ typeof meta.byteLength === "number" &&
215
+ Number.isInteger(meta.byteLength) &&
216
+ meta.byteLength >= 0);
217
+ };
218
+ /**
219
+ * Zero-copy handle for moving ArrayBuffer bytes to **thread** workers.
220
+ *
221
+ * Construction detaches the source. Consumers materialize the moved region in
222
+ * their isolate: owning on Node, alias/copy on Deno/Bun.
223
+ */
224
+ export class BufferReference {
225
+ [EXTERNAL_PAYLOAD_BRAND] = BUFFER_REFERENCE_CODEC_ID;
226
+ runtime;
227
+ origin;
228
+ pointer;
229
+ byteOffset;
230
+ byteLength;
231
+ #token;
232
+ #isProducer;
233
+ #finalizerToken;
234
+ #materializedRegions;
235
+ #owned;
236
+ #borrowedReturnReleaser;
237
+ #borrowedReturnFinalizerToken;
238
+ #transportRefs = 0;
239
+ #released = false;
240
+ constructor(source, meta) {
241
+ if (meta !== undefined) {
242
+ // Consumer side: rebuilt from metadata, materializes via the pointer/token.
243
+ this.runtime = meta.runtime;
244
+ this.origin = meta.origin;
245
+ this.pointer = BigInt(meta.pointer);
246
+ this.byteOffset = meta.byteOffset;
247
+ this.byteLength = meta.byteLength;
248
+ this.#token = BigInt(meta.token);
249
+ this.#isProducer = false;
250
+ return;
251
+ }
252
+ if (source === undefined) {
253
+ throw new TypeError("BufferReference requires a buffer source");
254
+ }
255
+ assertMovableSource(source);
256
+ const caps = getBufferReferenceCapabilities();
257
+ const produced = caps.produce(source); // detaches the source
258
+ this.runtime = getRuntime();
259
+ this.origin = PROCESS_ORIGIN;
260
+ this.pointer = produced.pointer;
261
+ this.byteOffset = produced.byteOffset;
262
+ this.byteLength = produced.byteLength;
263
+ this.#token = produced.token;
264
+ this.#isProducer = true;
265
+ this.#finalizerToken = {};
266
+ producerFinalizer?.register(this, { token: this.#token, release: caps.release }, this.#finalizerToken);
267
+ }
268
+ static fromMetadata(meta) {
269
+ if (!isBufferReferenceMetadata(meta)) {
270
+ throw new TypeError("Invalid BufferReferenceMetadata");
271
+ }
272
+ return new BufferReference(undefined, meta);
273
+ }
274
+ toMetadata() {
275
+ this.#assertActive();
276
+ return {
277
+ kind: BUFFER_REFERENCE_KIND,
278
+ origin: this.origin,
279
+ runtime: this.runtime,
280
+ pointer: this.pointer.toString(),
281
+ token: this.#token.toString(),
282
+ byteOffset: this.byteOffset,
283
+ byteLength: this.byteLength,
284
+ };
285
+ }
286
+ [BUFFER_REFERENCE_NUMERIC_TRANSFER]() {
287
+ this.#assertActive();
288
+ const originPid = readOriginPid(this.origin);
289
+ if (originPid === undefined ||
290
+ this.byteOffset > 0xffffffff ||
291
+ this.byteLength > 0xffffffff) {
292
+ return undefined;
293
+ }
294
+ const [pointerLow, pointerHigh] = splitU64(this.pointer);
295
+ const [tokenLow, tokenHigh] = splitU64(this.#token);
296
+ return [
297
+ pointerLow,
298
+ pointerHigh,
299
+ tokenLow,
300
+ tokenHigh,
301
+ this.byteOffset >>> 0,
302
+ this.byteLength >>> 0,
303
+ encodeRuntime(this.runtime),
304
+ originPid,
305
+ ];
306
+ }
307
+ get isLocal() {
308
+ return this.origin === PROCESS_ORIGIN && this.runtime === RUNTIME;
309
+ }
310
+ #assertLocal() {
311
+ if (this.origin !== PROCESS_ORIGIN) {
312
+ throw new Error(`BufferReference cannot cross a process boundary (origin ${this.origin} ` +
313
+ `!= ${PROCESS_ORIGIN}). Use ProcessSharedBuffer for cross-process memory.`);
314
+ }
315
+ if (this.runtime !== RUNTIME) {
316
+ throw new Error(`BufferReference runtime mismatch: produced on ${this.runtime}, ` +
317
+ `materializing on ${RUNTIME}.`);
318
+ }
319
+ }
320
+ #assertActive() {
321
+ if (this.#released) {
322
+ throw new Error("BufferReference has been released");
323
+ }
324
+ }
325
+ /** Prepare a returned reference before the worker-side producer hold drains. */
326
+ claimOwnership(releaser = currentReturnReleaser) {
327
+ if (this.#owned !== undefined || this.#borrowedReturnReleaser !== undefined ||
328
+ this.#released)
329
+ return this;
330
+ this.#assertLocal();
331
+ const caps = getBufferReferenceCapabilities();
332
+ const input = {
333
+ token: this.#token,
334
+ pointer: this.pointer,
335
+ byteOffset: this.byteOffset,
336
+ byteLength: this.byteLength,
337
+ };
338
+ if (releaser !== undefined) {
339
+ this.#borrowedReturnReleaser = releaser;
340
+ this.#borrowedReturnFinalizerToken = {};
341
+ borrowedReturnFinalizer?.register(this, { token: this.#token, release: releaser }, this.#borrowedReturnFinalizerToken);
342
+ return this;
343
+ }
344
+ this.#owned = caps.adopt(input, caps.supportsOwningAdopt ? undefined : { copy: true });
345
+ return this;
346
+ }
347
+ #materialize() {
348
+ this.#assertActive();
349
+ if (this.#owned !== undefined)
350
+ return this.#owned;
351
+ this.#assertLocal();
352
+ const region = getBufferReferenceCapabilities().adopt({
353
+ token: this.#token,
354
+ pointer: this.pointer,
355
+ byteOffset: this.byteOffset,
356
+ byteLength: this.byteLength,
357
+ });
358
+ if (this.#borrowedReturnReleaser !== undefined) {
359
+ this.#owned = region;
360
+ return region;
361
+ }
362
+ (this.#materializedRegions ??= []).push(region);
363
+ return region;
364
+ }
365
+ toArrayBuffer() {
366
+ const region = this.#materialize();
367
+ if (region.byteOffset === 0 && region.buffer.byteLength === region.byteLength) {
368
+ return region.buffer;
369
+ }
370
+ return region.buffer.slice(region.byteOffset, region.byteOffset + region.byteLength);
371
+ }
372
+ toUint8Array() {
373
+ const region = this.#materialize();
374
+ return new Uint8Array(region.buffer, region.byteOffset, region.byteLength);
375
+ }
376
+ /** The source is moved on construction, so it is never retained here. */
377
+ get source() {
378
+ return undefined;
379
+ }
380
+ release() {
381
+ if (this.#released)
382
+ return;
383
+ this.#released = true;
384
+ const regions = this.#materializedRegions;
385
+ this.#materializedRegions = undefined;
386
+ if (regions !== undefined) {
387
+ for (let i = 0; i < regions.length; i++) {
388
+ detachArrayBufferBestEffort(this.runtime, regions[i].buffer);
389
+ }
390
+ }
391
+ if (this.#borrowedReturnFinalizerToken !== undefined) {
392
+ borrowedReturnFinalizer?.unregister(this.#borrowedReturnFinalizerToken);
393
+ this.#borrowedReturnFinalizerToken = undefined;
394
+ }
395
+ const borrowedReturnReleaser = this.#borrowedReturnReleaser;
396
+ this.#borrowedReturnReleaser = undefined;
397
+ if (borrowedReturnReleaser !== undefined) {
398
+ const owned = this.#owned;
399
+ this.#owned = undefined;
400
+ if (owned !== undefined) {
401
+ detachArrayBufferBestEffort(this.runtime, owned.buffer);
402
+ }
403
+ try {
404
+ borrowedReturnReleaser(this.#token);
405
+ }
406
+ catch {
407
+ // best effort
408
+ }
409
+ }
410
+ if (this.#finalizerToken !== undefined) {
411
+ producerFinalizer?.unregister(this.#finalizerToken);
412
+ this.#finalizerToken = undefined;
413
+ }
414
+ // Only the producer owns the registry hold; consumers must not drop it.
415
+ if (this.#isProducer) {
416
+ try {
417
+ getBufferReferenceCapabilities().release(this.#token);
418
+ }
419
+ catch {
420
+ // best effort
421
+ }
422
+ }
423
+ }
424
+ [Symbol.dispose]() {
425
+ this.release();
426
+ }
427
+ [PAYLOAD_TRANSPORT_FINALIZER]() {
428
+ if (this.#released)
429
+ return undefined;
430
+ this.#transportRefs++;
431
+ let finalized = false;
432
+ const finalizer = (() => {
433
+ if (finalized)
434
+ return;
435
+ finalized = true;
436
+ this.#transportRefs--;
437
+ if (this.#transportRefs <= 0)
438
+ this.release();
439
+ });
440
+ if (this.#isProducer) {
441
+ finalizer[BUFFER_REFERENCE_RETURN_RELEASE_TOKEN] = this.#token;
442
+ }
443
+ return finalizer;
444
+ }
445
+ }
446
+ const codecGlobal = globalThis;
447
+ const codecs = codecGlobal.__KNITTING_PAYLOAD_CODECS__ ??= Object.create(null);
448
+ codecs[BUFFER_REFERENCE_CODEC_ID] = {
449
+ decode: (metadata) => {
450
+ const ref = BufferReference.fromMetadata(metadata);
451
+ // Host decodes are return values; claim before the worker drains.
452
+ // Worker decodes are forward inputs and stay lazy for the call.
453
+ return RUNTIME_IS_MAIN_THREAD ? ref.claimOwnership() : ref;
454
+ },
455
+ decodeNumeric: (metadata) => {
456
+ const ref = BufferReference.fromMetadata(numericMetadataToMetadata(metadata));
457
+ return RUNTIME_IS_MAIN_THREAD ? ref.claimOwnership() : ref;
458
+ },
459
+ };
@@ -1,3 +1,4 @@
1
1
  export { alignToCacheLine, CACHE_LINE_SIZE, type ConnectionRuntime, type CreateSharedMemoryOptions, expectFd, expectPositiveSize, type MapSharedMemoryOptions, requireSharedArrayBuffer, type SharedMemoryBuffer, type SharedMemoryBufferKind, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping, } from "./types.js";
2
+ export { BufferReference, BUFFER_REFERENCE_KIND, type BufferReferenceMetadata, type BufferReferenceRuntime, isBufferReferenceMetadata, } from "./buffer-reference.js";
2
3
  export { FileDescriptor, type FileDescriptorMetadata, parseFileDescriptorMetadata, } from "./file-descriptor.js";
3
4
  export { getDefaultProcessSharedBufferPrimitives, parseProcessSharedBufferMetadata, PROCESS_SHARED_BUFFER_BRAND, ProcessSharedBuffer, type ProcessSharedBufferCreator, type ProcessSharedBufferMapper, type ProcessSharedBufferMetadata, type ProcessSharedBufferPrimitives, type ProcessSharedBufferRange, type ProcessSharedBufferView, type ProcessSharedBufferViewConstructor, setDefaultProcessSharedBufferPrimitives, } from "./process-shared-buffer.js";
@@ -1,3 +1,4 @@
1
1
  export { alignToCacheLine, CACHE_LINE_SIZE, expectFd, expectPositiveSize, requireSharedArrayBuffer, } from "./types.js";
2
+ export { BufferReference, BUFFER_REFERENCE_KIND, isBufferReferenceMetadata, } from "./buffer-reference.js";
2
3
  export { FileDescriptor, parseFileDescriptorMetadata, } from "./file-descriptor.js";
3
4
  export { getDefaultProcessSharedBufferPrimitives, parseProcessSharedBufferMetadata, PROCESS_SHARED_BUFFER_BRAND, ProcessSharedBuffer, setDefaultProcessSharedBufferPrimitives, } from "./process-shared-buffer.js";
@@ -1,5 +1,5 @@
1
1
  type NodeRequire = (specifier: string) => unknown;
2
- export type NodeNativeAddonName = "knitting_shared_memory" | "knitting_shm";
2
+ export type NodeNativeAddonName = "knitting_shared_memory" | "knitting_shm" | "knitting_buffer_pointer";
3
3
  export declare const nodeNativeAddonSpecifiers: (name: NodeNativeAddonName) => readonly string[];
4
4
  export declare const loadNodeNativeAddon: <T>(require: NodeRequire, name: NodeNativeAddonName, specifier?: string) => T;
5
5
  export {};
@@ -0,0 +1,20 @@
1
+ export type NodeBufferPointerAddon = {
2
+ getPointer: (buffer: ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => bigint;
3
+ retainPointer: (buffer: ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => {
4
+ pointer: bigint;
5
+ byteLength: number;
6
+ token: bigint;
7
+ };
8
+ releasePointer: (token: bigint) => boolean;
9
+ wrapPointer: (pointer: bigint, byteLength: number) => ArrayBuffer;
10
+ detachArrayBuffer: (buffer: ArrayBuffer) => boolean;
11
+ retainBackingStore?: (buffer: ArrayBufferView | ArrayBuffer) => {
12
+ pointer: bigint;
13
+ byteOffset: number;
14
+ byteLength: number;
15
+ token: bigint;
16
+ };
17
+ adoptBackingStore?: (token: bigint) => ArrayBuffer;
18
+ releaseBackingStore?: (token: bigint) => boolean;
19
+ };
20
+ export declare const loadNodeBufferPointerAddon: (specifier?: string) => NodeBufferPointerAddon;
@@ -0,0 +1,16 @@
1
+ import { getNodeBuiltinModule } from "../common/node-compat.js";
2
+ import { loadNodeNativeAddon } from "./node-addons.js";
3
+ let cached;
4
+ export const loadNodeBufferPointerAddon = (specifier) => {
5
+ if (specifier === undefined && cached !== undefined)
6
+ return cached;
7
+ const nodeModule = getNodeBuiltinModule("node:module");
8
+ if (nodeModule === undefined) {
9
+ throw new Error("Node buffer-pointer addon can only be loaded in Node");
10
+ }
11
+ const require = nodeModule.createRequire(import.meta.url);
12
+ const addon = loadNodeNativeAddon(require, "knitting_buffer_pointer", specifier);
13
+ if (specifier === undefined)
14
+ cached = addon;
15
+ return addon;
16
+ };
@@ -150,6 +150,8 @@ export class ProcessSharedBuffer {
150
150
  static parse(serialized) {
151
151
  return ProcessSharedBuffer.fromMetadata(serialized);
152
152
  }
153
+ // Rebuilds from the 8 raw words (no name -> no JSON):
154
+ // fd, size, descByteLength, byteOffset, byteLength, runtime, kind, baseAddressMod64
153
155
  static [PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER](metadata) {
154
156
  const [fd, size, descriptorByteLength, byteOffset, byteLength, runtime, kind, baseAddressMod64,] = metadata;
155
157
  return new ProcessSharedBuffer(new FileDescriptor({
@@ -0,0 +1,36 @@
1
+ export declare const SHARED_ARRAY_BUFFER_CODEC_ID: "knitting.sharedArrayBuffer";
2
+ export declare const SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER: unique symbol;
3
+ export declare const SHARED_ARRAY_BUFFER_NUMERIC_WORDS = 8;
4
+ declare const EXTERNAL_PAYLOAD_BRAND: unique symbol;
5
+ type SharedArrayBufferMetadata = {
6
+ readonly kind: typeof SHARED_ARRAY_BUFFER_CODEC_ID;
7
+ readonly origin: string;
8
+ readonly runtime: string;
9
+ readonly pointer: string;
10
+ readonly token: string;
11
+ readonly byteLength: number;
12
+ };
13
+ type SharedArrayBufferNumericMetadata = readonly [
14
+ tokenLow: number,
15
+ tokenHigh: number,
16
+ pointerLow: number,
17
+ pointerHigh: number,
18
+ byteLength: number,
19
+ runtime: number,
20
+ originPid: number,
21
+ mode: number
22
+ ];
23
+ type SharedArrayBufferTokenNumericMetadata = readonly [
24
+ tokenLow: number,
25
+ tokenHigh: number
26
+ ];
27
+ type SharedArrayBufferPayload = {
28
+ readonly [EXTERNAL_PAYLOAD_BRAND]: typeof SHARED_ARRAY_BUFFER_CODEC_ID;
29
+ readonly toMetadata: () => SharedArrayBufferMetadata;
30
+ readonly [SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER]: (transportKey?: object) => SharedArrayBufferNumericMetadata | SharedArrayBufferTokenNumericMetadata | undefined;
31
+ };
32
+ export declare const isSharedArrayBufferValue: (value: unknown) => value is SharedArrayBuffer;
33
+ /** Wrap a SAB as external payload; GC-managed pins mean no settle finalizer. */
34
+ export declare const wrapSharedArrayBufferPayload: (sab: SharedArrayBuffer) => SharedArrayBufferPayload;
35
+ export declare const getSharedArrayBufferPayload: (value: object) => SharedArrayBufferPayload | undefined;
36
+ export {};