knitting 0.1.55 → 0.1.56
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/knitting.d.ts +6 -797
- package/knitting.js +6 -9780
- package/map.md +12 -8
- package/package.json +14 -6
- package/process-shared-buffer.d.ts +1 -0
- package/process-shared-buffer.js +1 -0
- package/shared-memory.d.ts +1 -155
- package/shared-memory.js +1 -1342
- package/src/api.d.ts +69 -0
- package/src/api.js +619 -0
- package/src/common/envelope.d.ts +17 -0
- package/src/common/envelope.js +22 -0
- package/src/common/module-url.d.ts +1 -0
- package/src/common/module-url.js +24 -0
- package/src/common/node-compat.d.ts +20 -0
- package/src/common/node-compat.js +24 -0
- package/src/common/path-canonical.d.ts +6 -0
- package/src/common/path-canonical.js +41 -0
- package/src/common/runtime.d.ts +15 -0
- package/src/common/runtime.js +91 -0
- package/src/common/shared-buffer-region.d.ts +11 -0
- package/src/common/shared-buffer-region.js +21 -0
- package/src/common/shared-buffer-text.d.ts +16 -0
- package/src/common/shared-buffer-text.js +65 -0
- package/src/common/task-source.d.ts +3 -0
- package/src/common/task-source.js +84 -0
- package/src/common/task-symbol.d.ts +1 -0
- package/src/common/task-symbol.js +1 -0
- package/src/common/with-resolvers.d.ts +9 -0
- package/src/common/with-resolvers.js +23 -0
- package/src/common/worker-runtime.d.ts +42 -0
- package/src/common/worker-runtime.js +61 -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/bun.d.ts +22 -0
- package/src/connections/bun.js +214 -0
- package/src/connections/deno.d.ts +22 -0
- package/src/connections/deno.js +205 -0
- package/src/connections/file-descriptor.d.ts +39 -0
- package/src/connections/file-descriptor.js +161 -0
- package/src/connections/index.d.ts +4 -0
- package/src/connections/index.js +4 -0
- package/src/connections/node-addons.d.ts +5 -0
- package/src/connections/node-addons.js +44 -0
- package/src/connections/node-buffer-pointer.d.ts +20 -0
- package/src/connections/node-buffer-pointer.js +16 -0
- package/src/connections/node.d.ts +30 -0
- package/src/connections/node.js +62 -0
- package/src/connections/numeric-array.d.ts +5 -0
- package/src/connections/numeric-array.js +17 -0
- package/src/connections/package-assets.d.ts +2 -0
- package/src/connections/package-assets.js +45 -0
- package/src/connections/posix.d.ts +32 -0
- package/src/connections/posix.js +77 -0
- package/src/connections/process-shared-buffer.d.ts +75 -0
- package/src/connections/process-shared-buffer.js +279 -0
- package/src/connections/shared-array-buffer-payload.d.ts +36 -0
- package/src/connections/shared-array-buffer-payload.js +235 -0
- package/src/connections/types.d.ts +50 -0
- package/src/connections/types.js +37 -0
- package/src/connections/windows.d.ts +28 -0
- package/src/connections/windows.js +224 -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/error.d.ts +13 -0
- package/src/error.js +49 -0
- package/src/ipc/tools/ring-queue.d.ts +33 -0
- package/src/ipc/tools/ring-queue.js +159 -0
- package/src/ipc/transport/shared-memory.d.ts +23 -0
- package/src/ipc/transport/shared-memory.js +35 -0
- package/src/memory/byte-carpet.d.ts +73 -0
- package/src/memory/byte-carpet.js +157 -0
- package/src/memory/lock.d.ts +203 -0
- package/src/memory/lock.js +741 -0
- package/src/memory/payload-config.d.ts +31 -0
- package/src/memory/payload-config.js +67 -0
- package/src/memory/payloadCodec.d.ts +46 -0
- package/src/memory/payloadCodec.js +1377 -0
- package/src/memory/regionRegistry.d.ts +17 -0
- package/src/memory/regionRegistry.js +285 -0
- package/src/memory/shared-buffer-io.d.ts +55 -0
- package/src/memory/shared-buffer-io.js +403 -0
- package/src/permission/index.d.ts +2 -0
- package/src/permission/index.js +2 -0
- package/src/permission/protocol.d.ts +166 -0
- package/src/permission/protocol.js +650 -0
- package/src/runtime/balancer.d.ts +19 -0
- package/src/runtime/balancer.js +149 -0
- package/src/runtime/dispatcher.d.ts +40 -0
- package/src/runtime/dispatcher.js +142 -0
- package/src/runtime/inline-executor.d.ts +10 -0
- package/src/runtime/inline-executor.js +282 -0
- package/src/runtime/pool.d.ts +39 -0
- package/src/runtime/pool.js +404 -0
- package/src/runtime/process-worker.d.ts +92 -0
- package/src/runtime/process-worker.js +673 -0
- package/src/runtime/tx-queue.d.ts +26 -0
- package/src/runtime/tx-queue.js +155 -0
- package/src/shared/abortSignal.d.ts +23 -0
- package/src/shared/abortSignal.js +134 -0
- package/src/types.d.ts +357 -0
- package/src/types.js +2 -0
- package/src/utils/http.d.ts +29 -0
- package/src/utils/http.js +100 -0
- package/src/worker/bootstrap.d.ts +5 -0
- package/src/worker/bootstrap.js +78 -0
- package/src/worker/composable-runners.d.ts +12 -0
- package/src/worker/composable-runners.js +97 -0
- package/src/worker/loop.d.ts +2 -0
- package/src/worker/loop.js +382 -0
- package/src/worker/process-worker-bootstrap.d.ts +8 -0
- package/src/worker/process-worker-bootstrap.js +160 -0
- package/src/worker/rx-queue.d.ts +25 -0
- package/src/worker/rx-queue.js +173 -0
- package/src/worker/safety/index.d.ts +4 -0
- package/src/worker/safety/index.js +4 -0
- package/src/worker/safety/performance.d.ts +1 -0
- package/src/worker/safety/performance.js +17 -0
- package/src/worker/safety/process.d.ts +2 -0
- package/src/worker/safety/process.js +79 -0
- package/src/worker/safety/startup.d.ts +16 -0
- package/src/worker/safety/startup.js +30 -0
- package/src/worker/safety/worker-data.d.ts +2 -0
- package/src/worker/safety/worker-data.js +36 -0
- package/src/worker/task-loader.d.ts +27 -0
- package/src/worker/task-loader.js +70 -0
- package/src/worker/timers.d.ts +18 -0
- package/src/worker/timers.js +116 -0
- package/unsafe.d.ts +1 -71
- package/unsafe.js +1 -878
- package/utils.d.ts +1 -31
- package/utils.js +1 -116
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const safeExists = (path) => {
|
|
5
|
+
try {
|
|
6
|
+
return existsSync(path);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const safeIsDirectory = (path) => {
|
|
13
|
+
try {
|
|
14
|
+
return statSync(path).isDirectory();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const moduleDirectory = (moduleUrl) => {
|
|
21
|
+
const path = fileURLToPath(moduleUrl);
|
|
22
|
+
return safeIsDirectory(path) ? path : dirname(path);
|
|
23
|
+
};
|
|
24
|
+
const isKnittingPackageRoot = (dir) => safeExists(join(dir, "package.json")) &&
|
|
25
|
+
(safeExists(join(dir, "prebuilds")) ||
|
|
26
|
+
safeExists(join(dir, "src")) ||
|
|
27
|
+
safeExists(join(dir, "build")));
|
|
28
|
+
let cachedPackageRoot;
|
|
29
|
+
export const findKnittingPackageRoot = (moduleUrl = import.meta.url) => {
|
|
30
|
+
if (cachedPackageRoot !== undefined)
|
|
31
|
+
return cachedPackageRoot;
|
|
32
|
+
let current = moduleDirectory(moduleUrl);
|
|
33
|
+
while (true) {
|
|
34
|
+
if (isKnittingPackageRoot(current)) {
|
|
35
|
+
cachedPackageRoot = current;
|
|
36
|
+
return current;
|
|
37
|
+
}
|
|
38
|
+
const parent = dirname(current);
|
|
39
|
+
if (parent === current) {
|
|
40
|
+
throw new Error(`Could not locate knitting package root from ${moduleUrl}`);
|
|
41
|
+
}
|
|
42
|
+
current = parent;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
export const resolveKnittingPackageAsset = (...segments) => join(findKnittingPackageRoot(), ...segments);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type PosixPlatform = "linux" | "darwin";
|
|
2
|
+
export declare const LINUX_LIBC_SO = "libc.so.6";
|
|
3
|
+
export declare const DARWIN_LIBSYSTEM = "/usr/lib/libSystem.B.dylib";
|
|
4
|
+
export declare const PROT_READ = 1;
|
|
5
|
+
export declare const PROT_WRITE = 2;
|
|
6
|
+
export declare const MAP_SHARED = 1;
|
|
7
|
+
export declare const O_RDWR = 2;
|
|
8
|
+
export declare const LINUX_O_CREAT = 64;
|
|
9
|
+
export declare const LINUX_O_EXCL = 128;
|
|
10
|
+
export declare const DARWIN_O_CREAT = 512;
|
|
11
|
+
export declare const DARWIN_O_EXCL = 2048;
|
|
12
|
+
export declare const DARWIN_SHM_MODE = 384;
|
|
13
|
+
export declare const POSIX_SHM_MODE = 384;
|
|
14
|
+
export declare const F_GETFD = 1;
|
|
15
|
+
export declare const F_SETFD = 2;
|
|
16
|
+
export declare const FD_CLOEXEC = 1;
|
|
17
|
+
export declare const encodeCString: (value: string) => Uint8Array;
|
|
18
|
+
type FcntlLibc = {
|
|
19
|
+
symbols: {
|
|
20
|
+
fcntl: (fd: number, cmd: number, arg: number) => number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export declare const setCloseOnExec: <T extends FcntlLibc>(libc: T, fd: number) => number;
|
|
24
|
+
export declare const shmOpenCreateFlag: (platform: PosixPlatform) => number;
|
|
25
|
+
export declare const shmOpenExclusiveFlag: (platform: PosixPlatform) => number;
|
|
26
|
+
export declare const POSIX_SHM_MAX_NAME_LEN = 30;
|
|
27
|
+
export declare const toPosixSharedMemoryName: (name: string) => string;
|
|
28
|
+
export declare const detectPosixPlatform: () => PosixPlatform;
|
|
29
|
+
export declare const assertPosixSharedMemoryPlatform: (feature: string) => void;
|
|
30
|
+
export declare const getPosixLibcPath: (platform?: PosixPlatform) => string;
|
|
31
|
+
export declare const makeDarwinSharedMemoryName: (_name: string, runtime: string) => string;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export const LINUX_LIBC_SO = "libc.so.6";
|
|
2
|
+
export const DARWIN_LIBSYSTEM = "/usr/lib/libSystem.B.dylib";
|
|
3
|
+
export const PROT_READ = 1;
|
|
4
|
+
export const PROT_WRITE = 2;
|
|
5
|
+
export const MAP_SHARED = 1;
|
|
6
|
+
export const O_RDWR = 0x0002;
|
|
7
|
+
export const LINUX_O_CREAT = 0x0040;
|
|
8
|
+
export const LINUX_O_EXCL = 0x0080;
|
|
9
|
+
export const DARWIN_O_CREAT = 0x0200;
|
|
10
|
+
export const DARWIN_O_EXCL = 0x0800;
|
|
11
|
+
export const DARWIN_SHM_MODE = 0o600;
|
|
12
|
+
export const POSIX_SHM_MODE = 0o600;
|
|
13
|
+
export const F_GETFD = 1;
|
|
14
|
+
export const F_SETFD = 2;
|
|
15
|
+
export const FD_CLOEXEC = 1;
|
|
16
|
+
export const encodeCString = (value) => new TextEncoder().encode(`${value}\0`);
|
|
17
|
+
export const setCloseOnExec = (libc, fd) => {
|
|
18
|
+
const flags = libc.symbols.fcntl(fd, F_GETFD, 0);
|
|
19
|
+
if (flags < 0) {
|
|
20
|
+
throw new Error("fcntl(F_GETFD) failed");
|
|
21
|
+
}
|
|
22
|
+
const result = libc.symbols.fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
23
|
+
if (result < 0) {
|
|
24
|
+
throw new Error("fcntl(F_SETFD) failed");
|
|
25
|
+
}
|
|
26
|
+
return fd;
|
|
27
|
+
};
|
|
28
|
+
export const shmOpenCreateFlag = (platform) => platform === "darwin" ? DARWIN_O_CREAT : LINUX_O_CREAT;
|
|
29
|
+
export const shmOpenExclusiveFlag = (platform) => platform === "darwin" ? DARWIN_O_EXCL : LINUX_O_EXCL;
|
|
30
|
+
// macOS limits POSIX shared memory names to 31 characters including the
|
|
31
|
+
// leading "/", so the usable name portion is at most 30 characters.
|
|
32
|
+
export const POSIX_SHM_MAX_NAME_LEN = 30;
|
|
33
|
+
export const toPosixSharedMemoryName = (name) => {
|
|
34
|
+
if (name.length === 0) {
|
|
35
|
+
throw new TypeError("shared memory name must be non-empty");
|
|
36
|
+
}
|
|
37
|
+
if (name.includes("\0")) {
|
|
38
|
+
throw new TypeError("shared memory name must not contain NUL bytes");
|
|
39
|
+
}
|
|
40
|
+
const out = name.startsWith("/") ? name : `/${name}`;
|
|
41
|
+
if (out.length < 2 || out.slice(1).includes("/")) {
|
|
42
|
+
throw new TypeError("POSIX shared memory name must not contain path separators");
|
|
43
|
+
}
|
|
44
|
+
if (out.length > POSIX_SHM_MAX_NAME_LEN + 1) {
|
|
45
|
+
throw new TypeError(`POSIX shared memory name must be at most ${POSIX_SHM_MAX_NAME_LEN} characters (macOS limit); got ${out.length - 1}`);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
};
|
|
49
|
+
export const detectPosixPlatform = () => {
|
|
50
|
+
const denoOs = globalThis.Deno?.build?.os;
|
|
51
|
+
if (denoOs === "darwin" || denoOs === "linux")
|
|
52
|
+
return denoOs;
|
|
53
|
+
const processPlatform = globalThis.process?.platform;
|
|
54
|
+
if (processPlatform === "darwin" || processPlatform === "linux") {
|
|
55
|
+
return processPlatform;
|
|
56
|
+
}
|
|
57
|
+
throw new Error("shared memory connections support Linux and macOS only");
|
|
58
|
+
};
|
|
59
|
+
export const assertPosixSharedMemoryPlatform = (feature) => {
|
|
60
|
+
try {
|
|
61
|
+
detectPosixPlatform();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new Error(`${feature} is supported on Linux and macOS only`);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
export const getPosixLibcPath = (platform = detectPosixPlatform()) => platform === "darwin" ? DARWIN_LIBSYSTEM : LINUX_LIBC_SO;
|
|
68
|
+
export const makeDarwinSharedMemoryName = (_name, runtime) => {
|
|
69
|
+
const processId = globalThis.process?.pid ??
|
|
70
|
+
globalThis.Deno?.pid ??
|
|
71
|
+
0;
|
|
72
|
+
const runtimeTag = runtime.slice(0, 1) || "x";
|
|
73
|
+
const pidTag = Math.abs(processId).toString(36).slice(-5);
|
|
74
|
+
const timeTag = Date.now().toString(36).slice(-6);
|
|
75
|
+
const nonce = Math.random().toString(36).slice(2, 6);
|
|
76
|
+
return `/knit_${runtimeTag}_${pidTag}_${timeTag}_${nonce}`;
|
|
77
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { SharedBuffer, SharedBufferRegion } from "../common/shared-buffer-region.js";
|
|
2
|
+
import { FileDescriptor, type FileDescriptorMetadata } from "./file-descriptor.js";
|
|
3
|
+
import { type CreateSharedMemoryOptions, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
|
|
4
|
+
export type ProcessSharedBufferMetadata = {
|
|
5
|
+
version: 1;
|
|
6
|
+
descriptor: FileDescriptorMetadata;
|
|
7
|
+
byteOffset: number;
|
|
8
|
+
byteLength: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const PROCESS_SHARED_BUFFER_BRAND: unique symbol;
|
|
11
|
+
export declare const PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER: unique symbol;
|
|
12
|
+
declare const EXTERNAL_PAYLOAD_BRAND: unique symbol;
|
|
13
|
+
export type ProcessSharedBufferNumericMetadata = readonly [
|
|
14
|
+
fd: number,
|
|
15
|
+
size: number,
|
|
16
|
+
descriptorByteLength: number,
|
|
17
|
+
byteOffset: number,
|
|
18
|
+
byteLength: number,
|
|
19
|
+
runtime: number,
|
|
20
|
+
kind: number,
|
|
21
|
+
baseAddressMod64: number
|
|
22
|
+
];
|
|
23
|
+
export type ProcessSharedBufferRange = {
|
|
24
|
+
byteOffset?: number;
|
|
25
|
+
byteLength?: number;
|
|
26
|
+
};
|
|
27
|
+
export type ProcessSharedBufferCreator = Pick<SharedMemoryConnectionPrimitives, "createSharedMemory">;
|
|
28
|
+
export type ProcessSharedBufferMapper = Pick<SharedMemoryConnectionPrimitives, "mapSharedMemory">;
|
|
29
|
+
export type ProcessSharedBufferPrimitives = Pick<SharedMemoryConnectionPrimitives, "createSharedMemory" | "mapSharedMemory"> & {
|
|
30
|
+
unlinkSharedMemory?: (name: string) => boolean;
|
|
31
|
+
};
|
|
32
|
+
export type ProcessSharedBufferView = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float32Array | Float64Array;
|
|
33
|
+
export type ProcessSharedBufferViewConstructor<View extends ProcessSharedBufferView = ProcessSharedBufferView> = {
|
|
34
|
+
readonly BYTES_PER_ELEMENT: number;
|
|
35
|
+
new (buffer: SharedBuffer, byteOffset: number, length: number): View;
|
|
36
|
+
};
|
|
37
|
+
export declare const setDefaultProcessSharedBufferPrimitives: (primitives: ProcessSharedBufferPrimitives | undefined) => void;
|
|
38
|
+
export declare const getDefaultProcessSharedBufferPrimitives: () => ProcessSharedBufferPrimitives;
|
|
39
|
+
/**
|
|
40
|
+
* Zero-copy shared bytes for process workers.
|
|
41
|
+
*
|
|
42
|
+
* Use this when the boundary is a separate process, container, or sandbox.
|
|
43
|
+
* For thread workers, `SharedArrayBuffer` or `BufferReference` can be cheaper.
|
|
44
|
+
*/
|
|
45
|
+
export declare class ProcessSharedBuffer {
|
|
46
|
+
readonly [PROCESS_SHARED_BUFFER_BRAND] = true;
|
|
47
|
+
readonly [EXTERNAL_PAYLOAD_BRAND] = "knitting.processSharedBuffer";
|
|
48
|
+
readonly descriptor: FileDescriptor;
|
|
49
|
+
readonly byteOffset: number;
|
|
50
|
+
readonly byteLength: number;
|
|
51
|
+
constructor(descriptor: FileDescriptor, range?: ProcessSharedBufferRange);
|
|
52
|
+
static create(options: number | CreateSharedMemoryOptions, creator?: ProcessSharedBufferCreator): ProcessSharedBuffer;
|
|
53
|
+
static fromMapping(mapping: SharedMemoryMapping): ProcessSharedBuffer;
|
|
54
|
+
static fromDescriptor(descriptor: FileDescriptor, range?: ProcessSharedBufferRange): ProcessSharedBuffer;
|
|
55
|
+
static fromMetadata(metadata: unknown): ProcessSharedBuffer;
|
|
56
|
+
static parse(serialized: string): ProcessSharedBuffer;
|
|
57
|
+
static [PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER](metadata: ProcessSharedBufferNumericMetadata): ProcessSharedBuffer;
|
|
58
|
+
get fd(): number;
|
|
59
|
+
get size(): number;
|
|
60
|
+
subbuffer(byteOffset: number, byteLength?: number): ProcessSharedBuffer;
|
|
61
|
+
getSharedArrayBuffer(mapper?: ProcessSharedBufferMapper): SharedArrayBuffer;
|
|
62
|
+
getSAB(mapper?: ProcessSharedBufferMapper): SharedArrayBuffer;
|
|
63
|
+
getBuffer(mapper?: ProcessSharedBufferMapper): SharedBuffer;
|
|
64
|
+
getRegion(mapper?: ProcessSharedBufferMapper): SharedBufferRegion;
|
|
65
|
+
view<View extends ProcessSharedBufferView>(constructor: ProcessSharedBufferViewConstructor<View>, mapper?: ProcessSharedBufferMapper): View;
|
|
66
|
+
bytes(mapper?: ProcessSharedBufferMapper): Uint8Array;
|
|
67
|
+
dataView(mapper?: ProcessSharedBufferMapper): DataView;
|
|
68
|
+
toMetadata(): ProcessSharedBufferMetadata;
|
|
69
|
+
toJSON(): ProcessSharedBufferMetadata;
|
|
70
|
+
stringify(): string;
|
|
71
|
+
stringifyMetadata(): string;
|
|
72
|
+
toString(): string;
|
|
73
|
+
}
|
|
74
|
+
export declare const parseProcessSharedBufferMetadata: (input: unknown) => ProcessSharedBufferMetadata;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { getNodeBuiltinModule } from "../common/node-compat.js";
|
|
2
|
+
import { FileDescriptor, } from "./file-descriptor.js";
|
|
3
|
+
import { RUNTIME } from "../common/runtime.js";
|
|
4
|
+
import { createBunConnectionPrimitives } from "./bun.js";
|
|
5
|
+
import { createDenoConnectionPrimitives } from "./deno.js";
|
|
6
|
+
import { loadNodeNativeAddon } from "./node-addons.js";
|
|
7
|
+
import { expectFd, expectPositiveSize, readCreateMode, readCreateName, readRequiredCreateName, readCreateSize, } from "./types.js";
|
|
8
|
+
export const PROCESS_SHARED_BUFFER_BRAND = Symbol.for("knitting.processSharedBuffer");
|
|
9
|
+
export const PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER = Symbol.for("knitting.processSharedBuffer.numericTransfer");
|
|
10
|
+
const EXTERNAL_PAYLOAD_BRAND = Symbol.for("knitting.payloadCodec");
|
|
11
|
+
const PROCESS_SHARED_BUFFER_CODEC_ID = "knitting.processSharedBuffer";
|
|
12
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
13
|
+
const NUMERIC_SENTINEL = 0xffffffff;
|
|
14
|
+
const RUNTIME_NODE = 1;
|
|
15
|
+
const RUNTIME_DENO = 2;
|
|
16
|
+
const RUNTIME_BUN = 3;
|
|
17
|
+
const KIND_SHARED_ARRAY_BUFFER = 1;
|
|
18
|
+
const KIND_EXTERNAL_ARRAY_BUFFER = 2;
|
|
19
|
+
const decodeRuntime = (value) => {
|
|
20
|
+
switch (value) {
|
|
21
|
+
case RUNTIME_NODE:
|
|
22
|
+
return "node";
|
|
23
|
+
case RUNTIME_DENO:
|
|
24
|
+
return "deno";
|
|
25
|
+
case RUNTIME_BUN:
|
|
26
|
+
return "bun";
|
|
27
|
+
default:
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const decodeKind = (value) => {
|
|
32
|
+
switch (value) {
|
|
33
|
+
case KIND_SHARED_ARRAY_BUFFER:
|
|
34
|
+
return "shared-array-buffer";
|
|
35
|
+
case KIND_EXTERNAL_ARRAY_BUFFER:
|
|
36
|
+
return "external-array-buffer";
|
|
37
|
+
default:
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
let defaultPrimitives;
|
|
42
|
+
const fromDefaultNodeNativeMapping = (mapped) => {
|
|
43
|
+
const mapping = {
|
|
44
|
+
runtime: "node",
|
|
45
|
+
fd: mapped.fd,
|
|
46
|
+
size: mapped.size,
|
|
47
|
+
byteLength: mapped.sab.byteLength,
|
|
48
|
+
buffer: mapped.sab,
|
|
49
|
+
kind: "shared-array-buffer",
|
|
50
|
+
sab: mapped.sab,
|
|
51
|
+
baseAddressMod64: mapped.baseAddressMod64,
|
|
52
|
+
};
|
|
53
|
+
if (mapped.name !== undefined && mapped.name.length > 0) {
|
|
54
|
+
mapping.name = mapped.name;
|
|
55
|
+
}
|
|
56
|
+
return mapping;
|
|
57
|
+
};
|
|
58
|
+
const createDefaultNodePrimitives = () => {
|
|
59
|
+
const nodeModule = getNodeBuiltinModule("node:module");
|
|
60
|
+
if (nodeModule === undefined) {
|
|
61
|
+
throw new TypeError("ProcessSharedBuffer needs connection primitives in this runtime");
|
|
62
|
+
}
|
|
63
|
+
const require = nodeModule.createRequire(import.meta.url);
|
|
64
|
+
const addon = loadNodeNativeAddon(require, "knitting_shared_memory");
|
|
65
|
+
return {
|
|
66
|
+
createSharedMemory: (options) => {
|
|
67
|
+
const size = expectPositiveSize(readCreateSize(options));
|
|
68
|
+
const mode = readCreateMode(options);
|
|
69
|
+
const name = mode === "anonymous"
|
|
70
|
+
? readCreateName(options, "knitting_shared_memory")
|
|
71
|
+
: readRequiredCreateName(options);
|
|
72
|
+
return fromDefaultNodeNativeMapping(addon.createSharedMemory(size, name, mode));
|
|
73
|
+
},
|
|
74
|
+
mapSharedMemory: (options) => {
|
|
75
|
+
const fd = expectFd(options.fd);
|
|
76
|
+
const size = expectPositiveSize(options.size);
|
|
77
|
+
return fromDefaultNodeNativeMapping(addon.mapSharedMemory(fd, size, options.name));
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const createDefaultPrimitives = () => {
|
|
82
|
+
if (RUNTIME === "bun")
|
|
83
|
+
return createBunConnectionPrimitives();
|
|
84
|
+
if (RUNTIME === "deno")
|
|
85
|
+
return createDenoConnectionPrimitives();
|
|
86
|
+
return createDefaultNodePrimitives();
|
|
87
|
+
};
|
|
88
|
+
export const setDefaultProcessSharedBufferPrimitives = (primitives) => {
|
|
89
|
+
defaultPrimitives = primitives;
|
|
90
|
+
};
|
|
91
|
+
export const getDefaultProcessSharedBufferPrimitives = () => {
|
|
92
|
+
defaultPrimitives ??= createDefaultPrimitives();
|
|
93
|
+
return defaultPrimitives;
|
|
94
|
+
};
|
|
95
|
+
const expectNonNegativeInteger = (value, label) => {
|
|
96
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
97
|
+
throw new RangeError(`${label} must be a non-negative integer`);
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
};
|
|
101
|
+
const readOptionalNonNegativeInteger = (value, label) => {
|
|
102
|
+
if (value === undefined)
|
|
103
|
+
return undefined;
|
|
104
|
+
if (typeof value !== "number") {
|
|
105
|
+
throw new TypeError(`${label} must be a number`);
|
|
106
|
+
}
|
|
107
|
+
return expectNonNegativeInteger(value, label);
|
|
108
|
+
};
|
|
109
|
+
const expectRange = (byteOffset, byteLength, availableByteLength) => {
|
|
110
|
+
expectNonNegativeInteger(byteOffset, "process shared buffer byteOffset");
|
|
111
|
+
if (byteOffset > availableByteLength) {
|
|
112
|
+
throw new RangeError("process shared buffer byteOffset is out of bounds");
|
|
113
|
+
}
|
|
114
|
+
expectNonNegativeInteger(byteLength, "process shared buffer byteLength");
|
|
115
|
+
if (byteLength > availableByteLength - byteOffset) {
|
|
116
|
+
throw new RangeError("process shared buffer byteLength is out of bounds");
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Zero-copy shared bytes for process workers.
|
|
121
|
+
*
|
|
122
|
+
* Use this when the boundary is a separate process, container, or sandbox.
|
|
123
|
+
* For thread workers, `SharedArrayBuffer` or `BufferReference` can be cheaper.
|
|
124
|
+
*/
|
|
125
|
+
export class ProcessSharedBuffer {
|
|
126
|
+
[PROCESS_SHARED_BUFFER_BRAND] = true;
|
|
127
|
+
[EXTERNAL_PAYLOAD_BRAND] = PROCESS_SHARED_BUFFER_CODEC_ID;
|
|
128
|
+
descriptor;
|
|
129
|
+
byteOffset;
|
|
130
|
+
byteLength;
|
|
131
|
+
constructor(descriptor, range = {}) {
|
|
132
|
+
const byteOffset = range.byteOffset ?? 0;
|
|
133
|
+
const byteLength = range.byteLength ??
|
|
134
|
+
descriptor.byteLength - byteOffset;
|
|
135
|
+
expectRange(byteOffset, byteLength, descriptor.byteLength);
|
|
136
|
+
this.descriptor = descriptor;
|
|
137
|
+
this.byteOffset = byteOffset;
|
|
138
|
+
this.byteLength = byteLength;
|
|
139
|
+
}
|
|
140
|
+
static create(options, creator = getDefaultProcessSharedBufferPrimitives()) {
|
|
141
|
+
return ProcessSharedBuffer.fromMapping(creator.createSharedMemory(options));
|
|
142
|
+
}
|
|
143
|
+
static fromMapping(mapping) {
|
|
144
|
+
return new ProcessSharedBuffer(FileDescriptor.fromMapping(mapping));
|
|
145
|
+
}
|
|
146
|
+
static fromDescriptor(descriptor, range = {}) {
|
|
147
|
+
return new ProcessSharedBuffer(descriptor, range);
|
|
148
|
+
}
|
|
149
|
+
static fromMetadata(metadata) {
|
|
150
|
+
const parsed = parseProcessSharedBufferMetadata(metadata);
|
|
151
|
+
return new ProcessSharedBuffer(FileDescriptor.fromMetadata(parsed.descriptor), {
|
|
152
|
+
byteOffset: parsed.byteOffset,
|
|
153
|
+
byteLength: parsed.byteLength,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
static parse(serialized) {
|
|
157
|
+
return ProcessSharedBuffer.fromMetadata(serialized);
|
|
158
|
+
}
|
|
159
|
+
// Rebuilds from the 8 raw words (no name -> no JSON):
|
|
160
|
+
// fd, size, descByteLength, byteOffset, byteLength, runtime, kind, baseAddressMod64
|
|
161
|
+
static [PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER](metadata) {
|
|
162
|
+
const [fd, size, descriptorByteLength, byteOffset, byteLength, runtime, kind, baseAddressMod64,] = metadata;
|
|
163
|
+
return new ProcessSharedBuffer(new FileDescriptor({
|
|
164
|
+
version: 1,
|
|
165
|
+
fd,
|
|
166
|
+
size,
|
|
167
|
+
byteLength: descriptorByteLength,
|
|
168
|
+
runtime: decodeRuntime(runtime),
|
|
169
|
+
kind: decodeKind(kind),
|
|
170
|
+
baseAddressMod64: baseAddressMod64 === NUMERIC_SENTINEL
|
|
171
|
+
? undefined
|
|
172
|
+
: baseAddressMod64,
|
|
173
|
+
}), {
|
|
174
|
+
byteOffset,
|
|
175
|
+
byteLength,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
get fd() {
|
|
179
|
+
return this.descriptor.fd;
|
|
180
|
+
}
|
|
181
|
+
get size() {
|
|
182
|
+
return this.descriptor.size;
|
|
183
|
+
}
|
|
184
|
+
subbuffer(byteOffset, byteLength) {
|
|
185
|
+
const relativeByteOffset = expectNonNegativeInteger(byteOffset, "process shared buffer subbuffer byteOffset");
|
|
186
|
+
const relativeByteLength = byteLength === undefined
|
|
187
|
+
? this.byteLength - relativeByteOffset
|
|
188
|
+
: expectNonNegativeInteger(byteLength, "process shared buffer subbuffer byteLength");
|
|
189
|
+
expectRange(relativeByteOffset, relativeByteLength, this.byteLength);
|
|
190
|
+
return new ProcessSharedBuffer(this.descriptor, {
|
|
191
|
+
byteOffset: this.byteOffset + relativeByteOffset,
|
|
192
|
+
byteLength: relativeByteLength,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
getSharedArrayBuffer(mapper) {
|
|
196
|
+
return this.descriptor.getSAB(mapper ??
|
|
197
|
+
(this.descriptor.mapping?.sab === undefined
|
|
198
|
+
? getDefaultProcessSharedBufferPrimitives()
|
|
199
|
+
: undefined));
|
|
200
|
+
}
|
|
201
|
+
getSAB(mapper) {
|
|
202
|
+
return this.getSharedArrayBuffer(mapper);
|
|
203
|
+
}
|
|
204
|
+
getBuffer(mapper) {
|
|
205
|
+
return this.descriptor.getBuffer(mapper ??
|
|
206
|
+
(this.descriptor.mapping?.buffer === undefined
|
|
207
|
+
? getDefaultProcessSharedBufferPrimitives()
|
|
208
|
+
: undefined));
|
|
209
|
+
}
|
|
210
|
+
getRegion(mapper) {
|
|
211
|
+
return {
|
|
212
|
+
sab: this.getBuffer(mapper),
|
|
213
|
+
byteOffset: this.byteOffset,
|
|
214
|
+
byteLength: this.byteLength,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
view(constructor, mapper) {
|
|
218
|
+
const bytesPerElement = constructor.BYTES_PER_ELEMENT;
|
|
219
|
+
if (this.byteOffset % bytesPerElement !== 0) {
|
|
220
|
+
throw new RangeError("process shared buffer byteOffset is not aligned for this view");
|
|
221
|
+
}
|
|
222
|
+
if (this.byteLength % bytesPerElement !== 0) {
|
|
223
|
+
throw new RangeError("process shared buffer byteLength is not aligned for this view");
|
|
224
|
+
}
|
|
225
|
+
return new constructor(this.getBuffer(mapper), this.byteOffset, this.byteLength / bytesPerElement);
|
|
226
|
+
}
|
|
227
|
+
bytes(mapper) {
|
|
228
|
+
return this.view(Uint8Array, mapper);
|
|
229
|
+
}
|
|
230
|
+
dataView(mapper) {
|
|
231
|
+
return new DataView(this.getBuffer(mapper), this.byteOffset, this.byteLength);
|
|
232
|
+
}
|
|
233
|
+
toMetadata() {
|
|
234
|
+
return {
|
|
235
|
+
version: 1,
|
|
236
|
+
descriptor: this.descriptor.toMetadata(),
|
|
237
|
+
byteOffset: this.byteOffset,
|
|
238
|
+
byteLength: this.byteLength,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
toJSON() {
|
|
242
|
+
return this.toMetadata();
|
|
243
|
+
}
|
|
244
|
+
stringify() {
|
|
245
|
+
return JSON.stringify(this.toMetadata());
|
|
246
|
+
}
|
|
247
|
+
stringifyMetadata() {
|
|
248
|
+
return this.stringify();
|
|
249
|
+
}
|
|
250
|
+
toString() {
|
|
251
|
+
return this.stringify();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
export const parseProcessSharedBufferMetadata = (input) => {
|
|
255
|
+
const value = typeof input === "string" ? JSON.parse(input) : input;
|
|
256
|
+
if (!isRecord(value)) {
|
|
257
|
+
throw new TypeError("process shared buffer metadata must be an object");
|
|
258
|
+
}
|
|
259
|
+
if (value.version !== 1) {
|
|
260
|
+
throw new TypeError("unsupported process shared buffer metadata version");
|
|
261
|
+
}
|
|
262
|
+
const descriptor = FileDescriptor.fromMetadata(value.descriptor);
|
|
263
|
+
const byteOffset = readOptionalNonNegativeInteger(value.byteOffset, "process shared buffer byteOffset") ?? 0;
|
|
264
|
+
const byteLength = readOptionalNonNegativeInteger(value.byteLength, "process shared buffer byteLength") ?? descriptor.byteLength - byteOffset;
|
|
265
|
+
expectRange(byteOffset, byteLength, descriptor.byteLength);
|
|
266
|
+
return {
|
|
267
|
+
version: 1,
|
|
268
|
+
descriptor: descriptor.toMetadata(),
|
|
269
|
+
byteOffset,
|
|
270
|
+
byteLength,
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
const processSharedBufferGlobal = globalThis;
|
|
274
|
+
const codecs = processSharedBufferGlobal.__KNITTING_PAYLOAD_CODECS__ ??=
|
|
275
|
+
Object.create(null);
|
|
276
|
+
codecs[PROCESS_SHARED_BUFFER_CODEC_ID] = {
|
|
277
|
+
decode: (metadata) => ProcessSharedBuffer.fromMetadata(metadata),
|
|
278
|
+
decodeNumeric: (metadata) => ProcessSharedBuffer[PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER](metadata),
|
|
279
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const SHARED_ARRAY_BUFFER_CODEC_ID: "knitting.sharedArrayBuffer";
|
|
2
|
+
export declare const SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER: unique symbol;
|
|
3
|
+
export declare const SHARED_ARRAY_BUFFER_NUMERIC_WORDS = 8;
|
|
4
|
+
declare const EXTERNAL_PAYLOAD_BRAND: unique symbol;
|
|
5
|
+
type SharedArrayBufferMetadata = {
|
|
6
|
+
readonly kind: typeof SHARED_ARRAY_BUFFER_CODEC_ID;
|
|
7
|
+
readonly origin: string;
|
|
8
|
+
readonly runtime: string;
|
|
9
|
+
readonly pointer: string;
|
|
10
|
+
readonly token: string;
|
|
11
|
+
readonly byteLength: number;
|
|
12
|
+
};
|
|
13
|
+
type SharedArrayBufferNumericMetadata = readonly [
|
|
14
|
+
tokenLow: number,
|
|
15
|
+
tokenHigh: number,
|
|
16
|
+
pointerLow: number,
|
|
17
|
+
pointerHigh: number,
|
|
18
|
+
byteLength: number,
|
|
19
|
+
runtime: number,
|
|
20
|
+
originPid: number,
|
|
21
|
+
mode: number
|
|
22
|
+
];
|
|
23
|
+
type SharedArrayBufferTokenNumericMetadata = readonly [
|
|
24
|
+
tokenLow: number,
|
|
25
|
+
tokenHigh: number
|
|
26
|
+
];
|
|
27
|
+
type SharedArrayBufferPayload = {
|
|
28
|
+
readonly [EXTERNAL_PAYLOAD_BRAND]: typeof SHARED_ARRAY_BUFFER_CODEC_ID;
|
|
29
|
+
readonly toMetadata: () => SharedArrayBufferMetadata;
|
|
30
|
+
readonly [SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER]: (transportKey?: object) => SharedArrayBufferNumericMetadata | SharedArrayBufferTokenNumericMetadata | undefined;
|
|
31
|
+
};
|
|
32
|
+
export declare const isSharedArrayBufferValue: (value: unknown) => value is SharedArrayBuffer;
|
|
33
|
+
/** Wrap a SAB as external payload; GC-managed pins mean no settle finalizer. */
|
|
34
|
+
export declare const wrapSharedArrayBufferPayload: (sab: SharedArrayBuffer) => SharedArrayBufferPayload;
|
|
35
|
+
export declare const getSharedArrayBufferPayload: (value: object) => SharedArrayBufferPayload | undefined;
|
|
36
|
+
export {};
|