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,217 @@
1
+ import { RUNTIME } from "../common/runtime.js";
2
+ import { loadNodeBufferPointerAddon } from "./node-buffer-pointer.js";
3
+ const isSharedArrayBufferInstance = (value) => typeof SharedArrayBuffer === "function" && value instanceof SharedArrayBuffer;
4
+ const assertMovableSource = (source) => {
5
+ const buffer = ArrayBuffer.isView(source) ? source.buffer : source;
6
+ if (isSharedArrayBufferInstance(buffer)) {
7
+ throw new TypeError("BufferReference expects ArrayBuffer-backed views; SharedArrayBuffer is already " +
8
+ "shared and cannot be moved");
9
+ }
10
+ };
11
+ const readSourceRegion = (source) => {
12
+ if (ArrayBuffer.isView(source)) {
13
+ return {
14
+ buffer: source.buffer,
15
+ byteOffset: source.byteOffset,
16
+ byteLength: source.byteLength,
17
+ };
18
+ }
19
+ return { buffer: source, byteOffset: 0, byteLength: source.byteLength };
20
+ };
21
+ /** Detach `buffer`, returning a fixed-length buffer over the same bytes (the move). */
22
+ const detachIntoFixedLength = (buffer) => {
23
+ const transferable = buffer;
24
+ if (typeof transferable.transferToFixedLength === "function") {
25
+ return transferable.transferToFixedLength();
26
+ }
27
+ if (typeof transferable.transfer === "function") {
28
+ return transferable.transfer();
29
+ }
30
+ throw new TypeError("BufferReference requires ArrayBuffer.prototype.transfer to move a buffer in this runtime");
31
+ };
32
+ // ---------------------------------------------------------------------------
33
+ // Deno/Bun pin moved buffers in JS and expose non-owning FFI aliases.
34
+ // Tokens stay producer-isolate-local; consumers materialize from the pointer.
35
+ // ---------------------------------------------------------------------------
36
+ const ffiPinned = new Map();
37
+ let nextFfiToken = 1n;
38
+ const pinTransferred = (buffer) => {
39
+ const token = nextFfiToken++;
40
+ ffiPinned.set(token, buffer);
41
+ return token;
42
+ };
43
+ const pinShared = (sab) => {
44
+ const token = nextFfiToken++;
45
+ ffiPinned.set(token, sab);
46
+ return token;
47
+ };
48
+ const releaseFfi = (token) => {
49
+ ffiPinned.delete(token);
50
+ };
51
+ const produceSharedFfi = (sab, readPointer) => {
52
+ // SABs are shared, not detached; pin keeps producer bytes alive.
53
+ const pointer = readPointer(new Uint8Array(sab));
54
+ const token = pinShared(sab);
55
+ return { token, pointer, byteOffset: 0, byteLength: sab.byteLength };
56
+ };
57
+ const getDeno = () => {
58
+ const deno = globalThis.Deno;
59
+ if (deno?.UnsafePointer === undefined || deno.UnsafePointerView === undefined) {
60
+ throw new Error("Deno FFI (UnsafePointer) is not available in this runtime");
61
+ }
62
+ return deno;
63
+ };
64
+ const getBunFFI = () => {
65
+ const ffi = globalThis.Bun?.FFI;
66
+ if (typeof ffi?.ptr !== "function" || typeof ffi?.toArrayBuffer !== "function") {
67
+ throw new Error("Bun FFI (ptr/toArrayBuffer) is not available in this runtime");
68
+ }
69
+ return ffi;
70
+ };
71
+ const produceFfi = (source, readPointer) => {
72
+ assertMovableSource(source);
73
+ const { buffer, byteOffset, byteLength } = readSourceRegion(source);
74
+ const moved = detachIntoFixedLength(buffer);
75
+ // Region view over the moved buffer; the pointer captures the offset.
76
+ const region = new Uint8Array(moved, byteOffset, byteLength);
77
+ const pointer = readPointer(region);
78
+ const token = pinTransferred(moved);
79
+ return { token, pointer, byteOffset, byteLength };
80
+ };
81
+ const finishAlias = (alias, copy) => {
82
+ if (copy) {
83
+ const owned = alias.slice(0);
84
+ return { buffer: owned, byteOffset: 0, byteLength: owned.byteLength };
85
+ }
86
+ return { buffer: alias, byteOffset: 0, byteLength: alias.byteLength };
87
+ };
88
+ const createDenoCapabilities = () => {
89
+ const deno = getDeno();
90
+ return {
91
+ runtime: "deno",
92
+ supportsOwningAdopt: false,
93
+ produce: (view) => produceFfi(view, (region) => deno.UnsafePointer.value(deno.UnsafePointer.of(region))),
94
+ adopt: ({ pointer, byteLength }, opts) => {
95
+ const alias = denoAlias(deno, pointer, byteLength);
96
+ return finishAlias(alias, opts?.copy);
97
+ },
98
+ release: releaseFfi,
99
+ supportsSharedAdopt: false,
100
+ produceShared: (sab) => produceSharedFfi(sab, (region) => deno.UnsafePointer.value(deno.UnsafePointer.of(region))),
101
+ adoptShared: ({ pointer, byteLength }) => {
102
+ const alias = denoAlias(deno, pointer, byteLength);
103
+ return { buffer: alias, byteOffset: 0, byteLength, isShared: false };
104
+ },
105
+ releaseShared: releaseFfi,
106
+ };
107
+ };
108
+ const denoAlias = (deno, pointer, byteLength) => {
109
+ const handle = deno.UnsafePointer.create(pointer);
110
+ if (handle === null || handle === undefined) {
111
+ throw new Error("Deno.UnsafePointer.create returned null");
112
+ }
113
+ return new deno.UnsafePointerView(handle).getArrayBuffer(byteLength);
114
+ };
115
+ const createBunCapabilities = () => {
116
+ const ffi = getBunFFI();
117
+ return {
118
+ runtime: "bun",
119
+ supportsOwningAdopt: false,
120
+ produce: (view) => produceFfi(view, (region) => BigInt(ffi.ptr(region))),
121
+ adopt: ({ pointer, byteLength }, opts) => {
122
+ const alias = ffi.toArrayBuffer(Number(pointer), 0, byteLength);
123
+ return finishAlias(alias, opts?.copy);
124
+ },
125
+ release: releaseFfi,
126
+ supportsSharedAdopt: false,
127
+ produceShared: (sab) => produceSharedFfi(sab, (region) => BigInt(ffi.ptr(region))),
128
+ adoptShared: ({ pointer, byteLength }) => {
129
+ const alias = ffi.toArrayBuffer(Number(pointer), 0, byteLength);
130
+ return { buffer: alias, byteOffset: 0, byteLength, isShared: false };
131
+ },
132
+ releaseShared: releaseFfi,
133
+ };
134
+ };
135
+ // ---------------------------------------------------------------------------
136
+ // Node: addon-backed owning move via shared_ptr<BackingStore>.
137
+ // Older prebuilds fall back to non-owning retain/wrap.
138
+ // ---------------------------------------------------------------------------
139
+ const createNodeCapabilities = () => {
140
+ const addon = loadNodeBufferPointerAddon();
141
+ const owning = typeof addon.retainBackingStore === "function" &&
142
+ typeof addon.adoptBackingStore === "function" &&
143
+ typeof addon.releaseBackingStore === "function";
144
+ // SABs use JS pins plus non-owning aliases so no native handle survives teardown.
145
+ const sharedCapabilities = {
146
+ supportsSharedAdopt: false,
147
+ produceShared: (sab) => produceSharedFfi(sab, (region) => addon.getPointer(region)),
148
+ adoptShared: ({ pointer, byteLength }) => {
149
+ const alias = addon.wrapPointer(pointer, byteLength);
150
+ return { buffer: alias, byteOffset: 0, byteLength, isShared: false };
151
+ },
152
+ releaseShared: releaseFfi,
153
+ };
154
+ if (owning) {
155
+ return {
156
+ runtime: "node",
157
+ supportsOwningAdopt: true,
158
+ produce: (source) => {
159
+ assertMovableSource(source);
160
+ const r = addon.retainBackingStore(source);
161
+ return {
162
+ token: r.token,
163
+ pointer: r.pointer,
164
+ byteOffset: r.byteOffset,
165
+ byteLength: r.byteLength,
166
+ };
167
+ },
168
+ adopt: ({ token, byteOffset, byteLength }, opts) => {
169
+ const buffer = addon.adoptBackingStore(token);
170
+ if (opts?.copy) {
171
+ const owned = buffer.slice(byteOffset, byteOffset + byteLength);
172
+ return { buffer: owned, byteOffset: 0, byteLength: owned.byteLength };
173
+ }
174
+ return { buffer, byteOffset, byteLength };
175
+ },
176
+ release: (token) => void addon.releaseBackingStore(token),
177
+ ...sharedCapabilities,
178
+ };
179
+ }
180
+ // Legacy fallback: producer pins via retainPointer, consumer wraps non-owning.
181
+ return {
182
+ runtime: "node",
183
+ supportsOwningAdopt: false,
184
+ produce: (source) => {
185
+ assertMovableSource(source);
186
+ const retained = addon.retainPointer(source);
187
+ const byteOffset = ArrayBuffer.isView(source) ? source.byteOffset : 0;
188
+ return {
189
+ token: retained.token,
190
+ pointer: retained.pointer,
191
+ byteOffset,
192
+ byteLength: retained.byteLength,
193
+ };
194
+ },
195
+ adopt: ({ pointer, byteLength }, opts) => {
196
+ const alias = addon.wrapPointer(pointer, byteLength);
197
+ return finishAlias(alias, opts?.copy);
198
+ },
199
+ release: (token) => void addon.releasePointer(token),
200
+ ...sharedCapabilities,
201
+ };
202
+ };
203
+ let cached;
204
+ export const getBufferReferenceCapabilities = () => {
205
+ if (cached !== undefined)
206
+ return cached;
207
+ switch (RUNTIME) {
208
+ case "node":
209
+ return cached = createNodeCapabilities();
210
+ case "deno":
211
+ return cached = createDenoCapabilities();
212
+ case "bun":
213
+ return cached = createBunCapabilities();
214
+ default:
215
+ throw new Error(`BufferReference cannot run in runtime "${RUNTIME}"`);
216
+ }
217
+ };
@@ -0,0 +1,76 @@
1
+ import { type BufferReferenceRuntime } from "./buffer-reference-native.js";
2
+ export type { BufferReferenceRuntime } from "./buffer-reference-native.js";
3
+ export declare const BUFFER_REFERENCE_KIND: "knitting.bufferReference";
4
+ /** Named values for the `unsafe.BufferReferenceReturn` pool option. */
5
+ export declare const BufferReferenceReturn: {
6
+ /** Safe default: Deno/Bun copy the returned bytes so they outlive the worker. */
7
+ readonly Copy: "copy";
8
+ /** Zero-copy: borrow the worker's backing store until the reference is released. */
9
+ readonly Borrow: "borrow";
10
+ };
11
+ export type BufferReferenceReturn = (typeof BufferReferenceReturn)[keyof typeof BufferReferenceReturn];
12
+ export declare const BUFFER_REFERENCE_NUMERIC_TRANSFER: unique symbol;
13
+ export declare const BUFFER_REFERENCE_RETURN_RELEASE_TOKEN: unique symbol;
14
+ declare const EXTERNAL_PAYLOAD_BRAND: unique symbol;
15
+ export declare const BUFFER_REFERENCE_CODEC_ID: "knitting.bufferReference";
16
+ declare const BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY = "__knittingBufferReferenceRelease";
17
+ export type BufferReferenceNumericMetadata = readonly [
18
+ pointerLow: number,
19
+ pointerHigh: number,
20
+ tokenLow: number,
21
+ tokenHigh: number,
22
+ byteOffset: number,
23
+ byteLength: number,
24
+ runtime: number,
25
+ originPid: number
26
+ ];
27
+ export type BufferReferenceMetadata = {
28
+ readonly kind: typeof BUFFER_REFERENCE_KIND;
29
+ readonly origin: string;
30
+ readonly runtime: BufferReferenceRuntime;
31
+ readonly pointer: string;
32
+ /** Producer-side release handle (process-local). */
33
+ readonly token: string;
34
+ readonly byteOffset: number;
35
+ readonly byteLength: number;
36
+ };
37
+ export type BufferReferenceReturnReleaseMessage = {
38
+ readonly [BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY]: string;
39
+ };
40
+ type BufferReferenceReturnReleaser = (token: bigint) => void;
41
+ type BufferSource = ArrayBufferView | ArrayBuffer;
42
+ declare const PAYLOAD_TRANSPORT_FINALIZER: unique symbol;
43
+ export declare const withBufferReferenceReturnReleaser: <T>(releaser: BufferReferenceReturnReleaser | undefined, run: () => T) => T;
44
+ export declare const createBufferReferenceReturnReleaseMessage: (token: bigint) => BufferReferenceReturnReleaseMessage;
45
+ export declare const readBufferReferenceReturnReleaseMessage: (value: unknown) => bigint | undefined;
46
+ export declare const isBufferReferenceMetadata: (value: unknown) => value is BufferReferenceMetadata;
47
+ /**
48
+ * Zero-copy handle for moving ArrayBuffer bytes to **thread** workers.
49
+ *
50
+ * Construction detaches the source. Consumers materialize the moved region in
51
+ * their isolate: owning on Node, alias/copy on Deno/Bun.
52
+ */
53
+ export declare class BufferReference {
54
+ #private;
55
+ readonly [EXTERNAL_PAYLOAD_BRAND]: "knitting.bufferReference";
56
+ readonly runtime: BufferReferenceRuntime;
57
+ readonly origin: string;
58
+ readonly pointer: bigint;
59
+ readonly byteOffset: number;
60
+ readonly byteLength: number;
61
+ constructor(source: BufferSource);
62
+ constructor(source: BufferSource | undefined, meta: BufferReferenceMetadata);
63
+ static fromMetadata(meta: BufferReferenceMetadata): BufferReference;
64
+ toMetadata(): BufferReferenceMetadata;
65
+ [BUFFER_REFERENCE_NUMERIC_TRANSFER](): BufferReferenceNumericMetadata | undefined;
66
+ get isLocal(): boolean;
67
+ /** Prepare a returned reference before the worker-side producer hold drains. */
68
+ claimOwnership(releaser?: BufferReferenceReturnReleaser): this;
69
+ toArrayBuffer(): ArrayBuffer;
70
+ toUint8Array(): Uint8Array;
71
+ /** The source is moved on construction, so it is never retained here. */
72
+ get source(): undefined;
73
+ release(): void;
74
+ [Symbol.dispose](): void;
75
+ [PAYLOAD_TRANSPORT_FINALIZER](): (() => void) | undefined;
76
+ }