knitting 0.1.51 → 0.1.53
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.
- package/README.md +213 -54
- package/knitting.d.ts +1 -0
- package/map.md +15 -3
- package/package.json +14 -2
- package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
- package/prebuilds/win32-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
- package/scripts/build-native-addons.ts +5 -0
- package/src/api.d.ts +29 -16
- package/src/api.js +98 -28
- package/src/common/envelope.d.ts +9 -3
- package/src/common/envelope.js +14 -0
- package/src/common/worker-runtime.d.ts +2 -0
- package/src/common/worker-runtime.js +9 -0
- package/src/connections/buffer-reference-native.d.ts +56 -0
- package/src/connections/buffer-reference-native.js +217 -0
- package/src/connections/buffer-reference.d.ts +78 -0
- package/src/connections/buffer-reference.js +461 -0
- package/src/connections/index.d.ts +1 -0
- package/src/connections/index.js +1 -0
- package/src/connections/node-addons.d.ts +1 -1
- package/src/connections/node-buffer-pointer.d.ts +20 -0
- package/src/connections/node-buffer-pointer.js +16 -0
- package/src/connections/process-shared-buffer.d.ts +6 -0
- package/src/connections/process-shared-buffer.js +6 -0
- package/src/connections/shared-array-buffer-payload.d.ts +36 -0
- package/src/connections/shared-array-buffer-payload.js +235 -0
- package/src/debug/env-diff.d.ts +26 -0
- package/src/debug/env-diff.js +49 -0
- package/src/debug/gate.d.ts +18 -0
- package/src/debug/gate.js +69 -0
- package/src/debug/handle.d.ts +23 -0
- package/src/debug/handle.js +48 -0
- package/src/ipc/transport/shared-memory.d.ts +1 -3
- package/src/knitting_buffer_pointer.cc +425 -0
- package/src/memory/lock.d.ts +12 -1
- package/src/memory/lock.js +47 -4
- package/src/memory/payload-config.d.ts +9 -0
- package/src/memory/payloadCodec.js +220 -37
- package/src/permission/protocol.d.ts +1 -1
- package/src/permission/protocol.js +30 -20
- package/src/runtime/pool.d.ts +3 -5
- package/src/runtime/pool.js +18 -18
- package/src/runtime/process-worker.js +5 -2
- package/src/runtime/tx-queue.d.ts +3 -2
- package/src/runtime/tx-queue.js +18 -13
- package/src/types.d.ts +54 -50
- package/src/utils/http.d.ts +29 -0
- package/src/utils/http.js +100 -0
- package/src/worker/loop.js +76 -14
- package/src/worker/rx-queue.d.ts +4 -1
- package/src/worker/rx-queue.js +53 -4
- package/src/worker/safety/startup.d.ts +2 -3
- package/src/worker/safety/startup.js +1 -4
- package/src/worker/timers.js +7 -2
- package/unsafe.d.ts +1 -0
- package/unsafe.js +1 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Lock2, type Task } from "../memory/lock.js";
|
|
2
2
|
import type { AbortSignalOption, TaskTimeout } from "../types.js";
|
|
3
3
|
import { type SignalAbortStore } from "../shared/abortSignal.js";
|
|
4
4
|
type RawArguments = unknown;
|
|
@@ -9,10 +9,11 @@ type CreateHostTxQueueArgs = {
|
|
|
9
9
|
max?: number;
|
|
10
10
|
lock: Lock2;
|
|
11
11
|
returnLock: Lock2;
|
|
12
|
+
releaseBufferReferenceReturn?: (token: bigint) => void;
|
|
12
13
|
abortSignals?: Pick<SignalAbortStore, "getSignal" | "setSignal" | "resetSignal" | "closeNow">;
|
|
13
14
|
now?: () => number;
|
|
14
15
|
};
|
|
15
|
-
export declare function createHostTxQueue({ max, lock, returnLock, abortSignals, now, }: CreateHostTxQueueArgs): {
|
|
16
|
+
export declare function createHostTxQueue({ max, lock, returnLock, releaseBufferReferenceReturn, abortSignals, now, }: CreateHostTxQueueArgs): {
|
|
16
17
|
rejectAll: (reason: string) => void;
|
|
17
18
|
hasPendingFrames: () => boolean;
|
|
18
19
|
txIdle: () => boolean;
|
package/src/runtime/tx-queue.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { makeTask, resetTaskLocalFlags, TaskIndex, } from "../memory/lock.js";
|
|
1
|
+
import { makeTask, resetTaskLocalFlags, runTaskFinalizers, TaskIndex, } from "../memory/lock.js";
|
|
2
2
|
import { withResolvers } from "../common/with-resolvers.js";
|
|
3
3
|
import { AbortSignalPoolExhausted, OneShotDeferred, } from "../shared/abortSignal.js";
|
|
4
|
+
import { withBufferReferenceReturnReleaser } from "../connections/buffer-reference.js";
|
|
4
5
|
const SLOT_INDEX_MASK = 31;
|
|
5
6
|
const SLOT_META_MASK = 0x07ffffff;
|
|
6
7
|
const SLOT_META_SHIFT = 5;
|
|
@@ -10,7 +11,7 @@ const FUNCTION_META_SHIFT = 16;
|
|
|
10
11
|
const ABORT_SIGNAL_META_OFFSET = 1;
|
|
11
12
|
const NO_ABORT_SIGNAL = -1;
|
|
12
13
|
const p_now = performance.now.bind(performance);
|
|
13
|
-
export function createHostTxQueue({ max, lock, returnLock, abortSignals, now, }) {
|
|
14
|
+
export function createHostTxQueue({ max, lock, returnLock, releaseBufferReferenceReturn, abortSignals, now, }) {
|
|
14
15
|
const PLACE_HOLDER = (_) => {
|
|
15
16
|
throw ("UNREACHABLE FROM PLACE HOLDER (main)");
|
|
16
17
|
};
|
|
@@ -26,7 +27,6 @@ export function createHostTxQueue({ max, lock, returnLock, abortSignals, now, })
|
|
|
26
27
|
const initialSize = max ?? 10;
|
|
27
28
|
const queue = Array.from({ length: initialSize }, (_, index) => newSlot(index));
|
|
28
29
|
const freeSockets = Array.from({ length: initialSize }, (_, i) => i);
|
|
29
|
-
// Local count
|
|
30
30
|
const freePush = (id) => freeSockets.push(id);
|
|
31
31
|
const freePop = () => freeSockets.pop();
|
|
32
32
|
const queuePush = (task) => queue.push(task);
|
|
@@ -40,13 +40,16 @@ export function createHostTxQueue({ max, lock, returnLock, abortSignals, now, })
|
|
|
40
40
|
onResolved: (task) => {
|
|
41
41
|
inUsed = (inUsed - 1) | 0;
|
|
42
42
|
resetTaskLocalFlags(task);
|
|
43
|
+
runTaskFinalizers(task);
|
|
43
44
|
task.value = null;
|
|
44
45
|
task.resolve = PLACE_HOLDER;
|
|
45
46
|
task.reject = PLACE_HOLDER;
|
|
46
47
|
freePush(task[TaskIndex.ID]);
|
|
47
48
|
},
|
|
48
49
|
});
|
|
49
|
-
|
|
50
|
+
const completeFrame = releaseBufferReferenceReturn === undefined
|
|
51
|
+
? resolveReturn
|
|
52
|
+
: () => withBufferReferenceReturnReleaser(releaseBufferReferenceReturn, resolveReturn);
|
|
50
53
|
const txIdle = () => getPendingFrameCount() === 0 && inUsed === getPendingPromiseCount();
|
|
51
54
|
const rejectAll = (reason) => {
|
|
52
55
|
for (let index = 0; index < queue.length; index++) {
|
|
@@ -57,6 +60,7 @@ export function createHostTxQueue({ max, lock, returnLock, abortSignals, now, })
|
|
|
57
60
|
}
|
|
58
61
|
catch {
|
|
59
62
|
}
|
|
63
|
+
runTaskFinalizers(slot);
|
|
60
64
|
slot.resolve = PLACE_HOLDER;
|
|
61
65
|
slot.reject = PLACE_HOLDER;
|
|
62
66
|
queue[index] = newSlot(index);
|
|
@@ -73,13 +77,13 @@ export function createHostTxQueue({ max, lock, returnLock, abortSignals, now, })
|
|
|
73
77
|
rejectAll,
|
|
74
78
|
hasPendingFrames,
|
|
75
79
|
txIdle,
|
|
76
|
-
completeFrame
|
|
80
|
+
completeFrame,
|
|
77
81
|
enqueue: (functionID, timeout, abortSignal) => {
|
|
78
82
|
const HAS_TIMER = timeout !== undefined;
|
|
79
83
|
const functionIDMasked = functionID & FUNCTION_ID_MASK;
|
|
80
|
-
const USE_SIGNAL = abortSignal !== undefined &&
|
|
84
|
+
const USE_SIGNAL = abortSignal !== undefined &&
|
|
85
|
+
abortSignals !== undefined;
|
|
81
86
|
return (rawArgs) => {
|
|
82
|
-
// Expanding size if needed
|
|
83
87
|
if (inUsed === queue.length) {
|
|
84
88
|
const newSize = inUsed + 32;
|
|
85
89
|
let current = queue.length;
|
|
@@ -101,19 +105,19 @@ export function createHostTxQueue({ max, lock, returnLock, abortSignals, now, })
|
|
|
101
105
|
new OneShotDeferred(deferred, () => resetSignal(maybeSignal), () => {
|
|
102
106
|
abortSignals.setSignal(maybeSignal);
|
|
103
107
|
});
|
|
104
|
-
const encodedSignalMeta = ((maybeSignal + ABORT_SIGNAL_META_OFFSET) & FUNCTION_META_MASK) >>>
|
|
108
|
+
const encodedSignalMeta = ((maybeSignal + ABORT_SIGNAL_META_OFFSET) & FUNCTION_META_MASK) >>>
|
|
109
|
+
0;
|
|
105
110
|
slot[TaskIndex.FunctionID] =
|
|
106
|
-
((encodedSignalMeta << FUNCTION_META_SHIFT) | functionIDMasked) >>>
|
|
111
|
+
((encodedSignalMeta << FUNCTION_META_SHIFT) | functionIDMasked) >>>
|
|
112
|
+
0;
|
|
107
113
|
}
|
|
108
|
-
// Set info
|
|
109
114
|
slot.value = rawArgs;
|
|
110
115
|
slot[TaskIndex.ID] = index;
|
|
111
116
|
slot.resolve = deferred.resolve;
|
|
112
117
|
slot.reject = deferred.reject;
|
|
113
118
|
if (HAS_TIMER) {
|
|
114
|
-
slot[TaskIndex.slotBuffer] =
|
|
115
|
-
((
|
|
116
|
-
((((nowTime() >>> 0) & SLOT_META_MASK) << SLOT_META_SHIFT) >>> 0)) >>> 0;
|
|
119
|
+
slot[TaskIndex.slotBuffer] = ((slot[TaskIndex.slotBuffer] & SLOT_INDEX_MASK) |
|
|
120
|
+
((((nowTime() >>> 0) & SLOT_META_MASK) << SLOT_META_SHIFT) >>> 0)) >>> 0;
|
|
117
121
|
}
|
|
118
122
|
void publish(slot);
|
|
119
123
|
inUsed = (inUsed + 1) | 0;
|
|
@@ -132,6 +136,7 @@ export function createHostTxQueue({ max, lock, returnLock, abortSignals, now, })
|
|
|
132
136
|
catch {
|
|
133
137
|
}
|
|
134
138
|
resetTaskLocalFlags(task);
|
|
139
|
+
runTaskFinalizers(task);
|
|
135
140
|
task.value = null;
|
|
136
141
|
task.resolve = PLACE_HOLDER;
|
|
137
142
|
task.reject = PLACE_HOLDER;
|
package/src/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { endpointSymbol } from "./common/task-symbol.js";
|
|
2
|
-
import type { Envelope } from "./common/envelope.js";
|
|
2
|
+
import type { Envelope, EnvelopeBody, EnvelopeHeader } from "./common/envelope.js";
|
|
3
3
|
import type { LockBufferTextCompat, SharedBufferTextCompat } from "./common/shared-buffer-text.js";
|
|
4
4
|
import type { SharedBufferRegion, SharedBufferSource } from "./common/shared-buffer-region.js";
|
|
5
5
|
import type { PayloadBufferMode, PayloadBufferOptions } from "./memory/payload-config.js";
|
|
@@ -32,8 +32,18 @@ type WorkerData = {
|
|
|
32
32
|
lock: LockBuffers;
|
|
33
33
|
returnLock: LockBuffers;
|
|
34
34
|
payloadConfig?: PayloadBufferOptions;
|
|
35
|
+
bufferReferenceReturn?: "copy" | "borrow";
|
|
35
36
|
permission?: ResolvedPermissionProtocol;
|
|
36
37
|
};
|
|
38
|
+
type UnsafeOptions = {
|
|
39
|
+
/**
|
|
40
|
+
* Experimental `BufferReference` return lifetime.
|
|
41
|
+
*
|
|
42
|
+
* `"copy"` is safe after worker release. `"borrow"` skips the Deno/Bun copy,
|
|
43
|
+
* but must be released before producer shutdown and must not outlive its ref.
|
|
44
|
+
*/
|
|
45
|
+
BufferReferenceReturn?: "copy" | "borrow";
|
|
46
|
+
};
|
|
37
47
|
type LockBuffers = {
|
|
38
48
|
headers: SharedBufferSource;
|
|
39
49
|
headerSlotStrideU32?: number;
|
|
@@ -49,11 +59,14 @@ interface JSONObject {
|
|
|
49
59
|
interface JSONArray extends Array<JSONValue> {
|
|
50
60
|
}
|
|
51
61
|
type Serializable = string | object | number | boolean | bigint;
|
|
52
|
-
type ValidInput = bigint | void | JSONValue | symbol | ArrayBuffer | Uint8Array | Int32Array | Float64Array | BigInt64Array | BigUint64Array | DataView | Error | Date | Envelope
|
|
62
|
+
type ValidInput = bigint | void | JSONValue | symbol | ArrayBuffer | Uint8Array | Int32Array | Float64Array | BigInt64Array | BigUint64Array | DataView | Error | Date | Envelope<EnvelopeHeader, EnvelopeBody>;
|
|
53
63
|
type Args = ValidInput | Serializable;
|
|
54
64
|
type MaybePromise<T> = T | Promise<T>;
|
|
65
|
+
/** Blob payloads are not supported; pass ArrayBuffer/typed arrays instead. */
|
|
55
66
|
type NoBlob<T> = T extends Blob ? never : T;
|
|
67
|
+
/** Task input may be a direct value or native Promise; thenables are plain values. */
|
|
56
68
|
type TaskInput = NoBlob<Args> | Promise<NoBlob<Args>>;
|
|
69
|
+
/** Per-call timeout. A number is milliseconds; object form can return a default. */
|
|
57
70
|
type TaskTimeout = number | {
|
|
58
71
|
time: number;
|
|
59
72
|
maybe?: true;
|
|
@@ -66,6 +79,7 @@ type BivariantCallback<Args extends unknown[], R> = {
|
|
|
66
79
|
type AbortSignalConfig = {
|
|
67
80
|
readonly hasAborted: true;
|
|
68
81
|
};
|
|
82
|
+
/** `true` or config injects an abort toolkit as the task's second parameter. */
|
|
69
83
|
type AbortSignalOption = true | AbortSignalConfig | undefined;
|
|
70
84
|
type AbortSignalMethods<AS extends AbortSignalOption> = AS extends undefined ? never : {
|
|
71
85
|
hasAborted: () => boolean;
|
|
@@ -88,13 +102,16 @@ type tasks = Record<string, Composed<any, any, AbortSignalOption> | TaskFunction
|
|
|
88
102
|
type ComposedWithKey = Composed<any, any, AbortSignalOption> & {
|
|
89
103
|
name: string;
|
|
90
104
|
};
|
|
91
|
-
type PromiseWrapped<F extends (...args: any[]) => any, AS extends AbortSignalOption = undefined> = (...args: PromisifyCallArgs<F, AS>) =>
|
|
105
|
+
type PromiseWrapped<F extends (...args: any[]) => any, AS extends AbortSignalOption = undefined> = (...args: PromisifyCallArgs<F, AS>) => AS extends undefined ? Promise<Awaited<ReturnType<F>>> : PromiseWithMaybeReject<Awaited<ReturnType<F>>>;
|
|
92
106
|
type PromiseInput<T> = T | Promise<T>;
|
|
93
107
|
type PromisifyArgs<T extends unknown[]> = {
|
|
94
108
|
[K in keyof T]: PromiseInput<T[K]>;
|
|
95
109
|
};
|
|
96
110
|
type NormalizeUndefinedSingleArg<T extends unknown[]> = T extends [undefined] ? [] | [undefined] : T;
|
|
97
|
-
type AbortAwareCallArgs<T extends unknown[]> = T extends [
|
|
111
|
+
type AbortAwareCallArgs<T extends unknown[]> = T extends [
|
|
112
|
+
...infer Head,
|
|
113
|
+
AbortSignalToolkit<any>
|
|
114
|
+
] ? NormalizeUndefinedSingleArg<Head> : NormalizeUndefinedSingleArg<T>;
|
|
98
115
|
type HostCallArgs<F extends (...args: any[]) => any, AS extends AbortSignalOption> = AS extends undefined ? Parameters<F> : AbortAwareCallArgs<Parameters<F>>;
|
|
99
116
|
type PromisifyCallArgs<F extends (...args: any[]) => any, AS extends AbortSignalOption> = HostCallArgs<F, AS> extends infer T ? T extends unknown[] ? PromisifyArgs<T> : never : never;
|
|
100
117
|
type TaskCallable<T> = T extends TaskLike<any> ? T["f"] : T extends TaskFunctionLike ? T : never;
|
|
@@ -105,7 +122,9 @@ type FunctionMapType<T extends Record<string, TaskLike<any> | TaskFunctionLike>>
|
|
|
105
122
|
[K in keyof T]: PromiseWrapped<TaskCallable<T[K]>, AbortSignalOfTask<T[K]>>;
|
|
106
123
|
};
|
|
107
124
|
interface FixPointBase<A extends TaskInput, B extends Args, AS extends AbortSignalOption = undefined> {
|
|
125
|
+
/** Worker function. It receives one value; use a tuple/object for many inputs. */
|
|
108
126
|
readonly f: TaskFn<A, B, AS>;
|
|
127
|
+
/** Soft call timeout. Use `worker.hardTimeoutMs` for runaway CPU walls. */
|
|
109
128
|
readonly timeout?: TaskTimeout;
|
|
110
129
|
}
|
|
111
130
|
type FixPoint<A extends TaskInput, B extends Args, AS extends AbortSignalOption = undefined> = FixPointBase<A, B, AS> & (AS extends undefined ? {
|
|
@@ -114,37 +133,39 @@ type FixPoint<A extends TaskInput, B extends Args, AS extends AbortSignalOption
|
|
|
114
133
|
readonly abortSignal: AS;
|
|
115
134
|
});
|
|
116
135
|
type ImportTaskOptions<A extends TaskInput = void, B extends Args = void, AS extends AbortSignalOption = undefined> = Omit<FixPoint<A, B, AS>, "f"> & {
|
|
136
|
+
/** Module imported by the worker only. Relative paths resolve from caller. */
|
|
117
137
|
readonly href: string;
|
|
138
|
+
/** Plain function export name. Defaults to `"default"`; do not target `task()`. */
|
|
118
139
|
readonly name?: string;
|
|
119
140
|
};
|
|
120
141
|
type SecondPart = {
|
|
121
142
|
readonly [endpointSymbol]: true;
|
|
122
143
|
readonly id: number;
|
|
123
|
-
/**
|
|
124
|
-
* IMPORTANT: `at` helps to create a `createPool` because we dont know
|
|
125
|
-
* the name of the variable at runtime, so basically this gets the logical order
|
|
126
|
-
* of the exported file, so no matter the name the worker can track which ` task `
|
|
127
|
-
* to track
|
|
128
|
-
*/
|
|
144
|
+
/** Logical export order used to match worker tasks before names are known. */
|
|
129
145
|
readonly at: number;
|
|
130
146
|
readonly importedFrom: string;
|
|
131
147
|
/**
|
|
132
|
-
*
|
|
133
|
-
* `importTask`). Such tasks must never run on the host inliner lane: their
|
|
134
|
-
* module import is meant to happen inside the worker so worker permission
|
|
135
|
-
* policies apply. The pool routes them to worker lanes only, even when the
|
|
136
|
-
* inliner is enabled for other tasks.
|
|
148
|
+
* Imported tasks stay on worker lanes so imports run under worker permissions.
|
|
137
149
|
*/
|
|
138
150
|
readonly imported?: boolean;
|
|
139
151
|
};
|
|
140
152
|
type SingleTaskPool<A extends TaskInput = Args, B extends Args = Args, AS extends AbortSignalOption = undefined> = {
|
|
153
|
+
/** Invoke the single task. Arguments may be native Promises. */
|
|
141
154
|
call: PromiseWrapped<TaskFn<A, B, AS>, AS>;
|
|
155
|
+
/** Await worker teardown now. `using` disposes at scope exit without awaiting. */
|
|
142
156
|
shutdown: (delayMs?: number) => Promise<void>;
|
|
157
|
+
/** Starts shutdown at scope exit. Use `shutdown()` when you must await it. */
|
|
143
158
|
[Symbol.dispose]: () => void;
|
|
144
159
|
};
|
|
145
160
|
type Pool<T extends Record<string, TaskLike<any> | TaskFunctionLike>> = {
|
|
161
|
+
/** Await worker teardown now. `using` disposes at scope exit without awaiting. */
|
|
146
162
|
shutdown: (delayMs?: number) => Promise<void>;
|
|
163
|
+
/** Starts shutdown at scope exit. Use `shutdown()` when you must await it. */
|
|
147
164
|
[Symbol.dispose]: () => void;
|
|
165
|
+
/**
|
|
166
|
+
* Typed task callers. Each call accepts the task input or a native Promise.
|
|
167
|
+
* Thrown errors/rejections reject here as Error objects with cause chains.
|
|
168
|
+
*/
|
|
148
169
|
call: FunctionMapType<T>;
|
|
149
170
|
};
|
|
150
171
|
type ReturnFixed<A extends TaskInput = undefined, B extends Args = undefined, AS extends AbortSignalOption = undefined> = FixPoint<A, B, AS> & SecondPart & {
|
|
@@ -171,12 +192,13 @@ type Balancer = BalancerStrategy | {
|
|
|
171
192
|
*/
|
|
172
193
|
strategy?: BalancerStrategy;
|
|
173
194
|
};
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
logImportedUrl?: boolean;
|
|
195
|
+
/** Debug namespaces for host setup, worker state, imports, globals, and lifecycle. */
|
|
196
|
+
type DebugNamespace = "host" | "globals" | "signals" | "imports" | "lifecycle";
|
|
197
|
+
type DebugFlags = {
|
|
198
|
+
[Namespace in DebugNamespace]?: boolean;
|
|
179
199
|
};
|
|
200
|
+
/** Pass `true` for all debug, or enable namespaces by name. */
|
|
201
|
+
type DebugOptions = boolean | DebugFlags;
|
|
180
202
|
type WorkerBootstrapContext = {
|
|
181
203
|
readonly thread: number;
|
|
182
204
|
readonly totalNumberOfThread: number;
|
|
@@ -208,7 +230,7 @@ type WorkerSettings = {
|
|
|
208
230
|
/**
|
|
209
231
|
* Experimental worker runtime.
|
|
210
232
|
* "thread" uses Worker/worker_threads. "process" spawns another JavaScript
|
|
211
|
-
* runtime
|
|
233
|
+
* runtime, useful for process isolation, bwrap, and containers.
|
|
212
234
|
*/
|
|
213
235
|
runtime?: "thread" | "process";
|
|
214
236
|
/**
|
|
@@ -276,38 +298,21 @@ type DispatcherSettings = {
|
|
|
276
298
|
maxBackoffMs?: number;
|
|
277
299
|
};
|
|
278
300
|
type CreatePool = {
|
|
301
|
+
/** Number of workers. Default: 1. */
|
|
279
302
|
threads?: number;
|
|
280
|
-
/**
|
|
281
|
-
* @deprecated Too risky with processes, need to rewrite or delete.
|
|
282
|
-
*/
|
|
303
|
+
/** Add a host inline lane for regular tasks. Imported tasks still use workers. */
|
|
283
304
|
inliner?: Inliner;
|
|
284
305
|
balancer?: Balancer;
|
|
285
306
|
worker?: WorkerSettings;
|
|
286
307
|
/**
|
|
287
|
-
* Payload transport settings.
|
|
308
|
+
* Payload transport settings. Default dynamic payload cap is about 8 MiB
|
|
309
|
+
* (`payloadMaxByteLength >> 3`, with a 64 MiB growth cap).
|
|
288
310
|
*/
|
|
289
311
|
payload?: PayloadBufferOptions;
|
|
290
312
|
/**
|
|
291
|
-
*
|
|
292
|
-
* Defaults to 4 MiB when growable SAB is available, otherwise defaults to
|
|
293
|
-
* `payloadMaxBytes`.
|
|
294
|
-
* @deprecated Use `payload.payloadInitialBytes`.
|
|
313
|
+
* Experimental unsafe options.
|
|
295
314
|
*/
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Maximum payload SharedArrayBuffer size (bytes) per worker direction.
|
|
299
|
-
* Defaults to 64 MiB.
|
|
300
|
-
* @deprecated Use `payload.payloadMaxByteLength`.
|
|
301
|
-
*/
|
|
302
|
-
payloadMaxBytes?: number;
|
|
303
|
-
/**
|
|
304
|
-
* @deprecated Use `payload.mode`.
|
|
305
|
-
*/
|
|
306
|
-
bufferMode?: PayloadBufferMode;
|
|
307
|
-
/**
|
|
308
|
-
* @deprecated Use `payload.maxPayloadBytes`.
|
|
309
|
-
*/
|
|
310
|
-
maxPayloadBytes?: number;
|
|
315
|
+
unsafe?: UnsafeOptions;
|
|
311
316
|
/**
|
|
312
317
|
* Abort-aware signal pool capacity.
|
|
313
318
|
* Defaults to `258`.
|
|
@@ -324,19 +329,18 @@ type CreatePool = {
|
|
|
324
329
|
workerExecArgv?: string[];
|
|
325
330
|
/**
|
|
326
331
|
* Runtime permission protocol.
|
|
327
|
-
* Omit to use strict defaults with `allowImport: true
|
|
332
|
+
* Omit to use strict defaults with `allowImport: true`; worker console is
|
|
333
|
+
* quiet unless `permission: { console: true }`.
|
|
334
|
+
*
|
|
335
|
+
* Task code cannot terminate the host: process/Deno exit APIs are blocked.
|
|
328
336
|
* Use `"strict"` (default for object mode) or `"unsafe"`.
|
|
329
337
|
* Accepts object form for fine-grained permission controls.
|
|
330
338
|
*/
|
|
331
339
|
permission?: PermissionProtocolInput;
|
|
332
|
-
/**
|
|
333
|
-
* @deprecated Use `host` instead.
|
|
334
|
-
*/
|
|
335
|
-
dispatcher?: DispatcherSettings;
|
|
336
340
|
debug?: DebugOptions;
|
|
337
341
|
source?: string;
|
|
338
342
|
};
|
|
339
|
-
export type {
|
|
343
|
+
export type { AbortSignalConfig as AbortSignalConfig, AbortSignalMethods as AbortSignalMethods, AbortSignalOption as AbortSignalOption, AbortSignalToolkit as AbortSignalToolkit, Args as Args, Balancer as Balancer, BalancerStrategy as BalancerStrategy, Composed as Composed, ComposedWithKey as ComposedWithKey, CreateContext as CreateContext, CreatePool as CreatePool, DebugNamespace as DebugNamespace, DebugOptions as DebugOptions, DispatcherSettings as DispatcherSettings, Envelope as Envelope, EnvelopeBody as EnvelopeBody, EnvelopeHeader as EnvelopeHeader, External as External, FixPoint as FixPoint, FunctionMapType as FunctionMapType, ImportTaskOptions as ImportTaskOptions, Inliner as Inliner, LockBuffers as LockBuffers, LockBufferTextCompat as LockBufferTextCompat, MaybePromise as MaybePromise, PayloadBufferMode as PayloadBufferMode, PayloadBufferOptions as PayloadBufferOptions, PermissionProtocol as PermissionProtocol, PermissionProtocolInput as PermissionProtocolInput, Pool as Pool, ProcessSharedMemoryMode as ProcessSharedMemoryMode, ProcessSharedMemorySettings as ProcessSharedMemorySettings, ResolvedPermissionProtocol as ResolvedPermissionProtocol, ReturnFixed as ReturnFixed, SecondPart as SecondPart, SharedBufferRegion as SharedBufferRegion, SharedBufferSource as SharedBufferSource, SharedBufferTextCompat as SharedBufferTextCompat, SingleTaskPool as SingleTaskPool, TaskFn as TaskFn, TaskInput as TaskInput, tasks as tasks, TaskTimeout as TaskTimeout, ValidInput as ValidInput, WorkerBootstrapContext as WorkerBootstrapContext, WorkerBootstrapFunction as WorkerBootstrapFunction, WorkerBootstrapOptions as WorkerBootstrapOptions, WorkerCall as WorkerCall, WorkerContext as WorkerContext, WorkerData as WorkerData, WorkerInvoke as WorkerInvoke, WorkerSettings as WorkerSettings, WorkerTimers as WorkerTimers, };
|
|
340
344
|
export type { Task as Task } from "./memory/lock.js";
|
|
341
345
|
export { LockBound as LockBound, PayloadBuffer as PayloadBuffer, PayloadSignal as PayloadSignal, TaskIndex as TaskIndex, } from "./memory/lock.js";
|
|
342
346
|
export type { RegisterMalloc as RegisterMalloc } from "./memory/regionRegistry.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type BufferLike = ArrayBuffer | SharedArrayBuffer | ArrayBufferView;
|
|
2
|
+
export type NumberFormat = "f64" | "f32" | "i32";
|
|
3
|
+
export type NumberBufferOptions = {
|
|
4
|
+
format?: NumberFormat;
|
|
5
|
+
};
|
|
6
|
+
/** View ArrayBuffer/SharedArrayBuffer/typed-array bytes as Uint8Array. */
|
|
7
|
+
export declare const bufferToBytes: (source: BufferLike) => Uint8Array;
|
|
8
|
+
/** Copy bytes into a SharedArrayBuffer for zero-copy worker inputs. */
|
|
9
|
+
export declare const bytesToBuffer: (source: BufferLike) => SharedArrayBuffer;
|
|
10
|
+
/** Decode UTF-8 bytes from ArrayBuffer/SharedArrayBuffer/typed-array input. */
|
|
11
|
+
export declare const bufferToString: (source: BufferLike) => string;
|
|
12
|
+
/** Encode UTF-8 text into a SharedArrayBuffer. */
|
|
13
|
+
export declare const stringToBuffer: (text: string) => SharedArrayBuffer;
|
|
14
|
+
/** Decode JSON from UTF-8 bytes. */
|
|
15
|
+
export declare const bufferToJson: <T = unknown>(source: BufferLike) => T;
|
|
16
|
+
/** Encode JSON into a SharedArrayBuffer. */
|
|
17
|
+
export declare const jsonToBuffer: (value: unknown) => SharedArrayBuffer;
|
|
18
|
+
/** Encode numbers into a SharedArrayBuffer (`f64` by default). */
|
|
19
|
+
export declare const numbersToBuffer: (numbers: ArrayLike<number>, options?: NumberBufferOptions) => SharedArrayBuffer;
|
|
20
|
+
/** Decode numeric typed-array views from worker-friendly byte buffers. */
|
|
21
|
+
export declare function bufferToNumbers(source: BufferLike, options?: {
|
|
22
|
+
format?: "f64";
|
|
23
|
+
}): Float64Array;
|
|
24
|
+
export declare function bufferToNumbers(source: BufferLike, options: {
|
|
25
|
+
format: "f32";
|
|
26
|
+
}): Float32Array;
|
|
27
|
+
export declare function bufferToNumbers(source: BufferLike, options: {
|
|
28
|
+
format: "i32";
|
|
29
|
+
}): Int32Array;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const nodeBuffer = globalThis.Buffer;
|
|
2
|
+
const hasNodeBuffer = typeof nodeBuffer?.from === "function" &&
|
|
3
|
+
typeof nodeBuffer?.byteLength === "function";
|
|
4
|
+
const textEncoder = new TextEncoder();
|
|
5
|
+
const textDecoder = new TextDecoder();
|
|
6
|
+
const hasSharedArrayBuffer = typeof SharedArrayBuffer === "function";
|
|
7
|
+
const isSharedArrayBuffer = (value) => hasSharedArrayBuffer && value instanceof SharedArrayBuffer;
|
|
8
|
+
const BYTES_PER_ELEMENT = {
|
|
9
|
+
f64: 8,
|
|
10
|
+
f32: 4,
|
|
11
|
+
i32: 4,
|
|
12
|
+
};
|
|
13
|
+
const toBytes = (source) => {
|
|
14
|
+
if (source instanceof Uint8Array)
|
|
15
|
+
return source;
|
|
16
|
+
if (ArrayBuffer.isView(source)) {
|
|
17
|
+
return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
|
18
|
+
}
|
|
19
|
+
if (source instanceof ArrayBuffer || isSharedArrayBuffer(source)) {
|
|
20
|
+
return new Uint8Array(source);
|
|
21
|
+
}
|
|
22
|
+
throw new TypeError("Expected an ArrayBuffer, SharedArrayBuffer, or typed-array view.");
|
|
23
|
+
};
|
|
24
|
+
/** View ArrayBuffer/SharedArrayBuffer/typed-array bytes as Uint8Array. */
|
|
25
|
+
export const bufferToBytes = (source) => toBytes(source);
|
|
26
|
+
/** Copy bytes into a SharedArrayBuffer for zero-copy worker inputs. */
|
|
27
|
+
export const bytesToBuffer = (source) => {
|
|
28
|
+
const bytes = toBytes(source);
|
|
29
|
+
const sab = new SharedArrayBuffer(bytes.byteLength);
|
|
30
|
+
new Uint8Array(sab).set(bytes);
|
|
31
|
+
return sab;
|
|
32
|
+
};
|
|
33
|
+
const makeNumberView = (format, buffer, byteOffset, length) => {
|
|
34
|
+
switch (format) {
|
|
35
|
+
case "f64":
|
|
36
|
+
return new Float64Array(buffer, byteOffset, length);
|
|
37
|
+
case "f32":
|
|
38
|
+
return new Float32Array(buffer, byteOffset, length);
|
|
39
|
+
case "i32":
|
|
40
|
+
return new Int32Array(buffer, byteOffset, length);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
/** Decode UTF-8 bytes from ArrayBuffer/SharedArrayBuffer/typed-array input. */
|
|
44
|
+
export const bufferToString = (source) => {
|
|
45
|
+
const bytes = toBytes(source);
|
|
46
|
+
if (hasNodeBuffer) {
|
|
47
|
+
return nodeBuffer
|
|
48
|
+
.from(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
49
|
+
.toString("utf8");
|
|
50
|
+
}
|
|
51
|
+
return textDecoder.decode(bytes);
|
|
52
|
+
};
|
|
53
|
+
/** Encode UTF-8 text into a SharedArrayBuffer. */
|
|
54
|
+
export const stringToBuffer = (text) => {
|
|
55
|
+
if (typeof text !== "string") {
|
|
56
|
+
throw new TypeError("stringToBuffer expects a string.");
|
|
57
|
+
}
|
|
58
|
+
if (hasNodeBuffer) {
|
|
59
|
+
const byteLength = nodeBuffer.byteLength(text, "utf8");
|
|
60
|
+
const sab = new SharedArrayBuffer(byteLength);
|
|
61
|
+
nodeBuffer.from(sab).write(text, "utf8");
|
|
62
|
+
return sab;
|
|
63
|
+
}
|
|
64
|
+
const encoded = textEncoder.encode(text);
|
|
65
|
+
const sab = new SharedArrayBuffer(encoded.byteLength);
|
|
66
|
+
new Uint8Array(sab).set(encoded);
|
|
67
|
+
return sab;
|
|
68
|
+
};
|
|
69
|
+
/** Decode JSON from UTF-8 bytes. */
|
|
70
|
+
export const bufferToJson = (source) => JSON.parse(bufferToString(source));
|
|
71
|
+
/** Encode JSON into a SharedArrayBuffer. */
|
|
72
|
+
export const jsonToBuffer = (value) => {
|
|
73
|
+
const text = JSON.stringify(value);
|
|
74
|
+
if (typeof text !== "string") {
|
|
75
|
+
throw new TypeError("jsonToBuffer: value is not JSON-serializable (stringify returned undefined).");
|
|
76
|
+
}
|
|
77
|
+
return stringToBuffer(text);
|
|
78
|
+
};
|
|
79
|
+
/** Encode numbers into a SharedArrayBuffer (`f64` by default). */
|
|
80
|
+
export const numbersToBuffer = (numbers, options) => {
|
|
81
|
+
const format = options?.format ?? "f64";
|
|
82
|
+
const sab = new SharedArrayBuffer(numbers.length * BYTES_PER_ELEMENT[format]);
|
|
83
|
+
makeNumberView(format, sab, 0, numbers.length).set(numbers);
|
|
84
|
+
return sab;
|
|
85
|
+
};
|
|
86
|
+
export function bufferToNumbers(source, options) {
|
|
87
|
+
const format = options?.format ?? "f64";
|
|
88
|
+
const bytesPer = BYTES_PER_ELEMENT[format];
|
|
89
|
+
const bytes = toBytes(source);
|
|
90
|
+
if (bytes.byteLength % bytesPer !== 0) {
|
|
91
|
+
throw new RangeError(`bufferToNumbers: byte length ${bytes.byteLength} is not a multiple of ` +
|
|
92
|
+
`${bytesPer} for format "${format}".`);
|
|
93
|
+
}
|
|
94
|
+
const length = bytes.byteLength / bytesPer;
|
|
95
|
+
if (bytes.byteOffset % bytesPer === 0) {
|
|
96
|
+
return makeNumberView(format, bytes.buffer, bytes.byteOffset, length);
|
|
97
|
+
}
|
|
98
|
+
const copy = bytes.slice();
|
|
99
|
+
return makeNumberView(format, copy.buffer, 0, length);
|
|
100
|
+
}
|