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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knitting",
3
- "version": "0.1.50",
3
+ "version": "0.1.52",
4
4
  "description": "Shared-memory IPC runtime for Node.js, Deno, and Bun.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -23,6 +23,14 @@
23
23
  "./process-shared-buffer": {
24
24
  "types": "./process-shared-buffer.d.ts",
25
25
  "default": "./process-shared-buffer.js"
26
+ },
27
+ "./unsafe": {
28
+ "types": "./unsafe.d.ts",
29
+ "default": "./unsafe.js"
30
+ },
31
+ "./utils": {
32
+ "types": "./utils.d.ts",
33
+ "default": "./utils.js"
26
34
  }
27
35
  },
28
36
  "files": [
@@ -38,7 +46,11 @@
38
46
  "knitting.d.ts",
39
47
  "knitting.js",
40
48
  "process-shared-buffer.d.ts",
41
- "process-shared-buffer.js"
49
+ "process-shared-buffer.js",
50
+ "unsafe.d.ts",
51
+ "unsafe.js",
52
+ "utils.d.ts",
53
+ "utils.js"
42
54
  ],
43
55
  "scripts": {
44
56
  "build": "bun run build.ts",
@@ -66,13 +78,13 @@
66
78
  "bun": ">=1.0.0"
67
79
  },
68
80
  "peerDependencies": {
69
- "typescript": "^5.0.0"
81
+ "typescript": "^5.2.0"
70
82
  },
71
83
  "devDependencies": {
72
84
  "@types/bun": "latest",
73
85
  "@types/deno": "latest",
74
86
  "@types/node": "latest",
75
87
  "mitata": "^1.0.34",
76
- "typescript": "^5.0.0"
88
+ "typescript": "^5.2.0"
77
89
  }
78
90
  }
@@ -232,6 +232,11 @@ const addons = [
232
232
  source: "src/knitting_shm.cc",
233
233
  output: "build/Release/knitting_shm.node",
234
234
  },
235
+ {
236
+ name: "knitting_buffer_pointer",
237
+ source: "src/knitting_buffer_pointer.cc",
238
+ output: "build/Release/knitting_buffer_pointer.node",
239
+ },
235
240
  ];
236
241
  const ffiLibraries = isWindows
237
242
  ? [
package/src/api.d.ts CHANGED
@@ -1,18 +1,17 @@
1
1
  import { endpointSymbol } from "./common/task-symbol.js";
2
- import type { Args, AbortSignalConfig, AbortSignalOption, AbortSignalToolkit, CreatePool, FixPoint, MaybePromise, Pool, TaskInput, ReturnFixed, ImportTaskOptions, TaskTimeout, tasks } from "./types.js";
2
+ import type { AbortSignalConfig, AbortSignalOption, AbortSignalToolkit, Args, ComposedWithKey, CreatePool, FixPoint, ImportTaskOptions, MaybePromise, Pool, ReturnFixed, TaskInput, tasks, TaskTimeout } from "./types.js";
3
3
  type ToListAndIds = {
4
4
  list: string[];
5
5
  ids: number[];
6
+ names: string[];
6
7
  at: number[];
7
8
  };
8
- type ToListAndIdsFn = (args: tasks) => ToListAndIds;
9
+ type ToListAndIdsFn = (args: ComposedWithKey[]) => ToListAndIds;
9
10
  type CreatePoolFactory = (options: CreatePool) => <T extends tasks>(tasks: T) => Pool<T>;
10
11
  type InferredTaskFunction = (...args: any[]) => MaybePromise<Args>;
11
12
  type InferredTaskInput<F extends InferredTaskFunction, AS extends AbortSignalOption> = Parameters<F> extends [] ? void : AS extends undefined ? Parameters<F> extends [infer A] ? A extends TaskInput ? A : never : never : Parameters<F> extends [infer A] ? A extends TaskInput ? A : never : Parameters<F> extends [infer A, AbortSignalToolkit<AS>] ? A extends TaskInput ? A : never : never;
12
13
  type InferredTaskOutput<F extends InferredTaskFunction> = Awaited<ReturnType<F>> extends infer R ? R extends Blob ? never : R extends Args ? R : never : never;
13
- type InferredTaskShape<F extends InferredTaskFunction, AS extends AbortSignalOption> = [
14
- InferredTaskInput<F, AS>
15
- ] extends [never] ? never : [InferredTaskOutput<F>] extends [never] ? never : {
14
+ type InferredTaskShape<F extends InferredTaskFunction, AS extends AbortSignalOption> = [InferredTaskInput<F, AS>] extends [never] ? never : [InferredTaskOutput<F>] extends [never] ? never : {
16
15
  readonly f: F;
17
16
  readonly timeout?: TaskTimeout;
18
17
  } & (AS extends undefined ? {
@@ -23,12 +22,7 @@ type InferredTaskShape<F extends InferredTaskFunction, AS extends AbortSignalOpt
23
22
  export declare const isMain: boolean;
24
23
  export { endpointSymbol as endpointSymbol };
25
24
  /**
26
- * With this information we can recreate the logical order of
27
- * relevant exported functions from a file, also it helps to
28
- * track a task before naming, ` export ` elements have to be declared
29
- * at top level and without branching, we take advantage of this to
30
- * correctly map them.
31
- *
25
+ * Reconstructs stable task order from top-level exports before names are bound.
32
26
  */
33
27
  export declare const toListAndIds: ToListAndIdsFn;
34
28
  export declare const createPool: CreatePoolFactory;
package/src/api.js CHANGED
@@ -11,7 +11,7 @@ import { genTaskID } from "./common/task-source.js";
11
11
  import { toModuleUrl } from "./common/module-url.js";
12
12
  import { endpointSymbol } from "./common/task-symbol.js";
13
13
  import { spawnWorkerContext } from "./runtime/pool.js";
14
- import { RUNTIME_IS_MAIN_THREAD, RUNTIME_WORKER_DATA, } from "./common/worker-runtime.js";
14
+ import { RUNTIME_IS_MAIN_THREAD, RUNTIME_POOL_DEPTH, RUNTIME_WORKER_DATA, } from "./common/worker-runtime.js";
15
15
  import { resolvePermissionProtocol, toRuntimePermissionFlags, } from "./permission/index.js";
16
16
  import { getNodeProcess } from "./common/node-compat.js";
17
17
  import { managerMethod } from "./runtime/balancer.js";
@@ -22,27 +22,25 @@ const DEFAULT_IMPORT_EXPORT_NAME = "default";
22
22
  export const isMain = RUNTIME_IS_MAIN_THREAD;
23
23
  export { endpointSymbol as endpointSymbol };
24
24
  /**
25
- * With this information we can recreate the logical order of
26
- * relevant exported functions from a file, also it helps to
27
- * track a task before naming, ` export ` elements have to be declared
28
- * at top level and without branching, we take advantage of this to
29
- * correctly map them.
30
- *
25
+ * Reconstructs stable task order from top-level exports before names are bound.
31
26
  */
32
27
  export const toListAndIds = (args) => {
33
- const result = Object.values(args)
28
+ const result = args
34
29
  .reduce((acc, v) => (acc[0].add(v.importedFrom),
35
30
  acc[1].add(v.id),
36
31
  acc[2].add(v.at),
32
+ acc[3].push(v.name),
37
33
  acc), [
38
34
  new Set(),
39
35
  new Set(),
40
- new Set()
36
+ new Set(),
37
+ [],
41
38
  ]);
42
39
  return {
43
40
  list: [...result[0]],
44
41
  ids: [...result[1]],
45
42
  at: [...result[2]],
43
+ names: result[3],
46
44
  };
47
45
  };
48
46
  const resolveImportHref = (href, callerHref) => {
@@ -73,7 +71,30 @@ const resolveWorkerBootstrapSettings = (worker, callerHref) => {
73
71
  },
74
72
  };
75
73
  };
76
- export const createPool = ({ threads, debug, inliner, balancer, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, source, worker, workerExecArgv, permission, dispatcher, host, }) => (tasks) => {
74
+ const isTaskDefinition = (value) => value != null &&
75
+ typeof value === "object" &&
76
+ typeof value.f === "function";
77
+ const toPoolTaskEntries = (input, callerHref) => Object.entries(input).map(([name, value]) => {
78
+ if (isTaskDefinition(value)) {
79
+ return {
80
+ ...value,
81
+ name,
82
+ };
83
+ }
84
+ if (typeof value === "function") {
85
+ return {
86
+ f: value,
87
+ id: -1,
88
+ importedFrom: new URL(callerHref).href,
89
+ at: -1,
90
+ name,
91
+ [endpointSymbol]: true,
92
+ };
93
+ }
94
+ throw new TypeError(`createPool task "${name}" must be a task definition or exported function`);
95
+ });
96
+ export const createPool = ({ threads, debug, inliner, balancer, payload, unsafe, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, source, worker, workerExecArgv, permission, dispatcher, host, }) => (tasks) => {
97
+ const bufferReferenceReturn = unsafe?.BufferReferenceReturn;
77
98
  /**
78
99
  * This functions is only available in the main thread.
79
100
  * Also triggers when debug extra is enabled.
@@ -97,14 +118,14 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
97
118
  //@ts-ignore
98
119
  return {
99
120
  shutdown: mainThreadOnlyProxy,
121
+ [Symbol.dispose]: () => { },
100
122
  call: mainThreadOnlyProxy,
101
123
  };
102
124
  }
103
- const { list, ids, at } = toListAndIds(tasks), listOfFunctions = Object.entries(tasks).map(([k, v]) => ({
104
- ...v,
105
- name: k,
106
- }))
125
+ const callerHref = getCallerHref(3);
126
+ const listOfFunctions = toPoolTaskEntries(tasks, callerHref)
107
127
  .sort((a, b) => a.name.localeCompare(b.name));
128
+ const { list, ids, names, at } = toListAndIds(listOfFunctions);
108
129
  if (listOfFunctions.length > MAX_FUNCTION_COUNT) {
109
130
  throw new RangeError(`Too many tasks: received ${listOfFunctions.length}. ` +
110
131
  `Maximum is ${MAX_FUNCTION_COUNT} (Uint16 function IDs: 0..${MAX_FUNCTION_ID}).`);
@@ -177,18 +198,28 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
177
198
  const execArgv = sanitizeExecArgv(combinedExecArgv.length > 0 ? combinedExecArgv : undefined);
178
199
  const hostDispatcher = host ?? dispatcher;
179
200
  const usesAbortSignal = listOfFunctions.some((fn) => fn.abortSignal !== undefined);
180
- const resolvedWorker = resolveWorkerBootstrapSettings(worker, getCallerHref(3));
201
+ const resolvedWorker = resolveWorkerBootstrapSettings(worker, callerHref);
181
202
  if (usingInliner && resolvedWorker?.bootstrap !== undefined) {
182
203
  throw new Error("worker.bootstrap cannot be used with the inliner");
183
204
  }
184
205
  const hardTimeoutMs = Number.isFinite(resolvedWorker?.hardTimeoutMs)
185
206
  ? Math.max(1, Math.floor(resolvedWorker?.hardTimeoutMs))
186
207
  : undefined;
208
+ if (RUNTIME_POOL_DEPTH >= 1) {
209
+ throw new Error(`createPool() tried to spawn workers from inside a worker process ` +
210
+ `(pool depth ${RUNTIME_POOL_DEPTH}). This usually means a pool is ` +
211
+ `created at module scope in a module your workers import, so every ` +
212
+ `worker spawns its own pool recursively. Is your createPool protected ` +
213
+ `by isMain? Guard pool creation behind \`if (isMain) { ... }\` ` +
214
+ `(import { isMain } from "knitting") so only the main program starts ` +
215
+ `the pool.`);
216
+ }
187
217
  let workers = Array.from({
188
218
  length: threads ?? 1,
189
219
  }).map((_, thread) => spawnWorkerContext({
190
220
  list,
191
221
  ids,
222
+ names,
192
223
  at,
193
224
  thread,
194
225
  debug,
@@ -198,6 +229,7 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
198
229
  workerExecArgv: execArgv,
199
230
  host: hostDispatcher,
200
231
  payload,
232
+ bufferReferenceReturn,
201
233
  payloadInitialBytes,
202
234
  payloadMaxBytes,
203
235
  bufferMode,
@@ -208,7 +240,7 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
208
240
  }));
209
241
  if (usingInliner) {
210
242
  const mainThread = createInlineExecutor({
211
- tasks,
243
+ tasks: listOfFunctions,
212
244
  genTaskID,
213
245
  batchSize: inliner?.batchSize ?? 1,
214
246
  });
@@ -293,11 +325,15 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
293
325
  })();
294
326
  return shutdownPromise;
295
327
  };
328
+ const disposePool = () => {
329
+ void shutdownWithDelay();
330
+ };
296
331
  const indexedFunctions = listOfFunctions.map((fn, index) => ({
297
332
  name: fn.name,
298
333
  index,
299
334
  timeout: fn.timeout,
300
335
  abortSignal: fn.abortSignal,
336
+ imported: fn.imported === true,
301
337
  }));
302
338
  const callHandlers = new Map();
303
339
  for (const { name } of indexedFunctions) {
@@ -316,9 +352,38 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
316
352
  }
317
353
  }
318
354
  const useDirectHandler = (threads ?? 1) === 1 && !usingInliner;
319
- const buildInvoker = (handlers) => useDirectHandler
320
- ? handlers[0]
321
- : managerMethod({
355
+ // Imported tasks must never execute on the host inliner lane: their module
356
+ // import is meant to happen inside the worker so worker permission policies
357
+ // apply. When the inliner is active we strip the inline lane from their
358
+ // handler set so they only ever reach real worker lanes.
359
+ const buildImportedInvoker = (handlers) => {
360
+ const workerHandlers = [];
361
+ const workerContexts = [];
362
+ for (let lane = 0; lane < handlers.length; lane += 1) {
363
+ if (lane === inlinerIndex)
364
+ continue;
365
+ workerHandlers.push(handlers[lane]);
366
+ workerContexts.push(workers[lane]);
367
+ }
368
+ if (workerHandlers.length === 0) {
369
+ throw new Error("Imported task has no worker lane to run on: the pool only has the " +
370
+ "host inliner. Imported tasks are never inlined on the host; add at " +
371
+ "least one worker thread.");
372
+ }
373
+ if (workerHandlers.length === 1)
374
+ return workerHandlers[0];
375
+ return managerMethod({
376
+ contexts: workerContexts,
377
+ balancer,
378
+ handlers: workerHandlers,
379
+ // No inlinerGate: the inline lane is intentionally excluded here.
380
+ });
381
+ };
382
+ const buildInvoker = (handlers, imported) => {
383
+ if (imported && usingInliner) {
384
+ return buildImportedInvoker(handlers);
385
+ }
386
+ return useDirectHandler ? handlers[0] : managerMethod({
322
387
  contexts: workers,
323
388
  balancer,
324
389
  handlers,
@@ -329,9 +394,23 @@ export const createPool = ({ threads, debug, inliner, balancer, payload, payload
329
394
  }
330
395
  : undefined,
331
396
  });
332
- const callEntries = Array.from(callHandlers.entries(), ([name, handlers]) => [name, buildInvoker(handlers)]);
397
+ };
398
+ let callEntries;
399
+ try {
400
+ callEntries = indexedFunctions.map(({ name, imported }) => [name, buildInvoker(callHandlers.get(name), imported)]);
401
+ }
402
+ catch (error) {
403
+ // Building invokers can throw (e.g. an imported task with no worker lane,
404
+ // or a balancer that needs >=2 lanes). The inline executor and any spawned
405
+ // workers are already live here, so tear them down before propagating —
406
+ // otherwise their MessageChannel ports / worker handles keep the event
407
+ // loop alive and hang the process.
408
+ void closePoolNow();
409
+ throw error;
410
+ }
333
411
  return {
334
412
  shutdown: shutdownWithDelay,
413
+ [Symbol.dispose]: disposePool,
335
414
  call: Object.fromEntries(callEntries),
336
415
  };
337
416
  };
@@ -343,15 +422,17 @@ const createSingleTaskPool = (single, options) => {
343
422
  return {
344
423
  call: pool.call[SINGLE_TASK_KEY],
345
424
  shutdown: pool.shutdown,
425
+ [Symbol.dispose]: pool[Symbol.dispose],
346
426
  };
347
427
  };
348
- const buildTaskDefinitionFromCaller = (input, callerHref, at) => {
428
+ const buildTaskDefinitionFromCaller = (input, callerHref, at, imported = false) => {
349
429
  const importedFrom = new URL(callerHref).href;
350
430
  const out = ({
351
431
  ...input,
352
432
  id: genTaskID(),
353
433
  importedFrom,
354
434
  at,
435
+ imported,
355
436
  [endpointSymbol]: true,
356
437
  });
357
438
  out.createPool = (options) => {
@@ -404,5 +485,5 @@ export function importTask(options) {
404
485
  return buildTaskDefinitionFromCaller({
405
486
  ...rest,
406
487
  f: createImportedTaskFn(resolvedHref, name),
407
- }, callerHref, at);
488
+ }, callerHref, at, true);
408
489
  }
@@ -1,11 +1,17 @@
1
+ import type { BufferReference } from "../connections/buffer-reference.js";
2
+ import type { ProcessSharedBuffer } from "../connections/process-shared-buffer.js";
1
3
  type EnvelopeHeaderPrimitive = string | number | boolean | null;
2
4
  type EnvelopeHeaderValue = EnvelopeHeaderPrimitive | EnvelopeHeaderValue[] | {
3
5
  [key: string]: EnvelopeHeaderValue;
4
6
  };
5
7
  export type EnvelopeHeader = EnvelopeHeaderValue;
6
- export declare class Envelope<H extends EnvelopeHeader = EnvelopeHeader> {
8
+ export type EnvelopeBody = ArrayBuffer | SharedArrayBuffer | BufferReference | ProcessSharedBuffer;
9
+ declare const PayloadTransportFinalizer: unique symbol;
10
+ export declare class Envelope<H extends EnvelopeHeader = EnvelopeHeader, B extends EnvelopeBody = ArrayBuffer> {
7
11
  readonly header: H;
8
- readonly payload: ArrayBuffer;
9
- constructor(header: H, payload: ArrayBuffer);
12
+ readonly payload: B;
13
+ constructor(header: H, payload: B);
14
+ [Symbol.dispose](): void;
15
+ [PayloadTransportFinalizer](): (() => void) | undefined;
10
16
  }
11
17
  export {};
@@ -1,3 +1,4 @@
1
+ const PayloadTransportFinalizer = Symbol.for("knitting.payloadCodec.transportFinalizer");
1
2
  export class Envelope {
2
3
  header;
3
4
  payload;
@@ -5,4 +6,17 @@ export class Envelope {
5
6
  this.header = header;
6
7
  this.payload = payload;
7
8
  }
9
+ [Symbol.dispose]() {
10
+ const body = this.payload;
11
+ if (body !== null && typeof body === "object") {
12
+ body[Symbol.dispose]?.();
13
+ }
14
+ }
15
+ [PayloadTransportFinalizer]() {
16
+ const body = this.payload;
17
+ if (body === null || typeof body !== "object")
18
+ return undefined;
19
+ const finalizer = body[PayloadTransportFinalizer];
20
+ return typeof finalizer === "function" ? finalizer.call(body) : undefined;
21
+ }
8
22
  }
@@ -2,9 +2,13 @@ import { toModuleUrl } from "./module-url.js";
2
2
  export const genTaskID = ((counter) => () => counter++)(0);
3
3
  const INTERNAL_CALLER_HINTS = [
4
4
  "/src/common/task-source.ts",
5
+ "/src/common/task-source.js",
5
6
  "\\src\\common\\task-source.ts",
7
+ "\\src\\common\\task-source.js",
6
8
  "/src/api.ts",
9
+ "/src/api.js",
7
10
  "\\src\\api.ts",
11
+ "\\src\\api.js",
8
12
  ];
9
13
  const INTERNAL_CALLER_FUNCTIONS = new Set([
10
14
  "collectStackFrames",
@@ -28,7 +28,9 @@ export type RuntimeMessageChannelLike = {
28
28
  export declare const RUNTIME_PROCESS_WORKER_ENV = "KNITTING_PROCESS_WORKER";
29
29
  export declare const RUNTIME_PROCESS_WORKER_BOOT_ENV = "KNITTING_PROCESS_WORKER_BOOT";
30
30
  export declare const RUNTIME_PROCESS_WORKER_BOOT_VERSION = 1;
31
+ export declare const RUNTIME_POOL_DEPTH_ENV = "KNITTING_POOL_DEPTH";
31
32
  export declare const RUNTIME_IS_PROCESS_WORKER: boolean;
33
+ export declare const RUNTIME_POOL_DEPTH: number;
32
34
  export declare const RUNTIME_WORKER: new (specifier: string | URL, options?: Record<string, unknown>) => RuntimeWorkerLike;
33
35
  export declare const RUNTIME_MESSAGE_CHANNEL: new () => RuntimeMessageChannelLike;
34
36
  export declare const HAS_NODE_WORKER_THREADS: boolean;
@@ -2,8 +2,17 @@ import { getNodeBuiltinModule, getNodeProcess } from "./node-compat.js";
2
2
  export const RUNTIME_PROCESS_WORKER_ENV = "KNITTING_PROCESS_WORKER";
3
3
  export const RUNTIME_PROCESS_WORKER_BOOT_ENV = "KNITTING_PROCESS_WORKER_BOOT";
4
4
  export const RUNTIME_PROCESS_WORKER_BOOT_VERSION = 1;
5
+ export const RUNTIME_POOL_DEPTH_ENV = "KNITTING_POOL_DEPTH";
5
6
  const nodeProcess = getNodeProcess();
6
7
  export const RUNTIME_IS_PROCESS_WORKER = nodeProcess?.env?.[RUNTIME_PROCESS_WORKER_ENV] === "1";
8
+ const readPoolDepth = () => {
9
+ const raw = nodeProcess?.env?.[RUNTIME_POOL_DEPTH_ENV];
10
+ if (typeof raw !== "string")
11
+ return 0;
12
+ const parsed = Number.parseInt(raw, 10);
13
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
14
+ };
15
+ export const RUNTIME_POOL_DEPTH = readPoolDepth();
7
16
  const workerThreads = getNodeBuiltinModule("node:worker_threads");
8
17
  const isWorkerGlobalScope = () => {
9
18
  const scopeCtor = globalThis.WorkerGlobalScope;
@@ -0,0 +1,56 @@
1
+ export type BufferReferenceRuntime = "node" | "deno" | "bun";
2
+ /** A backing store the producer has taken exclusive ownership of (source detached). */
3
+ export type ProducedBuffer = {
4
+ /** Producer-side release handle. */
5
+ token: bigint;
6
+ /** Address of the region start (view offset already applied). */
7
+ pointer: bigint;
8
+ /** Offset of the region inside the backing store (used by the Node owning adopt). */
9
+ byteOffset: number;
10
+ byteLength: number;
11
+ };
12
+ export type AdoptInput = {
13
+ token: bigint;
14
+ pointer: bigint;
15
+ byteOffset: number;
16
+ byteLength: number;
17
+ };
18
+ /** The materialized region: bytes live in `buffer` at `[byteOffset, byteOffset+byteLength)`. */
19
+ export type AdoptedRegion = {
20
+ buffer: ArrayBuffer;
21
+ byteOffset: number;
22
+ byteLength: number;
23
+ };
24
+ /** Materialized SAB region; `isShared` means `buffer` is a real SharedArrayBuffer. */
25
+ export type SharedAdoptedRegion = {
26
+ buffer: SharedArrayBuffer | ArrayBuffer;
27
+ byteOffset: number;
28
+ byteLength: number;
29
+ isShared: boolean;
30
+ };
31
+ export type AdoptOptions = {
32
+ /** Copy when bytes must survive producer release on non-owning runtimes. */
33
+ copy?: boolean;
34
+ };
35
+ type MovableBufferSource = ArrayBuffer | ArrayBufferView;
36
+ export type BufferReferenceCapabilities = {
37
+ readonly runtime: BufferReferenceRuntime;
38
+ /** True when `adopt` returns a buffer that co-owns the store and survives `release`. */
39
+ readonly supportsOwningAdopt: boolean;
40
+ /** Take exclusive ownership of `source`'s bytes, detaching the source. */
41
+ produce(source: MovableBufferSource): ProducedBuffer;
42
+ /** Materialize the region in the current isolate (owning on Node, alias/copy elsewhere). */
43
+ adopt(input: AdoptInput, opts?: AdoptOptions): AdoptedRegion;
44
+ /** Drop the producer's hold on the backing store. */
45
+ release(token: bigint): void;
46
+ /** True when `adoptShared` returns a real SharedArrayBuffer (Node). */
47
+ readonly supportsSharedAdopt: boolean;
48
+ /** Share a SharedArrayBuffer by reference (no detach — SABs persist). */
49
+ produceShared(sab: SharedArrayBuffer): ProducedBuffer;
50
+ /** Materialize a shared region as a non-owning alias over the same bytes. */
51
+ adoptShared(input: AdoptInput): SharedAdoptedRegion;
52
+ /** Drop the producer's pin on a shared buffer (JS-side; teardown-safe). */
53
+ releaseShared(token: bigint): void;
54
+ };
55
+ export declare const getBufferReferenceCapabilities: () => BufferReferenceCapabilities;
56
+ export {};