knitting 0.1.46

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 (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +632 -0
  3. package/knitting.d.ts +4 -0
  4. package/knitting.js +5 -0
  5. package/map.md +264 -0
  6. package/package.json +77 -0
  7. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  8. package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
  9. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  10. package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
  11. package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
  12. package/prebuilds/darwin-x64-node-127/knitting_shm.node +0 -0
  13. package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
  14. package/prebuilds/darwin-x64-node-137/knitting_shm.node +0 -0
  15. package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
  16. package/prebuilds/linux-x64-node-127/knitting_shm.node +0 -0
  17. package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
  18. package/prebuilds/linux-x64-node-137/knitting_shm.node +0 -0
  19. package/process-shared-buffer.d.ts +1 -0
  20. package/process-shared-buffer.js +1 -0
  21. package/scripts/build-native-addons.ts +295 -0
  22. package/src/api.d.ts +55 -0
  23. package/src/api.js +384 -0
  24. package/src/common/envelope.d.ts +11 -0
  25. package/src/common/envelope.js +8 -0
  26. package/src/common/module-url.d.ts +1 -0
  27. package/src/common/module-url.js +24 -0
  28. package/src/common/node-compat.d.ts +20 -0
  29. package/src/common/node-compat.js +24 -0
  30. package/src/common/path-canonical.d.ts +6 -0
  31. package/src/common/path-canonical.js +41 -0
  32. package/src/common/runtime.d.ts +15 -0
  33. package/src/common/runtime.js +91 -0
  34. package/src/common/shared-buffer-region.d.ts +11 -0
  35. package/src/common/shared-buffer-region.js +21 -0
  36. package/src/common/shared-buffer-text.d.ts +16 -0
  37. package/src/common/shared-buffer-text.js +65 -0
  38. package/src/common/task-source.d.ts +2 -0
  39. package/src/common/task-source.js +79 -0
  40. package/src/common/task-symbol.d.ts +1 -0
  41. package/src/common/task-symbol.js +1 -0
  42. package/src/common/with-resolvers.d.ts +9 -0
  43. package/src/common/with-resolvers.js +23 -0
  44. package/src/common/worker-runtime.d.ts +40 -0
  45. package/src/common/worker-runtime.js +52 -0
  46. package/src/connections/bun.d.ts +20 -0
  47. package/src/connections/bun.js +159 -0
  48. package/src/connections/deno.d.ts +20 -0
  49. package/src/connections/deno.js +150 -0
  50. package/src/connections/file-descriptor.d.ts +37 -0
  51. package/src/connections/file-descriptor.js +139 -0
  52. package/src/connections/index.d.ts +3 -0
  53. package/src/connections/index.js +3 -0
  54. package/src/connections/node-addons.d.ts +5 -0
  55. package/src/connections/node-addons.js +43 -0
  56. package/src/connections/node.d.ts +29 -0
  57. package/src/connections/node.js +59 -0
  58. package/src/connections/posix.d.ts +31 -0
  59. package/src/connections/posix.js +71 -0
  60. package/src/connections/process-shared-buffer.d.ts +67 -0
  61. package/src/connections/process-shared-buffer.js +267 -0
  62. package/src/connections/types.d.ts +48 -0
  63. package/src/connections/types.js +37 -0
  64. package/src/error.d.ts +13 -0
  65. package/src/error.js +49 -0
  66. package/src/ipc/tools/ring-queue.d.ts +33 -0
  67. package/src/ipc/tools/ring-queue.js +159 -0
  68. package/src/ipc/transport/shared-memory.d.ts +25 -0
  69. package/src/ipc/transport/shared-memory.js +35 -0
  70. package/src/knitting_shared_memory.cc +436 -0
  71. package/src/knitting_shm.cc +476 -0
  72. package/src/memory/byte-carpet.d.ts +73 -0
  73. package/src/memory/byte-carpet.js +157 -0
  74. package/src/memory/lock.d.ts +190 -0
  75. package/src/memory/lock.js +856 -0
  76. package/src/memory/payload-config.d.ts +22 -0
  77. package/src/memory/payload-config.js +67 -0
  78. package/src/memory/payloadCodec.d.ts +46 -0
  79. package/src/memory/payloadCodec.js +1157 -0
  80. package/src/memory/regionRegistry.d.ts +17 -0
  81. package/src/memory/regionRegistry.js +285 -0
  82. package/src/memory/shared-buffer-io.d.ts +53 -0
  83. package/src/memory/shared-buffer-io.js +380 -0
  84. package/src/permission/index.d.ts +2 -0
  85. package/src/permission/index.js +2 -0
  86. package/src/permission/protocol.d.ts +166 -0
  87. package/src/permission/protocol.js +640 -0
  88. package/src/runtime/balancer.d.ts +19 -0
  89. package/src/runtime/balancer.js +149 -0
  90. package/src/runtime/dispatcher.d.ts +34 -0
  91. package/src/runtime/dispatcher.js +142 -0
  92. package/src/runtime/inline-executor.d.ts +10 -0
  93. package/src/runtime/inline-executor.js +270 -0
  94. package/src/runtime/pool.d.ts +43 -0
  95. package/src/runtime/pool.js +922 -0
  96. package/src/runtime/tx-queue.d.ts +25 -0
  97. package/src/runtime/tx-queue.js +144 -0
  98. package/src/shared/abortSignal.d.ts +23 -0
  99. package/src/shared/abortSignal.js +126 -0
  100. package/src/types.d.ts +283 -0
  101. package/src/types.js +2 -0
  102. package/src/worker/composable-runners.d.ts +12 -0
  103. package/src/worker/composable-runners.js +105 -0
  104. package/src/worker/loop.d.ts +2 -0
  105. package/src/worker/loop.js +453 -0
  106. package/src/worker/rx-queue.d.ts +22 -0
  107. package/src/worker/rx-queue.js +124 -0
  108. package/src/worker/safety/index.d.ts +4 -0
  109. package/src/worker/safety/index.js +4 -0
  110. package/src/worker/safety/performance.d.ts +1 -0
  111. package/src/worker/safety/performance.js +17 -0
  112. package/src/worker/safety/process.d.ts +2 -0
  113. package/src/worker/safety/process.js +79 -0
  114. package/src/worker/safety/startup.d.ts +16 -0
  115. package/src/worker/safety/startup.js +30 -0
  116. package/src/worker/safety/worker-data.d.ts +2 -0
  117. package/src/worker/safety/worker-data.js +36 -0
  118. package/src/worker/task-loader.d.ts +26 -0
  119. package/src/worker/task-loader.js +66 -0
  120. package/src/worker/timers.d.ts +18 -0
  121. package/src/worker/timers.js +97 -0
@@ -0,0 +1,922 @@
1
+ // main.ts
2
+ import { fileURLToPath as fileURLToPathCompat } from "node:url";
3
+ import { createHostTxQueue } from "./tx-queue.js";
4
+ import { createSharedMemoryTransport, TRANSPORT_SIGNAL_BYTES, } from "../ipc/transport/shared-memory.js";
5
+ import { ChannelHandler, hostDispatcherLoop } from "./dispatcher.js";
6
+ import { HEADER_SLOT_STRIDE_U32, lock2, LOCK_SECTOR_BYTE_LENGTH, LockBound, } from "../memory/lock.js";
7
+ import "../worker/loop.js";
8
+ import { createSharedArrayBuffer, createWasmSharedArrayBuffer, RUNTIME, } from "../common/runtime.js";
9
+ import { HAS_NODE_WORKER_THREADS, RUNTIME_PROCESS_WORKER_BOOT_ENV, RUNTIME_PROCESS_WORKER_BOOT_VERSION, RUNTIME_PROCESS_WORKER_ENV, RUNTIME_WORKER, } from "../common/worker-runtime.js";
10
+ import { toSharedBufferRegion, } from "../common/shared-buffer-region.js";
11
+ import { probeLockBufferTextCompat } from "../common/shared-buffer-text.js";
12
+ import { signalAbortFactory } from "../shared/abortSignal.js";
13
+ import { createByteCarpet, createLockControlCarpet, getHeaderBlockByteLength, makeSharedBufferRegion, } from "../memory/byte-carpet.js";
14
+ import { resolvePayloadBufferOptions, } from "../memory/payload-config.js";
15
+ import { getNodeBuiltinModule, getNodeProcess, } from "../common/node-compat.js";
16
+ import { createBunConnectionPrimitives } from "../connections/bun.js";
17
+ import { createDenoConnectionPrimitives } from "../connections/deno.js";
18
+ import { FileDescriptor, ProcessSharedBuffer, } from "../connections/index.js";
19
+ import { createNodeConnectionPrimitives, loadNodeFutexAddon, } from "../connections/node.js";
20
+ import { loadNodeNativeAddon } from "../connections/node-addons.js";
21
+ import { assertPosixSharedMemoryPlatform, detectPosixPlatform, } from "../connections/posix.js";
22
+ const WORKER_FATAL_MESSAGE_KEY = "__knittingWorkerFatal";
23
+ const execFlagKey = (flag) => flag.split("=", 1)[0];
24
+ const NODE_PERMISSION_EXEC_FLAGS = new Set([
25
+ "--permission",
26
+ "--experimental-permission",
27
+ "--allow-fs-read",
28
+ "--allow-fs-write",
29
+ "--allow-worker",
30
+ "--allow-child-process",
31
+ "--allow-addons",
32
+ "--allow-wasi",
33
+ ]);
34
+ const NODE_WORKER_SAFE_EXEC_FLAGS = new Set([
35
+ "--experimental-transform-types",
36
+ "--expose-gc",
37
+ "--no-warnings",
38
+ ...NODE_PERMISSION_EXEC_FLAGS,
39
+ ]);
40
+ const isWorkerFatalMessage = (value) => !!value &&
41
+ typeof value === "object" &&
42
+ typeof value[WORKER_FATAL_MESSAGE_KEY] === "string";
43
+ const isNodeWorkerSafeExecFlag = (flag) => NODE_WORKER_SAFE_EXEC_FLAGS.has(execFlagKey(flag));
44
+ const isNodePermissionExecFlag = (flag) => NODE_PERMISSION_EXEC_FLAGS.has(execFlagKey(flag));
45
+ const toWorkerSafeExecArgv = (flags) => {
46
+ if (!flags || flags.length === 0)
47
+ return undefined;
48
+ const filtered = flags.filter(isNodeWorkerSafeExecFlag);
49
+ if (filtered.length === 0)
50
+ return undefined;
51
+ const seen = new Set();
52
+ const deduped = [];
53
+ for (const flag of filtered) {
54
+ if (seen.has(flag))
55
+ continue;
56
+ seen.add(flag);
57
+ deduped.push(flag);
58
+ }
59
+ return deduped;
60
+ };
61
+ const toWorkerCompatExecArgv = (flags) => {
62
+ const safe = toWorkerSafeExecArgv(flags);
63
+ if (!safe || safe.length === 0)
64
+ return undefined;
65
+ const compat = safe.filter((flag) => !isNodePermissionExecFlag(flag));
66
+ return compat.length > 0 ? compat : undefined;
67
+ };
68
+ // Keep idle workers self-healing if an Atomics.notify wake is missed.
69
+ const DEFAULT_WORKER_PARK_MS = 1;
70
+ const withDefaultWorkerTimers = (options) => {
71
+ const parkMs = options?.timers?.parkMs ?? DEFAULT_WORKER_PARK_MS;
72
+ if (options === undefined)
73
+ return { timers: { parkMs } };
74
+ return {
75
+ ...options,
76
+ timers: {
77
+ ...options.timers,
78
+ parkMs,
79
+ },
80
+ };
81
+ };
82
+ const toProcessSharedMemorySize = (byteLength) => {
83
+ if (!Number.isFinite(byteLength) || byteLength <= 0) {
84
+ throw new RangeError("process shared memory byteLength must be positive");
85
+ }
86
+ const size = Math.trunc(byteLength);
87
+ return size + ((64 - (size % 64)) % 64);
88
+ };
89
+ const createProcessSharedMemoryAllocator = (debug) => {
90
+ if (RUNTIME !== "node")
91
+ return undefined;
92
+ try {
93
+ assertPosixSharedMemoryPlatform("Process-shared memory allocator");
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ let addon;
99
+ try {
100
+ const nodeModule = getNodeBuiltinModule("node:module");
101
+ if (nodeModule === undefined)
102
+ return undefined;
103
+ const require = nodeModule.createRequire(import.meta.url);
104
+ addon = loadNodeNativeAddon(require, "knitting_shared_memory");
105
+ }
106
+ catch (error) {
107
+ if (debug?.extras === true) {
108
+ console.warn("Process-shared memory allocator unavailable; falling back to SharedArrayBuffer.", error);
109
+ }
110
+ return undefined;
111
+ }
112
+ const backings = [];
113
+ return {
114
+ backings,
115
+ createBuffer: (byteLength) => {
116
+ const mapping = addon.createSharedMemory(toProcessSharedMemorySize(byteLength));
117
+ backings.push({
118
+ ...mapping,
119
+ runtime: "node",
120
+ buffer: mapping.sab,
121
+ kind: "shared-array-buffer",
122
+ byteLength: mapping.sab.byteLength,
123
+ });
124
+ return mapping.sab;
125
+ },
126
+ };
127
+ };
128
+ const PROCESS_WORKER_CHILD_FD = 0;
129
+ const DEFAULT_BUN_BINARY = "bun";
130
+ const DEFAULT_DENO_BINARY = "deno";
131
+ const DEFAULT_NODE_BINARY = "node";
132
+ const DENO_PROCESS_WORKER_BOOT_ENV_ALLOW = [
133
+ RUNTIME_PROCESS_WORKER_ENV,
134
+ RUNTIME_PROCESS_WORKER_BOOT_ENV,
135
+ ].join(",");
136
+ const DENO_PROCESS_WORKER_INTERNAL_FLAGS = [
137
+ `--allow-env=${DENO_PROCESS_WORKER_BOOT_ENV_ALLOW}`,
138
+ "--allow-ffi",
139
+ ];
140
+ const NODE_PROCESS_WORKER_EXEC_ARGV = [
141
+ "--no-warnings",
142
+ "--experimental-transform-types",
143
+ ];
144
+ const withFixedPayloadConfig = (config) => ({
145
+ ...config,
146
+ mode: "fixed",
147
+ payloadInitialBytes: config.payloadMaxByteLength,
148
+ });
149
+ const getProcessWorkerSharedMemoryPrimitives = () => {
150
+ assertPosixSharedMemoryPlatform("Process worker runtime");
151
+ switch (RUNTIME) {
152
+ case "bun":
153
+ return createBunConnectionPrimitives();
154
+ case "deno":
155
+ return createDenoConnectionPrimitives();
156
+ case "node":
157
+ return createNodeConnectionPrimitives();
158
+ default:
159
+ throw new Error("process worker runtime needs Node, Deno, or Bun shared memory primitives");
160
+ }
161
+ };
162
+ const createProcessWorkerMemoryLayout = ({ signalBytes, abortBytes, payloadBytes, thread, }) => {
163
+ const carpet = createByteCarpet();
164
+ const signalsSlice = carpet.take("signals", signalBytes);
165
+ const requestLockSlice = carpet.take("requestLockSector", LOCK_SECTOR_BYTE_LENGTH);
166
+ const returnLockSlice = carpet.take("returnLockSector", LOCK_SECTOR_BYTE_LENGTH);
167
+ const requestHeadersSlice = carpet.take("requestHeaders", getHeaderBlockByteLength({
168
+ slotCount: LockBound.slots,
169
+ slotStrideU32: HEADER_SLOT_STRIDE_U32,
170
+ alignTo: 64,
171
+ }));
172
+ const returnHeadersSlice = carpet.take("returnHeaders", getHeaderBlockByteLength({
173
+ slotCount: LockBound.slots,
174
+ slotStrideU32: HEADER_SLOT_STRIDE_U32,
175
+ alignTo: 64,
176
+ }));
177
+ const abortSignalsSlice = carpet.take("abortSignals", abortBytes);
178
+ const requestPayloadSlice = carpet.take("requestPayload", payloadBytes);
179
+ const returnPayloadSlice = carpet.take("returnPayload", payloadBytes);
180
+ const primitives = getProcessWorkerSharedMemoryPrimitives();
181
+ const mapping = primitives.createSharedMemory({
182
+ size: carpet.byteLength(),
183
+ name: `knitting_process_worker_${thread}`,
184
+ });
185
+ const buffer = mapping.buffer;
186
+ const bind = (slice) => makeSharedBufferRegion(buffer, slice.byteOffset, slice.byteLength);
187
+ const controlLayout = {
188
+ controlSAB: buffer,
189
+ signals: bind(signalsSlice),
190
+ abortSignals: bind(abortSignalsSlice),
191
+ lock: {
192
+ headers: bind(requestHeadersSlice),
193
+ headerSlotStrideU32: HEADER_SLOT_STRIDE_U32,
194
+ lockSector: bind(requestLockSlice),
195
+ payloadSector: bind(requestLockSlice),
196
+ },
197
+ returnLock: {
198
+ headers: bind(returnHeadersSlice),
199
+ headerSlotStrideU32: HEADER_SLOT_STRIDE_U32,
200
+ lockSector: bind(returnLockSlice),
201
+ payloadSector: bind(returnLockSlice),
202
+ },
203
+ slices: carpet.slices,
204
+ };
205
+ return {
206
+ mapping,
207
+ descriptor: FileDescriptor.fromMapping(mapping),
208
+ controlLayout,
209
+ lockPayload: bind(requestPayloadSlice),
210
+ returnPayload: bind(returnPayloadSlice),
211
+ };
212
+ };
213
+ const toChildProcessSharedBufferMetadata = (source, descriptor) => {
214
+ const region = toSharedBufferRegion(source);
215
+ return ProcessSharedBuffer.fromDescriptor(new FileDescriptor({
216
+ ...descriptor.toMetadata(),
217
+ fd: PROCESS_WORKER_CHILD_FD,
218
+ }), {
219
+ byteOffset: region.byteOffset,
220
+ byteLength: region.byteLength,
221
+ }).toMetadata();
222
+ };
223
+ const toProcessWorkerWireLockBuffers = (lock, descriptor) => ({
224
+ ...lock,
225
+ headers: toChildProcessSharedBufferMetadata(lock.headers, descriptor),
226
+ lockSector: toChildProcessSharedBufferMetadata(lock.lockSector, descriptor),
227
+ payload: toChildProcessSharedBufferMetadata(lock.payload, descriptor),
228
+ payloadSector: toChildProcessSharedBufferMetadata(lock.payloadSector, descriptor),
229
+ });
230
+ const toProcessWorkerBootPayload = (workerData, memory) => ({
231
+ version: RUNTIME_PROCESS_WORKER_BOOT_VERSION,
232
+ workerData: {
233
+ ...workerData,
234
+ sab: toChildProcessSharedBufferMetadata(workerData.sab, memory.descriptor),
235
+ abortSignalSAB: workerData.abortSignalSAB === undefined
236
+ ? undefined
237
+ : toChildProcessSharedBufferMetadata(workerData.abortSignalSAB, memory.descriptor),
238
+ lock: toProcessWorkerWireLockBuffers(workerData.lock, memory.descriptor),
239
+ returnLock: toProcessWorkerWireLockBuffers(workerData.returnLock, memory.descriptor),
240
+ },
241
+ });
242
+ const toProcessWorkerPath = (specifier) => {
243
+ const value = specifier instanceof URL ? specifier.href : specifier;
244
+ if (value.startsWith("file:"))
245
+ return fileURLToPathCompat(value);
246
+ return value;
247
+ };
248
+ const readProcessWorkerRuntime = (options) => {
249
+ const runtime = options?.processRuntime ?? "bun";
250
+ if (runtime === "bun" || runtime === "deno" || runtime === "node") {
251
+ return runtime;
252
+ }
253
+ throw new TypeError(`Unsupported process worker runtime: ${String(runtime)}`);
254
+ };
255
+ const readProcessWorkerCommandPrefix = (options) => {
256
+ const prefix = options?.processCommandPrefix;
257
+ if (prefix === undefined)
258
+ return undefined;
259
+ if (!Array.isArray(prefix)) {
260
+ throw new TypeError("processCommandPrefix must be an argv array");
261
+ }
262
+ if (prefix.length === 0)
263
+ return undefined;
264
+ const out = [];
265
+ for (const [index, value] of prefix.entries()) {
266
+ if (typeof value !== "string" || value.length === 0) {
267
+ throw new TypeError(`processCommandPrefix[${index}] must be a non-empty string`);
268
+ }
269
+ out.push(value);
270
+ }
271
+ return out;
272
+ };
273
+ const currentProcessEnv = () => ({
274
+ ...getNodeProcess()?.env,
275
+ });
276
+ const processWorkerEnv = (extra) => ({
277
+ ...currentProcessEnv(),
278
+ [RUNTIME_PROCESS_WORKER_ENV]: "1",
279
+ ...extra,
280
+ });
281
+ const processWorkerBootEnv = (bootPayload) => processWorkerEnv({
282
+ [RUNTIME_PROCESS_WORKER_BOOT_ENV]: JSON.stringify(bootPayload),
283
+ });
284
+ const stringProcessEnv = (input) => {
285
+ const out = {};
286
+ for (const [key, value] of Object.entries(input)) {
287
+ if (value !== undefined)
288
+ out[key] = value;
289
+ }
290
+ return out;
291
+ };
292
+ const processWorkerBunBinary = (bun) => getNodeProcess()?.env?.BUN_BINARY ??
293
+ bun?.argv?.[0] ??
294
+ DEFAULT_BUN_BINARY;
295
+ const processWorkerDenoBinary = (deno) => getNodeProcess()?.env?.DENO_BINARY ??
296
+ deno?.execPath?.() ??
297
+ DEFAULT_DENO_BINARY;
298
+ const processWorkerDenoFlags = (permission) => {
299
+ if (permission?.enabled !== true || permission.unsafe === true) {
300
+ return ["-A"];
301
+ }
302
+ return [
303
+ ...DENO_PROCESS_WORKER_INTERNAL_FLAGS,
304
+ ...permission.deno.flags,
305
+ ];
306
+ };
307
+ const processWorkerNodeBinary = () => {
308
+ const nodeProcess = getNodeProcess();
309
+ return nodeProcess?.env?.NODE_BINARY ??
310
+ (RUNTIME === "node" ? nodeProcess?.execPath : undefined) ??
311
+ DEFAULT_NODE_BINARY;
312
+ };
313
+ const processWorkerNodeExecArgv = () => {
314
+ const out = [];
315
+ const seen = new Set();
316
+ const add = (flag) => {
317
+ if (seen.has(flag))
318
+ return;
319
+ seen.add(flag);
320
+ out.push(flag);
321
+ };
322
+ for (const flag of toWorkerCompatExecArgv(getNodeProcess()?.execArgv) ?? []) {
323
+ add(flag);
324
+ }
325
+ for (const flag of NODE_PROCESS_WORKER_EXEC_ARGV)
326
+ add(flag);
327
+ return out;
328
+ };
329
+ const processWorkerCommand = ({ processRuntime, workerUrl, bun, deno, commandPrefix, permission, }) => {
330
+ const workerPath = toProcessWorkerPath(workerUrl);
331
+ let command;
332
+ if (processRuntime === "deno") {
333
+ command = [
334
+ processWorkerDenoBinary(deno),
335
+ "run",
336
+ ...processWorkerDenoFlags(permission),
337
+ workerPath,
338
+ ];
339
+ }
340
+ else if (processRuntime === "node") {
341
+ command = [
342
+ processWorkerNodeBinary(),
343
+ ...processWorkerNodeExecArgv(),
344
+ workerPath,
345
+ ];
346
+ }
347
+ else {
348
+ command = [processWorkerBunBinary(bun), workerPath];
349
+ }
350
+ return commandPrefix === undefined
351
+ ? command
352
+ : [...commandPrefix, ...command];
353
+ };
354
+ const createProcessWorkerNativeSignalNotifier = ({ processRuntime, signal, }) => {
355
+ if (RUNTIME !== "node" || processRuntime !== "node")
356
+ return undefined;
357
+ try {
358
+ const futex = loadNodeFutexAddon();
359
+ return () => {
360
+ futex.wakeU32(signal.buffer, signal.byteOffset, 1);
361
+ };
362
+ }
363
+ catch {
364
+ return undefined;
365
+ }
366
+ };
367
+ const createProcessWorkerEventHub = () => {
368
+ const messageHandlers = [];
369
+ const errorHandlers = [];
370
+ const exitHandlers = [];
371
+ return {
372
+ emitMessage: (message) => {
373
+ for (const handler of messageHandlers)
374
+ handler(message);
375
+ },
376
+ emitError: (error) => {
377
+ for (const handler of errorHandlers)
378
+ handler(error);
379
+ },
380
+ emitExit: (code) => {
381
+ for (const handler of exitHandlers)
382
+ handler(code);
383
+ },
384
+ on: (event, listener) => {
385
+ if (event === "message")
386
+ messageHandlers.push(listener);
387
+ if (event === "error")
388
+ errorHandlers.push(listener);
389
+ if (event === "exit")
390
+ exitHandlers.push(listener);
391
+ },
392
+ };
393
+ };
394
+ const spawnBunHostedProcessWorker = ({ workerUrl, bootPayload, memory, processRuntime, commandPrefix, permission, }) => {
395
+ const bun = globalThis.Bun;
396
+ if (typeof bun?.spawn !== "function") {
397
+ throw new Error("Bun.spawn is not available for process workers");
398
+ }
399
+ const events = createProcessWorkerEventHub();
400
+ const nodeProcess = getNodeProcess();
401
+ const useIpcBoot = processRuntime === "bun" && commandPrefix === undefined;
402
+ const spawnOptions = {
403
+ cmd: processWorkerCommand({
404
+ processRuntime,
405
+ workerUrl,
406
+ bun,
407
+ commandPrefix,
408
+ permission,
409
+ }),
410
+ cwd: nodeProcess?.cwd?.(),
411
+ env: useIpcBoot
412
+ ? processWorkerEnv()
413
+ : processWorkerBootEnv(bootPayload),
414
+ stdin: memory.mapping.fd,
415
+ stdout: "inherit",
416
+ stderr: "inherit",
417
+ onExit: (_subprocess, exitCode, _signalCode, error) => {
418
+ if (error !== undefined)
419
+ events.emitError(error);
420
+ events.emitExit(exitCode ?? -1);
421
+ },
422
+ };
423
+ if (useIpcBoot) {
424
+ spawnOptions.serialization = "advanced";
425
+ spawnOptions.ipc = (message) => {
426
+ events.emitMessage(message);
427
+ };
428
+ }
429
+ const child = bun.spawn(spawnOptions);
430
+ if (useIpcBoot) {
431
+ queueMicrotask(() => child.send?.(bootPayload));
432
+ }
433
+ child.exited.catch((error) => {
434
+ events.emitError(error);
435
+ });
436
+ return {
437
+ terminate: () => {
438
+ child.kill();
439
+ return child.exited.catch(() => undefined);
440
+ },
441
+ on: events.on,
442
+ };
443
+ };
444
+ const spawnNodeHostedProcessWorker = ({ workerUrl, bootPayload, memory, processRuntime, commandPrefix, permission, }) => {
445
+ const childProcess = getNodeBuiltinModule("node:child_process");
446
+ if (typeof childProcess?.spawn !== "function") {
447
+ throw new Error("node:child_process.spawn is not available");
448
+ }
449
+ const events = createProcessWorkerEventHub();
450
+ const useIpcBoot = processRuntime === "bun" && commandPrefix === undefined;
451
+ const [command, ...args] = processWorkerCommand({
452
+ processRuntime,
453
+ workerUrl,
454
+ commandPrefix,
455
+ permission,
456
+ });
457
+ const child = childProcess.spawn(command, args, {
458
+ cwd: getNodeProcess()?.cwd?.(),
459
+ env: useIpcBoot
460
+ ? processWorkerEnv()
461
+ : processWorkerBootEnv(bootPayload),
462
+ stdio: useIpcBoot
463
+ ? [memory.mapping.fd, "inherit", "inherit", "ipc"]
464
+ : [memory.mapping.fd, "inherit", "inherit"],
465
+ });
466
+ if (useIpcBoot) {
467
+ child.on("message", events.emitMessage);
468
+ queueMicrotask(() => child.send?.(bootPayload));
469
+ }
470
+ child.on("error", events.emitError);
471
+ child.on("exit", (code) => events.emitExit(code ?? -1));
472
+ return {
473
+ terminate: () => child.kill(),
474
+ unref: () => child.unref?.(),
475
+ on: events.on,
476
+ };
477
+ };
478
+ const getDenoRuntime = () => globalThis.Deno;
479
+ const denoFileRid = (file) => {
480
+ for (const symbol of Object.getOwnPropertySymbols(file)) {
481
+ if (String(symbol) === "Symbol(Deno.internal.rid)") {
482
+ const rid = file[symbol];
483
+ if (typeof rid === "number")
484
+ return rid;
485
+ }
486
+ }
487
+ throw new Error("Deno FsFile resource id is not available");
488
+ };
489
+ const openDenoInheritedFd = (fd) => {
490
+ const deno = getDenoRuntime();
491
+ if (typeof deno?.openSync !== "function") {
492
+ throw new Error("Deno.openSync is not available for process workers");
493
+ }
494
+ const fdPath = detectPosixPlatform() === "linux"
495
+ ? `/proc/self/fd/${fd}`
496
+ : `/dev/fd/${fd}`;
497
+ return deno.openSync(fdPath, { read: true, write: true });
498
+ };
499
+ const spawnDenoHostedProcessWorker = ({ workerUrl, bootPayload, memory, processRuntime, commandPrefix, permission, }) => {
500
+ const deno = getDenoRuntime();
501
+ if (typeof deno?.Command !== "function") {
502
+ throw new Error("Deno.Command is not available for process workers");
503
+ }
504
+ const inheritedFd = openDenoInheritedFd(memory.mapping.fd);
505
+ const events = createProcessWorkerEventHub();
506
+ const [command, ...args] = processWorkerCommand({
507
+ processRuntime,
508
+ workerUrl,
509
+ deno,
510
+ commandPrefix,
511
+ permission,
512
+ });
513
+ const child = new deno.Command(command, {
514
+ args,
515
+ cwd: deno.cwd?.(),
516
+ env: stringProcessEnv(processWorkerBootEnv(bootPayload)),
517
+ stdin: denoFileRid(inheritedFd),
518
+ stdout: "inherit",
519
+ stderr: "inherit",
520
+ }).spawn();
521
+ const closeInheritedFd = () => {
522
+ try {
523
+ inheritedFd.close?.();
524
+ }
525
+ catch {
526
+ }
527
+ };
528
+ child.status.then((status) => {
529
+ closeInheritedFd();
530
+ events.emitExit(status.code);
531
+ }, (error) => {
532
+ closeInheritedFd();
533
+ events.emitError(error);
534
+ events.emitExit(-1);
535
+ });
536
+ return {
537
+ terminate: () => {
538
+ try {
539
+ child.kill("SIGTERM");
540
+ }
541
+ catch {
542
+ }
543
+ return child.status.finally(closeInheritedFd);
544
+ },
545
+ on: events.on,
546
+ };
547
+ };
548
+ const spawnProcessWorker = (options) => {
549
+ switch (RUNTIME) {
550
+ case "bun":
551
+ return spawnBunHostedProcessWorker(options);
552
+ case "node":
553
+ return spawnNodeHostedProcessWorker(options);
554
+ case "deno":
555
+ return spawnDenoHostedProcessWorker(options);
556
+ default:
557
+ throw new Error("process worker runtime is only available in Node, Deno, or Bun");
558
+ }
559
+ };
560
+ const terminateWorkerQuietly = (worker) => {
561
+ try {
562
+ // Runaway worker termination can be slow or stuck on some runtimes; once the
563
+ // pool is closing it must not keep the host process alive.
564
+ worker.unref?.();
565
+ void Promise.resolve(worker.terminate()).catch(() => { });
566
+ }
567
+ catch {
568
+ }
569
+ };
570
+ export const spawnWorkerContext = ({ list, ids, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }) => {
571
+ const tsFileUrl = new URL(import.meta.url);
572
+ const poliWorker = RUNTIME_WORKER;
573
+ const resolvedWorkerOptions = withDefaultWorkerTimers(workerOptions);
574
+ const useProcessWorkerRuntime = resolvedWorkerOptions.runtime === "process";
575
+ if (useProcessWorkerRuntime) {
576
+ assertPosixSharedMemoryPlatform("Process worker runtime");
577
+ }
578
+ const processWorkerRuntime = useProcessWorkerRuntime
579
+ ? readProcessWorkerRuntime(resolvedWorkerOptions)
580
+ : undefined;
581
+ const processWorkerCommandPrefix = useProcessWorkerRuntime
582
+ ? readProcessWorkerCommandPrefix(resolvedWorkerOptions)
583
+ : undefined;
584
+ if (debug?.logHref === true) {
585
+ console.log(tsFileUrl);
586
+ }
587
+ if (!useProcessWorkerRuntime && typeof poliWorker !== "function") {
588
+ throw new Error("Worker is not available in this runtime");
589
+ }
590
+ const WorkerCtor = poliWorker;
591
+ // Lock buffers must be shared between host and worker.
592
+ const sanitizeBytes = (value) => {
593
+ if (!Number.isFinite(value))
594
+ return undefined;
595
+ const bytes = Math.floor(value);
596
+ return bytes > 0 ? bytes : undefined;
597
+ };
598
+ const basePayloadConfig = resolvePayloadBufferOptions({
599
+ options: {
600
+ ...payload,
601
+ mode: payload?.mode ?? bufferMode,
602
+ maxPayloadBytes: payload?.maxPayloadBytes ?? maxPayloadBytes,
603
+ payloadInitialBytes: payload?.payloadInitialBytes ??
604
+ sanitizeBytes(payloadInitialBytes),
605
+ payloadMaxByteLength: payload?.payloadMaxByteLength ??
606
+ sanitizeBytes(payloadMaxBytes),
607
+ },
608
+ });
609
+ const resolvedPayloadConfig = useProcessWorkerRuntime
610
+ ? withFixedPayloadConfig(basePayloadConfig)
611
+ : basePayloadConfig;
612
+ const defaultAbortSignalCapacity = 258;
613
+ const requestedAbortSignalCapacity = sanitizeBytes(abortSignalCapacity);
614
+ const resolvedAbortSignalCapacity = requestedAbortSignalCapacity ??
615
+ defaultAbortSignalCapacity;
616
+ const abortSignalWords = Math.max(1, Math.ceil(resolvedAbortSignalCapacity / 32));
617
+ const requestedSignalBytes = sanitizeBytes(sab?.size);
618
+ const externalSignalSab = sab?.sharedSab;
619
+ if (useProcessWorkerRuntime && externalSignalSab != null) {
620
+ throw new Error("process worker runtime cannot use an external signal buffer");
621
+ }
622
+ const signalBytes = Math.max(TRANSPORT_SIGNAL_BYTES, requestedSignalBytes ?? TRANSPORT_SIGNAL_BYTES);
623
+ const abortBytes = abortSignalWords * Uint32Array.BYTES_PER_ELEMENT;
624
+ const processWorkerMemory = useProcessWorkerRuntime
625
+ ? createProcessWorkerMemoryLayout({
626
+ signalBytes,
627
+ abortBytes,
628
+ payloadBytes: resolvedPayloadConfig.payloadMaxByteLength,
629
+ thread,
630
+ })
631
+ : undefined;
632
+ const processSharedMemory = processWorkerMemory === undefined
633
+ ? createProcessSharedMemoryAllocator(debug)
634
+ : undefined;
635
+ const createControlBuffer = processSharedMemory?.createBuffer ??
636
+ createWasmSharedArrayBuffer;
637
+ const createPayloadBuffer = processSharedMemory?.createBuffer;
638
+ const makePayloadBuffer = () => createPayloadBuffer
639
+ // ProcessSharedBuffer is fixed-size today, so reserve the configured
640
+ // payload ceiling instead of relying on SAB growth.
641
+ ? createPayloadBuffer(resolvedPayloadConfig.payloadMaxByteLength)
642
+ : resolvedPayloadConfig.mode === "growable"
643
+ ? createSharedArrayBuffer(resolvedPayloadConfig.payloadInitialBytes, resolvedPayloadConfig.payloadMaxByteLength)
644
+ : createSharedArrayBuffer(resolvedPayloadConfig.payloadInitialBytes);
645
+ const makeLockControlLayout = () => {
646
+ // Keep the hottest control words in one compact front strip:
647
+ // transport signals -> request lock -> return lock.
648
+ // Request/return headers stay in separate contiguous slabs to preserve
649
+ // sequential batching locality.
650
+ // Abort bitmap stays at the tail.
651
+ return createLockControlCarpet({
652
+ signalBytes,
653
+ abortBytes,
654
+ lockSectorBytes: LOCK_SECTOR_BYTE_LENGTH,
655
+ headerSlotStrideU32: HEADER_SLOT_STRIDE_U32,
656
+ slotCount: LockBound.slots,
657
+ headerLayout: "split",
658
+ createBuffer: createControlBuffer,
659
+ });
660
+ };
661
+ const controlLayout = processWorkerMemory?.controlLayout ??
662
+ makeLockControlLayout();
663
+ const lockPayload = processWorkerMemory?.lockPayload ?? makePayloadBuffer();
664
+ const lockBuffers = {
665
+ ...controlLayout.lock,
666
+ payload: lockPayload,
667
+ textCompat: probeLockBufferTextCompat({
668
+ headers: controlLayout.lock.headers,
669
+ payload: lockPayload,
670
+ }),
671
+ };
672
+ const returnPayload = processWorkerMemory?.returnPayload ??
673
+ makePayloadBuffer();
674
+ const returnLockBuffers = {
675
+ ...controlLayout.returnLock,
676
+ payload: returnPayload,
677
+ textCompat: probeLockBufferTextCompat({
678
+ headers: controlLayout.returnLock.headers,
679
+ payload: returnPayload,
680
+ }),
681
+ };
682
+ const lock = lock2({
683
+ headers: lockBuffers.headers,
684
+ headerSlotStrideU32: lockBuffers.headerSlotStrideU32,
685
+ LockBoundSector: lockBuffers.lockSector,
686
+ payload: lockBuffers.payload,
687
+ payloadSector: lockBuffers.payloadSector,
688
+ payloadConfig: resolvedPayloadConfig,
689
+ textCompat: lockBuffers.textCompat,
690
+ });
691
+ const returnLock = lock2({
692
+ headers: returnLockBuffers.headers,
693
+ headerSlotStrideU32: returnLockBuffers.headerSlotStrideU32,
694
+ LockBoundSector: returnLockBuffers.lockSector,
695
+ payload: returnLockBuffers.payload,
696
+ payloadSector: returnLockBuffers.payloadSector,
697
+ payloadConfig: resolvedPayloadConfig,
698
+ textCompat: returnLockBuffers.textCompat,
699
+ });
700
+ const abortSignalSAB = usesAbortSignal === true
701
+ ? controlLayout.abortSignals
702
+ : undefined;
703
+ const abortSignals = abortSignalSAB
704
+ ? signalAbortFactory({
705
+ sab: abortSignalSAB,
706
+ maxSignals: resolvedAbortSignalCapacity,
707
+ })
708
+ : undefined;
709
+ const signals = createSharedMemoryTransport({
710
+ sabObject: externalSignalSab == null
711
+ ? {
712
+ size: requestedSignalBytes,
713
+ sharedSab: controlLayout.signals,
714
+ }
715
+ : sab,
716
+ isMain: true,
717
+ thread,
718
+ debug,
719
+ });
720
+ const signalBox = signals;
721
+ const nativeNotifySignal = createProcessWorkerNativeSignalNotifier({
722
+ processRuntime: processWorkerRuntime,
723
+ signal: signalBox.opView,
724
+ });
725
+ const queue = createHostTxQueue({
726
+ lock,
727
+ returnLock,
728
+ abortSignals,
729
+ });
730
+ const { enqueue, rejectAll, txIdle, } = queue;
731
+ const channelHandler = new ChannelHandler();
732
+ const { check } = hostDispatcherLoop({
733
+ signalBox,
734
+ queue,
735
+ channelHandler,
736
+ dispatcherOptions: host,
737
+ notifySignal: nativeNotifySignal,
738
+ //thread,
739
+ //debugSignal: debug?.logMain ?? false,
740
+ //perf,
741
+ });
742
+ channelHandler.open(check);
743
+ let worker;
744
+ const workerUrl = source ?? tsFileUrl;
745
+ const workerDataPayload = {
746
+ sab: signals.sab,
747
+ abortSignalSAB,
748
+ abortSignalMax: usesAbortSignal === true
749
+ ? resolvedAbortSignalCapacity
750
+ : undefined,
751
+ list,
752
+ ids,
753
+ at,
754
+ thread,
755
+ debug,
756
+ workerOptions: resolvedWorkerOptions,
757
+ totalNumberOfThread,
758
+ startAt: signalBox.startAt,
759
+ lock: lockBuffers,
760
+ returnLock: returnLockBuffers,
761
+ payloadConfig: resolvedPayloadConfig,
762
+ permission,
763
+ };
764
+ const baseWorkerOptions = {
765
+ //@ts-ignore Reason
766
+ type: "module",
767
+ //@ts-ignore
768
+ workerData: workerDataPayload,
769
+ };
770
+ const withExecArgv = workerExecArgv && workerExecArgv.length > 0
771
+ ? { ...baseWorkerOptions, execArgv: workerExecArgv }
772
+ : baseWorkerOptions;
773
+ if (processWorkerMemory !== undefined) {
774
+ worker = spawnProcessWorker({
775
+ workerUrl,
776
+ bootPayload: toProcessWorkerBootPayload(workerDataPayload, processWorkerMemory),
777
+ memory: processWorkerMemory,
778
+ processRuntime: processWorkerRuntime,
779
+ commandPrefix: processWorkerCommandPrefix,
780
+ permission,
781
+ });
782
+ }
783
+ else if (HAS_NODE_WORKER_THREADS) {
784
+ try {
785
+ worker = new WorkerCtor(workerUrl, withExecArgv);
786
+ }
787
+ catch (error) {
788
+ if (error?.code === "ERR_WORKER_INVALID_EXEC_ARGV") {
789
+ const fallbackExecArgv = toWorkerSafeExecArgv(withExecArgv.execArgv);
790
+ if (fallbackExecArgv && fallbackExecArgv.length > 0) {
791
+ try {
792
+ worker = new WorkerCtor(workerUrl, { ...baseWorkerOptions, execArgv: fallbackExecArgv });
793
+ }
794
+ catch (fallbackError) {
795
+ if (fallbackError?.code ===
796
+ "ERR_WORKER_INVALID_EXEC_ARGV") {
797
+ const compatExecArgv = toWorkerCompatExecArgv(fallbackExecArgv);
798
+ if (compatExecArgv && compatExecArgv.length > 0) {
799
+ try {
800
+ worker = new WorkerCtor(workerUrl, { ...baseWorkerOptions, execArgv: compatExecArgv });
801
+ }
802
+ catch {
803
+ worker = new WorkerCtor(workerUrl, baseWorkerOptions);
804
+ }
805
+ }
806
+ else {
807
+ worker = new WorkerCtor(workerUrl, baseWorkerOptions);
808
+ }
809
+ }
810
+ else {
811
+ throw fallbackError;
812
+ }
813
+ }
814
+ }
815
+ else {
816
+ worker = new WorkerCtor(workerUrl, baseWorkerOptions);
817
+ }
818
+ }
819
+ else {
820
+ throw error;
821
+ }
822
+ }
823
+ }
824
+ else {
825
+ worker = new WorkerCtor(workerUrl, {
826
+ type: "module",
827
+ });
828
+ worker.postMessage?.(workerDataPayload);
829
+ }
830
+ let closedReason;
831
+ const markWorkerClosed = (reason) => {
832
+ if (closedReason)
833
+ return;
834
+ closedReason = reason;
835
+ rejectAll(reason);
836
+ channelHandler.close();
837
+ };
838
+ const onWorkerMessage = (message) => {
839
+ if (!isWorkerFatalMessage(message))
840
+ return;
841
+ markWorkerClosed(`Worker startup failed: ${message[WORKER_FATAL_MESSAGE_KEY]}`);
842
+ terminateWorkerQuietly(worker);
843
+ };
844
+ const onWorkerError = (error) => {
845
+ const message = String(error?.message ?? error);
846
+ markWorkerClosed(`Worker crashed: ${message}`);
847
+ };
848
+ const nodeWorker = worker;
849
+ if (typeof nodeWorker.on === "function") {
850
+ nodeWorker.on("message", onWorkerMessage);
851
+ nodeWorker.on("error", onWorkerError);
852
+ nodeWorker.on("exit", (code) => {
853
+ if (typeof code === "number" && code === 0)
854
+ return;
855
+ const normalized = typeof code === "number" ? code : -1;
856
+ markWorkerClosed(`Worker exited with code ${normalized}`);
857
+ });
858
+ }
859
+ else {
860
+ const eventWorker = worker;
861
+ if (typeof eventWorker.addEventListener === "function") {
862
+ eventWorker.addEventListener("message", (event) => {
863
+ onWorkerMessage(event?.data);
864
+ });
865
+ eventWorker.addEventListener("error", (event) => {
866
+ onWorkerError(event?.error ?? event?.message ?? event);
867
+ });
868
+ }
869
+ else {
870
+ eventWorker.onmessage = (event) => {
871
+ onWorkerMessage(event?.data);
872
+ };
873
+ eventWorker.onerror = (event) => {
874
+ onWorkerError(event);
875
+ };
876
+ }
877
+ }
878
+ const thisSignal = signalBox.opView;
879
+ const a_add = Atomics.add;
880
+ const a_load = Atomics.load;
881
+ const a_notify = Atomics.notify;
882
+ const canNotifySignal = thisSignal.buffer instanceof SharedArrayBuffer;
883
+ const notifySignal = nativeNotifySignal ??
884
+ (canNotifySignal ? (() => a_notify(thisSignal, 0, 1)) : undefined);
885
+ //const scheduleFastCheck = queueMicrotask;
886
+ const send = () => {
887
+ if (check.isRunning === true)
888
+ return;
889
+ check.isRunning = true;
890
+ Promise.resolve().then(check);
891
+ // Macro lane: dispatcher check is driven by the channel callback.
892
+ // channelHandler.notify();
893
+ // Use opView as a wake counter in lock2 mode to avoid lost wakeups.
894
+ if (a_load(signalBox.rxStatus, 0) === 0) {
895
+ a_add(thisSignal, 0, 1);
896
+ notifySignal?.();
897
+ }
898
+ };
899
+ lock.setPromiseHandler((task, isRejected, value) => {
900
+ queue.settlePromisePayload(task, isRejected, value);
901
+ send();
902
+ });
903
+ const call = ({ fnNumber, timeout, abortSignal }) => {
904
+ const enqueues = enqueue(fnNumber, timeout, abortSignal);
905
+ return (args) => {
906
+ const pending = enqueues(args);
907
+ send();
908
+ return pending;
909
+ };
910
+ };
911
+ const context = {
912
+ txIdle,
913
+ call,
914
+ kills: async () => {
915
+ markWorkerClosed("Thread closed");
916
+ terminateWorkerQuietly(worker);
917
+ },
918
+ lock,
919
+ processSharedMemoryBackings: processSharedMemory?.backings,
920
+ };
921
+ return context;
922
+ };