knitting 0.1.54 → 0.1.55

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 (135) hide show
  1. package/README.md +3 -3
  2. package/knitting.d.ts +797 -5
  3. package/knitting.js +9780 -5
  4. package/map.md +9 -12
  5. package/package.json +14 -13
  6. package/shared-memory.d.ts +155 -0
  7. package/shared-memory.js +1342 -0
  8. package/unsafe.d.ts +71 -1
  9. package/unsafe.js +878 -1
  10. package/utils.d.ts +31 -1
  11. package/utils.js +116 -1
  12. package/process-shared-buffer.d.ts +0 -1
  13. package/process-shared-buffer.js +0 -1
  14. package/src/api.d.ts +0 -69
  15. package/src/api.js +0 -627
  16. package/src/common/envelope.d.ts +0 -17
  17. package/src/common/envelope.js +0 -22
  18. package/src/common/module-url.d.ts +0 -1
  19. package/src/common/module-url.js +0 -24
  20. package/src/common/node-compat.d.ts +0 -20
  21. package/src/common/node-compat.js +0 -24
  22. package/src/common/path-canonical.d.ts +0 -6
  23. package/src/common/path-canonical.js +0 -41
  24. package/src/common/runtime.d.ts +0 -15
  25. package/src/common/runtime.js +0 -91
  26. package/src/common/shared-buffer-region.d.ts +0 -11
  27. package/src/common/shared-buffer-region.js +0 -21
  28. package/src/common/shared-buffer-text.d.ts +0 -16
  29. package/src/common/shared-buffer-text.js +0 -65
  30. package/src/common/task-source.d.ts +0 -3
  31. package/src/common/task-source.js +0 -84
  32. package/src/common/task-symbol.d.ts +0 -1
  33. package/src/common/task-symbol.js +0 -1
  34. package/src/common/with-resolvers.d.ts +0 -9
  35. package/src/common/with-resolvers.js +0 -23
  36. package/src/common/worker-runtime.d.ts +0 -42
  37. package/src/common/worker-runtime.js +0 -61
  38. package/src/connections/buffer-reference-native.d.ts +0 -56
  39. package/src/connections/buffer-reference-native.js +0 -217
  40. package/src/connections/buffer-reference.d.ts +0 -78
  41. package/src/connections/buffer-reference.js +0 -461
  42. package/src/connections/bun.d.ts +0 -22
  43. package/src/connections/bun.js +0 -214
  44. package/src/connections/deno.d.ts +0 -22
  45. package/src/connections/deno.js +0 -205
  46. package/src/connections/file-descriptor.d.ts +0 -39
  47. package/src/connections/file-descriptor.js +0 -161
  48. package/src/connections/index.d.ts +0 -4
  49. package/src/connections/index.js +0 -4
  50. package/src/connections/node-addons.d.ts +0 -5
  51. package/src/connections/node-addons.js +0 -43
  52. package/src/connections/node-buffer-pointer.d.ts +0 -20
  53. package/src/connections/node-buffer-pointer.js +0 -16
  54. package/src/connections/node.d.ts +0 -30
  55. package/src/connections/node.js +0 -62
  56. package/src/connections/posix.d.ts +0 -32
  57. package/src/connections/posix.js +0 -77
  58. package/src/connections/process-shared-buffer.d.ts +0 -75
  59. package/src/connections/process-shared-buffer.js +0 -279
  60. package/src/connections/shared-array-buffer-payload.d.ts +0 -36
  61. package/src/connections/shared-array-buffer-payload.js +0 -235
  62. package/src/connections/types.d.ts +0 -50
  63. package/src/connections/types.js +0 -37
  64. package/src/connections/windows.d.ts +0 -28
  65. package/src/connections/windows.js +0 -224
  66. package/src/debug/env-diff.d.ts +0 -26
  67. package/src/debug/env-diff.js +0 -49
  68. package/src/debug/gate.d.ts +0 -18
  69. package/src/debug/gate.js +0 -69
  70. package/src/debug/handle.d.ts +0 -23
  71. package/src/debug/handle.js +0 -48
  72. package/src/error.d.ts +0 -13
  73. package/src/error.js +0 -49
  74. package/src/ipc/tools/ring-queue.d.ts +0 -33
  75. package/src/ipc/tools/ring-queue.js +0 -159
  76. package/src/ipc/transport/shared-memory.d.ts +0 -23
  77. package/src/ipc/transport/shared-memory.js +0 -35
  78. package/src/memory/byte-carpet.d.ts +0 -73
  79. package/src/memory/byte-carpet.js +0 -157
  80. package/src/memory/lock.d.ts +0 -201
  81. package/src/memory/lock.js +0 -739
  82. package/src/memory/payload-config.d.ts +0 -31
  83. package/src/memory/payload-config.js +0 -67
  84. package/src/memory/payloadCodec.d.ts +0 -46
  85. package/src/memory/payloadCodec.js +0 -1334
  86. package/src/memory/regionRegistry.d.ts +0 -17
  87. package/src/memory/regionRegistry.js +0 -285
  88. package/src/memory/shared-buffer-io.d.ts +0 -55
  89. package/src/memory/shared-buffer-io.js +0 -403
  90. package/src/permission/index.d.ts +0 -2
  91. package/src/permission/index.js +0 -2
  92. package/src/permission/protocol.d.ts +0 -166
  93. package/src/permission/protocol.js +0 -650
  94. package/src/runtime/balancer.d.ts +0 -19
  95. package/src/runtime/balancer.js +0 -149
  96. package/src/runtime/dispatcher.d.ts +0 -40
  97. package/src/runtime/dispatcher.js +0 -142
  98. package/src/runtime/inline-executor.d.ts +0 -10
  99. package/src/runtime/inline-executor.js +0 -282
  100. package/src/runtime/pool.d.ts +0 -39
  101. package/src/runtime/pool.js +0 -404
  102. package/src/runtime/process-worker.d.ts +0 -92
  103. package/src/runtime/process-worker.js +0 -673
  104. package/src/runtime/tx-queue.d.ts +0 -26
  105. package/src/runtime/tx-queue.js +0 -155
  106. package/src/shared/abortSignal.d.ts +0 -23
  107. package/src/shared/abortSignal.js +0 -134
  108. package/src/types.d.ts +0 -357
  109. package/src/types.js +0 -2
  110. package/src/utils/http.d.ts +0 -29
  111. package/src/utils/http.js +0 -100
  112. package/src/worker/bootstrap.d.ts +0 -5
  113. package/src/worker/bootstrap.js +0 -78
  114. package/src/worker/composable-runners.d.ts +0 -12
  115. package/src/worker/composable-runners.js +0 -97
  116. package/src/worker/loop.d.ts +0 -2
  117. package/src/worker/loop.js +0 -382
  118. package/src/worker/process-worker-bootstrap.d.ts +0 -8
  119. package/src/worker/process-worker-bootstrap.js +0 -160
  120. package/src/worker/rx-queue.d.ts +0 -25
  121. package/src/worker/rx-queue.js +0 -173
  122. package/src/worker/safety/index.d.ts +0 -4
  123. package/src/worker/safety/index.js +0 -4
  124. package/src/worker/safety/performance.d.ts +0 -1
  125. package/src/worker/safety/performance.js +0 -17
  126. package/src/worker/safety/process.d.ts +0 -2
  127. package/src/worker/safety/process.js +0 -79
  128. package/src/worker/safety/startup.d.ts +0 -16
  129. package/src/worker/safety/startup.js +0 -30
  130. package/src/worker/safety/worker-data.d.ts +0 -2
  131. package/src/worker/safety/worker-data.js +0 -36
  132. package/src/worker/task-loader.d.ts +0 -27
  133. package/src/worker/task-loader.js +0 -78
  134. package/src/worker/timers.d.ts +0 -18
  135. package/src/worker/timers.js +0 -116
package/src/api.js DELETED
@@ -1,627 +0,0 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
- import { getCallerFilePath, getCallerHref } from "./common/task-source.js";
10
- import { DEBUG_ENABLED, resolveDebugNamespaces } from "./debug/gate.js";
11
- import { genTaskID } from "./common/task-source.js";
12
- import { toModuleUrl } from "./common/module-url.js";
13
- import { endpointSymbol } from "./common/task-symbol.js";
14
- import { spawnWorkerContext } from "./runtime/pool.js";
15
- import { ChannelHandler } from "./runtime/dispatcher.js";
16
- import { RUNTIME } from "./common/runtime.js";
17
- import { RUNTIME_IS_MAIN_THREAD, RUNTIME_POOL_DEPTH, RUNTIME_WORKER_DATA, } from "./common/worker-runtime.js";
18
- import { resolvePermissionProtocol, toRuntimePermissionFlags, } from "./permission/index.js";
19
- import { getNodeProcess } from "./common/node-compat.js";
20
- import { managerMethod } from "./runtime/balancer.js";
21
- import { createInlineExecutor } from "./runtime/inline-executor.js";
22
- const hasDebugNamespace = (namespaces, namespace) => namespaces.has("*") || namespaces.has(namespace);
23
- const createHostDebug = (namespaces) => {
24
- const enabled = (namespace) => hasDebugNamespace(namespaces, namespace);
25
- if (!enabled("host"))
26
- return undefined;
27
- const base = performance.now();
28
- const tag = `host·${RUNTIME}`;
29
- const log = (message) => {
30
- const elapsed = (performance.now() - base).toFixed(1);
31
- console.error(`[${tag}·+${elapsed}ms] host: ${message}`);
32
- };
33
- return { log };
34
- };
35
- const readHostCwd = () => {
36
- const denoCwd = globalThis.Deno?.cwd;
37
- if (typeof denoCwd === "function") {
38
- try {
39
- return denoCwd();
40
- }
41
- catch {
42
- }
43
- }
44
- const nodeProcess = getNodeProcess();
45
- if (typeof nodeProcess?.cwd === "function") {
46
- try {
47
- return nodeProcess.cwd();
48
- }
49
- catch {
50
- return undefined;
51
- }
52
- }
53
- return undefined;
54
- };
55
- const formatDebugList = (values, empty = "(none)") => values && values.length > 0 ? values.join(",") : empty;
56
- const MAX_FUNCTION_ID = 0xFFFF;
57
- const MAX_FUNCTION_COUNT = MAX_FUNCTION_ID + 1;
58
- const DEFAULT_IMPORT_EXPORT_NAME = "default";
59
- export const isMain = RUNTIME_IS_MAIN_THREAD;
60
- export { endpointSymbol as endpointSymbol };
61
- /**
62
- * Reconstructs stable task order from top-level exports before names are bound.
63
- */
64
- export const toListAndIds = (args) => {
65
- const result = args
66
- .reduce((acc, v) => (acc[0].add(v.importedFrom),
67
- acc[1].add(v.id),
68
- acc[2].add(v.at),
69
- acc[3].push(v.name),
70
- acc), [
71
- new Set(),
72
- new Set(),
73
- new Set(),
74
- [],
75
- ]);
76
- return {
77
- list: [...result[0]],
78
- ids: [...result[1]],
79
- at: [...result[2]],
80
- names: result[3],
81
- };
82
- };
83
- const resolveImportHref = (href, callerHref) => {
84
- try {
85
- return new URL(href, callerHref).href;
86
- }
87
- catch {
88
- return toModuleUrl(href);
89
- }
90
- };
91
- const resolveWorkerBootstrapSettings = (worker, callerHref) => {
92
- const bootstrap = worker?.bootstrap;
93
- if (bootstrap === undefined)
94
- return worker;
95
- const name = bootstrap.name ?? DEFAULT_IMPORT_EXPORT_NAME;
96
- if (typeof bootstrap.href !== "string" || bootstrap.href.length === 0) {
97
- throw new TypeError("worker.bootstrap.href must be a non-empty string");
98
- }
99
- if (typeof name !== "string" || name.length === 0) {
100
- throw new TypeError("worker.bootstrap.name must be a non-empty string");
101
- }
102
- return {
103
- ...worker,
104
- bootstrap: {
105
- ...bootstrap,
106
- href: resolveImportHref(bootstrap.href, callerHref),
107
- name,
108
- },
109
- };
110
- };
111
- const isTaskDefinition = (value) => value != null &&
112
- typeof value === "object" &&
113
- typeof value.f === "function";
114
- const toPoolTaskEntries = (input, callerHref) => Object.entries(input).map(([name, value]) => {
115
- if (isTaskDefinition(value)) {
116
- return {
117
- ...value,
118
- name,
119
- };
120
- }
121
- if (typeof value === "function") {
122
- return {
123
- f: value,
124
- id: -1,
125
- importedFrom: new URL(callerHref).href,
126
- at: -1,
127
- name,
128
- [endpointSymbol]: true,
129
- };
130
- }
131
- throw new TypeError(`createPool task "${name}" must be a task definition or exported function`);
132
- });
133
- /**
134
- * Create a typed worker pool from module-scope exported tasks/functions.
135
- *
136
- * Install/import as `knitting` on npm or `@vixeny/knitting` on JSR. Requires
137
- * Node 22+, Deno 2+, or Bun 1+.
138
- *
139
- * Use `createPool(options)({ taskA })`, call `await pool.call.taskA(arg)`, and
140
- * clean up with `using pool = ...` or `await pool.shutdown()`.
141
- *
142
- * Guard host-only pool setup with `isMain`: workers re-import task modules, and
143
- * top-level imports in those modules run in every worker. Keep task modules
144
- * lean and separate from server/framework setup. Each task receives one
145
- * argument; use an object or tuple for multiple values.
146
- */
147
- export const createPool = ({ threads, debug, inliner, balancer, payload, unsafe, abortSignalCapacity, source, worker, workerExecArgv, permission, host, }) => (tasks) => {
148
- const bufferReferenceReturn = unsafe?.BufferReferenceReturn;
149
- const debugRequested = DEBUG_ENABLED ||
150
- (debug !== undefined && debug !== false);
151
- let debugNamespaces;
152
- const getDebugNamespaces = () => debugNamespaces ??= resolveDebugNamespaces(debug);
153
- const hostDebug = debugRequested
154
- ? createHostDebug(getDebugNamespaces())
155
- : undefined;
156
- const debugEnabled = (namespace) => debugRequested && hasDebugNamespace(getDebugNamespaces(), namespace);
157
- /**
158
- * This functions is only available in the main thread.
159
- * Also triggers when debug extra is enabled.
160
- */
161
- if (RUNTIME_IS_MAIN_THREAD === false) {
162
- if (debugEnabled("lifecycle")) {
163
- console.warn("createPool has been called with : " + JSON.stringify(RUNTIME_WORKER_DATA));
164
- }
165
- const notMainThreadError = () => {
166
- throw new Error("createPool can only be called in the main thread.");
167
- };
168
- const throwingProxyTarget = function () {
169
- return notMainThreadError();
170
- };
171
- const throwingProxyHandler = {
172
- get: function () {
173
- return notMainThreadError;
174
- },
175
- };
176
- const mainThreadOnlyProxy = new Proxy(throwingProxyTarget, throwingProxyHandler);
177
- //@ts-ignore
178
- return {
179
- shutdown: mainThreadOnlyProxy,
180
- [Symbol.dispose]: () => { },
181
- call: mainThreadOnlyProxy,
182
- };
183
- }
184
- const callerHref = getCallerHref(3);
185
- const listOfFunctions = toPoolTaskEntries(tasks, callerHref)
186
- .sort((a, b) => a.name.localeCompare(b.name));
187
- const { list, ids, names, at } = toListAndIds(listOfFunctions);
188
- hostDebug?.log(`cwd=${readHostCwd() ?? "(unknown)"} caller=${callerHref}`);
189
- listOfFunctions.forEach((fn) => {
190
- hostDebug?.log(`task name=${fn.name} id=${fn.id} from=${fn.importedFrom}`);
191
- });
192
- if (listOfFunctions.length > MAX_FUNCTION_COUNT) {
193
- throw new RangeError(`Too many tasks: received ${listOfFunctions.length}. ` +
194
- `Maximum is ${MAX_FUNCTION_COUNT} (Uint16 function IDs: 0..${MAX_FUNCTION_ID}).`);
195
- }
196
- const usingInliner = typeof inliner === "object" && inliner != null;
197
- const totalNumberOfThread = (threads ?? 1) +
198
- (usingInliner ? 1 : 0);
199
- const permissionProtocol = resolvePermissionProtocol({
200
- permission: permission ?? {
201
- mode: "strict",
202
- allowImport: true,
203
- },
204
- modules: list,
205
- });
206
- const permissionExecArgv = toRuntimePermissionFlags(permissionProtocol);
207
- const nodeProcess = getNodeProcess();
208
- const allowedFlags = nodeProcess?.allowedNodeEnvironmentFlags ?? null;
209
- const isNodePermissionFlag = (flag) => {
210
- const key = flag.split("=", 1)[0];
211
- return key === "--permission" ||
212
- key === "--experimental-permission" ||
213
- key === "--allow-fs-read" ||
214
- key === "--allow-fs-write" ||
215
- key === "--allow-worker" ||
216
- key === "--allow-child-process" ||
217
- key === "--allow-addons" ||
218
- key === "--allow-wasi";
219
- };
220
- const stripNodePermissionFlags = (flags) => flags?.filter((flag) => !isNodePermissionFlag(flag));
221
- const dedupeFlags = (flags) => {
222
- const out = [];
223
- const seen = new Set();
224
- for (const flag of flags) {
225
- if (seen.has(flag))
226
- continue;
227
- seen.add(flag);
228
- out.push(flag);
229
- }
230
- return out;
231
- };
232
- const sanitizeExecArgv = (flags) => {
233
- if (!flags || flags.length === 0)
234
- return undefined;
235
- if (!allowedFlags)
236
- return flags;
237
- const filtered = flags.filter((flag) => {
238
- const key = flag.split("=", 1)[0];
239
- return allowedFlags.has(key);
240
- });
241
- return filtered.length > 0 ? filtered : undefined;
242
- };
243
- const inheritedExecArgv = Array.isArray(nodeProcess?.execArgv)
244
- ? nodeProcess.execArgv
245
- : undefined;
246
- const defaultExecArgvCandidate = workerExecArgv ??
247
- (inheritedExecArgv
248
- ? (allowedFlags?.has("--expose-gc") === true
249
- ? (inheritedExecArgv.includes("--expose-gc")
250
- ? inheritedExecArgv
251
- : [...inheritedExecArgv, "--expose-gc"])
252
- : inheritedExecArgv)
253
- : undefined);
254
- const defaultExecArgv = permissionProtocol?.unsafe === true
255
- ? stripNodePermissionFlags(defaultExecArgvCandidate)
256
- : defaultExecArgvCandidate;
257
- const combinedExecArgv = dedupeFlags([
258
- ...permissionExecArgv,
259
- ...(defaultExecArgv ?? []),
260
- ]);
261
- const execArgv = sanitizeExecArgv(combinedExecArgv.length > 0 ? combinedExecArgv : undefined);
262
- hostDebug?.log(`pool runtime=${RUNTIME} workers=${threads ?? 1}` +
263
- ` lanes=${totalNumberOfThread} inliner=${usingInliner ? "on" : "off"}`);
264
- hostDebug?.log(`modules=${formatDebugList(list)}`);
265
- hostDebug?.log(`permission=${permissionProtocol?.mode ?? "off"} execArgv=${formatDebugList(execArgv)}`);
266
- const usesAbortSignal = listOfFunctions.some((fn) => fn.abortSignal !== undefined);
267
- const resolvedWorker = resolveWorkerBootstrapSettings(worker, callerHref);
268
- if (resolvedWorker?.bootstrap !== undefined) {
269
- hostDebug?.log(`bootstrap href=${resolvedWorker.bootstrap.href}` +
270
- ` name=${resolvedWorker.bootstrap.name}`);
271
- }
272
- if (usingInliner && resolvedWorker?.bootstrap !== undefined) {
273
- throw new Error("worker.bootstrap cannot be used with the inliner");
274
- }
275
- const hardTimeoutMs = Number.isFinite(resolvedWorker?.hardTimeoutMs)
276
- ? Math.max(1, Math.floor(resolvedWorker?.hardTimeoutMs))
277
- : undefined;
278
- if (RUNTIME_POOL_DEPTH >= 1) {
279
- throw new Error(`createPool() tried to spawn workers from inside a worker process ` +
280
- `(pool depth ${RUNTIME_POOL_DEPTH}). This usually means a pool is ` +
281
- `created at module scope in a module your workers import, so every ` +
282
- `worker spawns its own pool recursively. Is your createPool protected ` +
283
- `by isMain? Guard pool creation behind \`if (isMain) { ... }\` ` +
284
- `(import { isMain } from "knitting") so only the main program starts ` +
285
- `the pool.`);
286
- }
287
- const dispatcherEnv = nodeProcess?.env?.KNITTING_DISPATCHER;
288
- const explicitDispatcher = host?.dispatcher ??
289
- (dispatcherEnv === "serial-channel" || dispatcherEnv === "per-thread"
290
- ? dispatcherEnv
291
- : undefined);
292
- const autoDispatcher = (() => {
293
- // Experimental default for HTTP-style bursts: Bun still favors direct
294
- // per-thread channels, while Node/Deno multi-worker pools favor one shared
295
- // macro channel over the per-lane dispatcher checks.
296
- if (RUNTIME === "bun")
297
- return "per-thread";
298
- if ((threads ?? 1) <= 1)
299
- return "per-thread";
300
- return "serial-channel";
301
- })();
302
- const dispatcher = explicitDispatcher ?? autoDispatcher;
303
- const serialChannel = dispatcher === "serial-channel";
304
- const serialDispatcherChannel = serialChannel
305
- ? new ChannelHandler()
306
- : undefined;
307
- let workers = Array.from({
308
- length: threads ?? 1,
309
- }).map((_, thread) => spawnWorkerContext({
310
- list,
311
- ids,
312
- names,
313
- at,
314
- thread,
315
- debug,
316
- hostDebug: hostDebug?.log,
317
- totalNumberOfThread,
318
- source,
319
- workerOptions: resolvedWorker,
320
- workerExecArgv: execArgv,
321
- host,
322
- payload,
323
- bufferReferenceReturn,
324
- abortSignalCapacity,
325
- usesAbortSignal,
326
- permission: permissionProtocol,
327
- sharedChannelHandler: serialDispatcherChannel,
328
- }));
329
- const sharedDispatcherChannel = serialDispatcherChannel;
330
- if (serialChannel) {
331
- const channel = serialDispatcherChannel;
332
- const checks = workers.map((context) => context.dispatcherCheck);
333
- let serialScheduled = false;
334
- let serialInFlight = false;
335
- let serialRerun = false;
336
- const runSerialChecks = () => {
337
- if (serialInFlight) {
338
- serialRerun = true;
339
- return;
340
- }
341
- serialInFlight = true;
342
- do {
343
- serialRerun = false;
344
- serialScheduled = false;
345
- for (let index = 0; index < checks.length; index++) {
346
- const check = checks[index];
347
- if (check.isRunning !== true)
348
- check.isRunning = true;
349
- check();
350
- }
351
- } while (serialRerun);
352
- serialInFlight = false;
353
- };
354
- const scheduleSerialCheck = () => {
355
- if (serialInFlight) {
356
- serialRerun = true;
357
- return;
358
- }
359
- if (serialScheduled)
360
- return;
361
- serialScheduled = true;
362
- Promise.resolve().then(runSerialChecks);
363
- };
364
- channel.open(runSerialChecks);
365
- workers.forEach((context) => {
366
- const wake = context.laneWake;
367
- context.bindSend(() => {
368
- scheduleSerialCheck();
369
- wake();
370
- });
371
- });
372
- hostDebug?.log(`dispatcher=serial-channel lanes=${checks.length}`);
373
- }
374
- else {
375
- hostDebug?.log(`dispatcher=per-thread lanes=${workers.length}`);
376
- }
377
- if (usingInliner) {
378
- const mainThread = createInlineExecutor({
379
- tasks: listOfFunctions,
380
- genTaskID,
381
- batchSize: inliner?.batchSize ?? 1,
382
- });
383
- if (inliner?.position === "first") {
384
- workers = [
385
- //@ts-ignore
386
- mainThread,
387
- ...workers,
388
- ];
389
- }
390
- else {
391
- workers.push(
392
- //@ts-ignore
393
- mainThread);
394
- }
395
- }
396
- const inlinerIndex = usingInliner
397
- ? (inliner?.position === "first" ? 0 : workers.length - 1)
398
- : -1;
399
- const inlinerDispatchThreshold = Number.isFinite(inliner?.dispatchThreshold)
400
- ? Math.max(1, Math.floor(inliner?.dispatchThreshold ?? 1))
401
- : 1;
402
- let closing = false;
403
- let closePromise;
404
- let shutdownPromise;
405
- const closePoolNow = () => {
406
- if (closePromise)
407
- return closePromise;
408
- closing = true;
409
- closePromise = Promise.allSettled(workers.map((context) => context.kills()))
410
- .then(() => {
411
- sharedDispatcherChannel?.close();
412
- });
413
- return closePromise;
414
- };
415
- const wrapGuardedInvoke = ({ invoke, taskName, }) => (args) => {
416
- if (closing) {
417
- return Promise.reject(new Error("Pool is shut down"));
418
- }
419
- const pending = invoke(args);
420
- if (!hardTimeoutMs)
421
- return pending;
422
- return new Promise((resolve, reject) => {
423
- let settled = false;
424
- const timeoutId = setTimeout(() => {
425
- if (settled)
426
- return;
427
- settled = true;
428
- reject(new Error(`Task hard timeout after ${hardTimeoutMs}ms (${taskName}); pool force-shutdown`));
429
- void closePoolNow();
430
- }, hardTimeoutMs);
431
- pending.then((value) => {
432
- if (settled)
433
- return;
434
- settled = true;
435
- clearTimeout(timeoutId);
436
- resolve(value);
437
- }, (error) => {
438
- if (settled)
439
- return;
440
- settled = true;
441
- clearTimeout(timeoutId);
442
- reject(error);
443
- });
444
- });
445
- };
446
- const shutdownWithDelay = (delayMs) => {
447
- if (closePromise)
448
- return closePromise;
449
- if (shutdownPromise)
450
- return shutdownPromise;
451
- const ms = Number.isFinite(delayMs)
452
- ? Math.max(0, Math.floor(delayMs))
453
- : 0;
454
- shutdownPromise = (async () => {
455
- if (closePromise)
456
- return await closePromise;
457
- if (ms > 0) {
458
- await new Promise((resolve) => setTimeout(resolve, ms));
459
- }
460
- if (closePromise)
461
- return await closePromise;
462
- await closePoolNow();
463
- })();
464
- return shutdownPromise;
465
- };
466
- const disposePool = () => {
467
- void shutdownWithDelay();
468
- };
469
- const indexedFunctions = listOfFunctions.map((fn, index) => ({
470
- name: fn.name,
471
- index,
472
- timeout: fn.timeout,
473
- abortSignal: fn.abortSignal,
474
- imported: fn.imported === true,
475
- }));
476
- const callHandlers = new Map();
477
- for (const { name } of indexedFunctions) {
478
- callHandlers.set(name, []);
479
- }
480
- for (const worker of workers) {
481
- for (const { name, index, timeout, abortSignal } of indexedFunctions) {
482
- callHandlers.get(name).push(wrapGuardedInvoke({
483
- taskName: name,
484
- invoke: worker.call({
485
- fnNumber: index,
486
- timeout,
487
- abortSignal,
488
- }),
489
- }));
490
- }
491
- }
492
- const useDirectHandler = (threads ?? 1) === 1 && !usingInliner;
493
- // Imported tasks must never execute on the host inliner lane: their module
494
- // import is meant to happen inside the worker so worker permission policies
495
- // apply. When the inliner is active we strip the inline lane from their
496
- // handler set so they only ever reach real worker lanes.
497
- const buildImportedInvoker = (handlers) => {
498
- const workerHandlers = [];
499
- const workerContexts = [];
500
- for (let lane = 0; lane < handlers.length; lane += 1) {
501
- if (lane === inlinerIndex)
502
- continue;
503
- workerHandlers.push(handlers[lane]);
504
- workerContexts.push(workers[lane]);
505
- }
506
- if (workerHandlers.length === 0) {
507
- throw new Error("Imported task has no worker lane to run on: the pool only has the " +
508
- "host inliner. Imported tasks are never inlined on the host; add at " +
509
- "least one worker thread.");
510
- }
511
- if (workerHandlers.length === 1)
512
- return workerHandlers[0];
513
- return managerMethod({
514
- contexts: workerContexts,
515
- balancer,
516
- handlers: workerHandlers,
517
- // No inlinerGate: the inline lane is intentionally excluded here.
518
- });
519
- };
520
- const buildInvoker = (handlers, imported) => {
521
- if (imported && usingInliner) {
522
- return buildImportedInvoker(handlers);
523
- }
524
- return useDirectHandler ? handlers[0] : managerMethod({
525
- contexts: workers,
526
- balancer,
527
- handlers,
528
- inlinerGate: usingInliner
529
- ? {
530
- index: inlinerIndex,
531
- threshold: inlinerDispatchThreshold,
532
- }
533
- : undefined,
534
- });
535
- };
536
- let callEntries;
537
- try {
538
- callEntries = indexedFunctions.map(({ name, imported }) => [name, buildInvoker(callHandlers.get(name), imported)]);
539
- }
540
- catch (error) {
541
- // Building invokers can throw (e.g. an imported task with no worker lane,
542
- // or a balancer that needs >=2 lanes). The inline executor and any spawned
543
- // workers are already live here, so tear them down before propagating —
544
- // otherwise their MessageChannel ports / worker handles keep the event
545
- // loop alive and hang the process.
546
- void closePoolNow();
547
- throw error;
548
- }
549
- return {
550
- shutdown: shutdownWithDelay,
551
- [Symbol.dispose]: disposePool,
552
- call: Object.fromEntries(callEntries),
553
- };
554
- };
555
- const SINGLE_TASK_KEY = "__task__";
556
- const createSingleTaskPool = (single, options) => {
557
- const pool = createPool(options ?? {})({
558
- [SINGLE_TASK_KEY]: single,
559
- });
560
- return {
561
- call: pool.call[SINGLE_TASK_KEY],
562
- shutdown: pool.shutdown,
563
- [Symbol.dispose]: pool[Symbol.dispose],
564
- };
565
- };
566
- const buildTaskDefinitionFromCaller = (input, callerHref, at, imported = false) => {
567
- const importedFrom = new URL(callerHref).href;
568
- const out = ({
569
- ...input,
570
- id: genTaskID(),
571
- importedFrom,
572
- at,
573
- imported,
574
- [endpointSymbol]: true,
575
- });
576
- out.createPool = (options) => {
577
- if (RUNTIME_IS_MAIN_THREAD === false) {
578
- return out;
579
- }
580
- return createSingleTaskPool(out, options);
581
- };
582
- return out;
583
- };
584
- const buildTaskDefinition = (input, callerOffset) => {
585
- const [href, at] = getCallerFilePath(callerOffset);
586
- return buildTaskDefinitionFromCaller(input, href, at);
587
- };
588
- const createImportedTaskFn = (href, exportName) => {
589
- let cachedFn;
590
- let cachedLoad;
591
- const loadFn = async () => {
592
- if (cachedFn)
593
- return cachedFn;
594
- if (!cachedLoad) {
595
- cachedLoad = import(__rewriteRelativeImportExtension(href)).then((module) => {
596
- const record = module;
597
- const selected = exportName === DEFAULT_IMPORT_EXPORT_NAME
598
- ? record.default
599
- : record[exportName];
600
- if (typeof selected !== "function") {
601
- const available = Object.keys(record).join(", ");
602
- throw new TypeError(`importTask expected export "${exportName}" from "${href}" to be a function.` +
603
- ` Available exports: ${available || "(none)"}`);
604
- }
605
- cachedFn = selected;
606
- return cachedFn;
607
- });
608
- }
609
- return cachedLoad;
610
- };
611
- return (async (...args) => {
612
- const fn = await loadFn();
613
- return fn(...args);
614
- });
615
- };
616
- export function task(I) {
617
- return buildTaskDefinition(I, 4);
618
- }
619
- export function importTask(options) {
620
- const [callerHref, at] = getCallerFilePath(3);
621
- const { href, name = DEFAULT_IMPORT_EXPORT_NAME, ...rest } = options;
622
- const resolvedHref = resolveImportHref(href, callerHref);
623
- return buildTaskDefinitionFromCaller({
624
- ...rest,
625
- f: createImportedTaskFn(resolvedHref, name),
626
- }, callerHref, at, true);
627
- }
@@ -1,17 +0,0 @@
1
- import type { BufferReference } from "../connections/buffer-reference.js";
2
- import type { ProcessSharedBuffer } from "../connections/process-shared-buffer.js";
3
- type EnvelopeHeaderPrimitive = string | number | boolean | null;
4
- type EnvelopeHeaderValue = EnvelopeHeaderPrimitive | EnvelopeHeaderValue[] | {
5
- [key: string]: EnvelopeHeaderValue;
6
- };
7
- export type EnvelopeHeader = EnvelopeHeaderValue;
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> {
11
- readonly header: H;
12
- readonly payload: B;
13
- constructor(header: H, payload: B);
14
- [Symbol.dispose](): void;
15
- [PayloadTransportFinalizer](): (() => void) | undefined;
16
- }
17
- export {};
@@ -1,22 +0,0 @@
1
- const PayloadTransportFinalizer = Symbol.for("knitting.payloadCodec.transportFinalizer");
2
- export class Envelope {
3
- header;
4
- payload;
5
- constructor(header, payload) {
6
- this.header = header;
7
- this.payload = payload;
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
- }
22
- }
@@ -1 +0,0 @@
1
- export declare const toModuleUrl: (specifier: string) => string;
@@ -1,24 +0,0 @@
1
- import { pathToFileURL } from "node:url";
2
- const WINDOWS_DRIVE_PATH = /^[A-Za-z]:[\\/]/;
3
- const WINDOWS_UNC_PATH = /^\\\\[^\\/?]+\\[^\\/?]+/;
4
- const encodeFilePath = (path) => encodeURI(path)
5
- .replace(/\?/g, "%3F")
6
- .replace(/#/g, "%23");
7
- export const toModuleUrl = (specifier) => {
8
- if (WINDOWS_DRIVE_PATH.test(specifier)) {
9
- const normalized = specifier.replace(/\\/g, "/");
10
- return `file:///${encodeFilePath(normalized)}`;
11
- }
12
- if (WINDOWS_UNC_PATH.test(specifier)) {
13
- const normalized = specifier
14
- .replace(/^\\\\+/, "")
15
- .replace(/\\/g, "/");
16
- return `file://${encodeFilePath(normalized)}`;
17
- }
18
- try {
19
- return new URL(specifier).href;
20
- }
21
- catch {
22
- return pathToFileURL(specifier).href;
23
- }
24
- };