knitting 0.1.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +632 -0
  3. package/knitting.d.ts +4 -0
  4. package/knitting.js +5 -0
  5. package/map.md +264 -0
  6. package/package.json +77 -0
  7. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  8. package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
  9. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  10. package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
  11. package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
  12. package/prebuilds/darwin-x64-node-127/knitting_shm.node +0 -0
  13. package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
  14. package/prebuilds/darwin-x64-node-137/knitting_shm.node +0 -0
  15. package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
  16. package/prebuilds/linux-x64-node-127/knitting_shm.node +0 -0
  17. package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
  18. package/prebuilds/linux-x64-node-137/knitting_shm.node +0 -0
  19. package/process-shared-buffer.d.ts +1 -0
  20. package/process-shared-buffer.js +1 -0
  21. package/scripts/build-native-addons.ts +295 -0
  22. package/src/api.d.ts +55 -0
  23. package/src/api.js +384 -0
  24. package/src/common/envelope.d.ts +11 -0
  25. package/src/common/envelope.js +8 -0
  26. package/src/common/module-url.d.ts +1 -0
  27. package/src/common/module-url.js +24 -0
  28. package/src/common/node-compat.d.ts +20 -0
  29. package/src/common/node-compat.js +24 -0
  30. package/src/common/path-canonical.d.ts +6 -0
  31. package/src/common/path-canonical.js +41 -0
  32. package/src/common/runtime.d.ts +15 -0
  33. package/src/common/runtime.js +91 -0
  34. package/src/common/shared-buffer-region.d.ts +11 -0
  35. package/src/common/shared-buffer-region.js +21 -0
  36. package/src/common/shared-buffer-text.d.ts +16 -0
  37. package/src/common/shared-buffer-text.js +65 -0
  38. package/src/common/task-source.d.ts +2 -0
  39. package/src/common/task-source.js +79 -0
  40. package/src/common/task-symbol.d.ts +1 -0
  41. package/src/common/task-symbol.js +1 -0
  42. package/src/common/with-resolvers.d.ts +9 -0
  43. package/src/common/with-resolvers.js +23 -0
  44. package/src/common/worker-runtime.d.ts +40 -0
  45. package/src/common/worker-runtime.js +52 -0
  46. package/src/connections/bun.d.ts +20 -0
  47. package/src/connections/bun.js +159 -0
  48. package/src/connections/deno.d.ts +20 -0
  49. package/src/connections/deno.js +150 -0
  50. package/src/connections/file-descriptor.d.ts +37 -0
  51. package/src/connections/file-descriptor.js +139 -0
  52. package/src/connections/index.d.ts +3 -0
  53. package/src/connections/index.js +3 -0
  54. package/src/connections/node-addons.d.ts +5 -0
  55. package/src/connections/node-addons.js +43 -0
  56. package/src/connections/node.d.ts +29 -0
  57. package/src/connections/node.js +59 -0
  58. package/src/connections/posix.d.ts +31 -0
  59. package/src/connections/posix.js +71 -0
  60. package/src/connections/process-shared-buffer.d.ts +67 -0
  61. package/src/connections/process-shared-buffer.js +267 -0
  62. package/src/connections/types.d.ts +48 -0
  63. package/src/connections/types.js +37 -0
  64. package/src/error.d.ts +13 -0
  65. package/src/error.js +49 -0
  66. package/src/ipc/tools/ring-queue.d.ts +33 -0
  67. package/src/ipc/tools/ring-queue.js +159 -0
  68. package/src/ipc/transport/shared-memory.d.ts +25 -0
  69. package/src/ipc/transport/shared-memory.js +35 -0
  70. package/src/knitting_shared_memory.cc +436 -0
  71. package/src/knitting_shm.cc +476 -0
  72. package/src/memory/byte-carpet.d.ts +73 -0
  73. package/src/memory/byte-carpet.js +157 -0
  74. package/src/memory/lock.d.ts +190 -0
  75. package/src/memory/lock.js +856 -0
  76. package/src/memory/payload-config.d.ts +22 -0
  77. package/src/memory/payload-config.js +67 -0
  78. package/src/memory/payloadCodec.d.ts +46 -0
  79. package/src/memory/payloadCodec.js +1157 -0
  80. package/src/memory/regionRegistry.d.ts +17 -0
  81. package/src/memory/regionRegistry.js +285 -0
  82. package/src/memory/shared-buffer-io.d.ts +53 -0
  83. package/src/memory/shared-buffer-io.js +380 -0
  84. package/src/permission/index.d.ts +2 -0
  85. package/src/permission/index.js +2 -0
  86. package/src/permission/protocol.d.ts +166 -0
  87. package/src/permission/protocol.js +640 -0
  88. package/src/runtime/balancer.d.ts +19 -0
  89. package/src/runtime/balancer.js +149 -0
  90. package/src/runtime/dispatcher.d.ts +34 -0
  91. package/src/runtime/dispatcher.js +142 -0
  92. package/src/runtime/inline-executor.d.ts +10 -0
  93. package/src/runtime/inline-executor.js +270 -0
  94. package/src/runtime/pool.d.ts +43 -0
  95. package/src/runtime/pool.js +922 -0
  96. package/src/runtime/tx-queue.d.ts +25 -0
  97. package/src/runtime/tx-queue.js +144 -0
  98. package/src/shared/abortSignal.d.ts +23 -0
  99. package/src/shared/abortSignal.js +126 -0
  100. package/src/types.d.ts +283 -0
  101. package/src/types.js +2 -0
  102. package/src/worker/composable-runners.d.ts +12 -0
  103. package/src/worker/composable-runners.js +105 -0
  104. package/src/worker/loop.d.ts +2 -0
  105. package/src/worker/loop.js +453 -0
  106. package/src/worker/rx-queue.d.ts +22 -0
  107. package/src/worker/rx-queue.js +124 -0
  108. package/src/worker/safety/index.d.ts +4 -0
  109. package/src/worker/safety/index.js +4 -0
  110. package/src/worker/safety/performance.d.ts +1 -0
  111. package/src/worker/safety/performance.js +17 -0
  112. package/src/worker/safety/process.d.ts +2 -0
  113. package/src/worker/safety/process.js +79 -0
  114. package/src/worker/safety/startup.d.ts +16 -0
  115. package/src/worker/safety/startup.js +30 -0
  116. package/src/worker/safety/worker-data.d.ts +2 -0
  117. package/src/worker/safety/worker-data.js +36 -0
  118. package/src/worker/task-loader.d.ts +26 -0
  119. package/src/worker/task-loader.js +66 -0
  120. package/src/worker/timers.d.ts +18 -0
  121. package/src/worker/timers.js +97 -0
@@ -0,0 +1,150 @@
1
+ import { DARWIN_O_CREAT, DARWIN_O_EXCL, DARWIN_SHM_MODE, setCloseOnExec, detectPosixPlatform, encodeCString, getPosixLibcPath, makeDarwinSharedMemoryName, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE, } from "./posix.js";
2
+ import { expectFd, expectPositiveSize, readCreateName, readCreateSize, } from "./types.js";
3
+ const getDeno = () => {
4
+ const deno = globalThis.Deno;
5
+ if (deno === undefined) {
6
+ throw new Error("Deno shared memory primitives can only run in Deno");
7
+ }
8
+ return deno;
9
+ };
10
+ export const openDenoLibc = () => getDeno().dlopen(getPosixLibcPath(), {
11
+ ...getDenoCreateSymbols(),
12
+ ftruncate: {
13
+ parameters: ["i32", "i64"],
14
+ result: "i32",
15
+ },
16
+ dup: {
17
+ parameters: ["i32"],
18
+ result: "i32",
19
+ },
20
+ fcntl: {
21
+ parameters: ["i32", "i32", "i32"],
22
+ result: "i32",
23
+ },
24
+ mmap: {
25
+ parameters: ["pointer", "usize", "i32", "i32", "i32", "i64"],
26
+ result: "pointer",
27
+ },
28
+ munmap: {
29
+ parameters: ["pointer", "usize"],
30
+ result: "i32",
31
+ },
32
+ close: {
33
+ parameters: ["i32"],
34
+ result: "i32",
35
+ },
36
+ });
37
+ const getDenoCreateSymbols = (platform = detectPosixPlatform()) => platform === "darwin"
38
+ ? {
39
+ shm_open: {
40
+ parameters: ["buffer", "i32", "u32"],
41
+ result: "i32",
42
+ },
43
+ shm_unlink: {
44
+ parameters: ["buffer"],
45
+ result: "i32",
46
+ },
47
+ }
48
+ : {
49
+ memfd_create: {
50
+ parameters: ["buffer", "u32"],
51
+ result: "i32",
52
+ },
53
+ };
54
+ const checkResult = (result, message) => {
55
+ if (result < 0)
56
+ throw new Error(message);
57
+ return result;
58
+ };
59
+ const isDenoMmapFailed = (pointer) => {
60
+ if (pointer === null)
61
+ return true;
62
+ const value = getDeno().UnsafePointer?.value(pointer);
63
+ return value === -1n || value === BigInt.asUintN(64, -1n);
64
+ };
65
+ const createDenoSharedMemoryFd = (name, platform, libc) => {
66
+ if (platform === "darwin") {
67
+ const shmOpen = libc.symbols.shm_open;
68
+ const shmUnlink = libc.symbols.shm_unlink;
69
+ if (shmOpen === undefined || shmUnlink === undefined) {
70
+ throw new Error("shm_open symbols are not available");
71
+ }
72
+ const shmName = encodeCString(makeDarwinSharedMemoryName(name, "deno"));
73
+ const fd = checkResult(shmOpen(shmName, O_RDWR | DARWIN_O_CREAT | DARWIN_O_EXCL, DARWIN_SHM_MODE), "shm_open failed");
74
+ shmUnlink(shmName);
75
+ try {
76
+ return setCloseOnExec(libc, fd);
77
+ }
78
+ catch (error) {
79
+ libc.symbols.close(fd);
80
+ throw error;
81
+ }
82
+ }
83
+ const memfdCreate = libc.symbols.memfd_create;
84
+ if (memfdCreate === undefined) {
85
+ throw new Error("memfd_create symbol is not available");
86
+ }
87
+ const fd = checkResult(memfdCreate(encodeCString(name), 0), "memfd_create failed");
88
+ try {
89
+ return setCloseOnExec(libc, fd);
90
+ }
91
+ catch (error) {
92
+ libc.symbols.close(fd);
93
+ throw error;
94
+ }
95
+ };
96
+ export const mapDenoSharedMemory = (options, libc = openDenoLibc()) => {
97
+ const sourceFd = expectFd(options.fd);
98
+ const size = expectPositiveSize(options.size);
99
+ let fd = sourceFd;
100
+ if (options.duplicateFd !== false) {
101
+ fd = checkResult(libc.symbols.dup(sourceFd), "dup(fd) failed");
102
+ try {
103
+ setCloseOnExec(libc, fd);
104
+ }
105
+ catch (error) {
106
+ libc.symbols.close(fd);
107
+ throw error;
108
+ }
109
+ }
110
+ const pointer = libc.symbols.mmap(null, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0n);
111
+ if (isDenoMmapFailed(pointer)) {
112
+ if (options.duplicateFd !== false)
113
+ libc.symbols.close(fd);
114
+ throw new Error("mmap failed");
115
+ }
116
+ const arrayBuffer = new (getDeno().UnsafePointerView)(pointer)
117
+ .getArrayBuffer(size);
118
+ return {
119
+ runtime: "deno",
120
+ fd,
121
+ size,
122
+ byteLength: arrayBuffer.byteLength,
123
+ buffer: arrayBuffer,
124
+ kind: "external-array-buffer",
125
+ arrayBuffer,
126
+ unsafePointer: pointer,
127
+ close: () => {
128
+ libc.symbols.munmap(pointer, size);
129
+ libc.symbols.close(fd);
130
+ },
131
+ };
132
+ };
133
+ export const createDenoSharedMemory = (options, libc = openDenoLibc()) => {
134
+ const size = expectPositiveSize(readCreateSize(options));
135
+ const name = readCreateName(options, "knitting_shared_memory");
136
+ const fd = createDenoSharedMemoryFd(name, detectPosixPlatform(), libc);
137
+ try {
138
+ checkResult(libc.symbols.ftruncate(fd, BigInt(size)), "ftruncate failed");
139
+ return mapDenoSharedMemory({ fd, size, duplicateFd: false }, libc);
140
+ }
141
+ catch (error) {
142
+ libc.symbols.close(fd);
143
+ throw error;
144
+ }
145
+ };
146
+ export const createDenoConnectionPrimitives = (libc = openDenoLibc()) => ({
147
+ runtime: "deno",
148
+ createSharedMemory: (options) => createDenoSharedMemory(options, libc),
149
+ mapSharedMemory: (options) => mapDenoSharedMemory(options, libc),
150
+ });
@@ -0,0 +1,37 @@
1
+ import { type ConnectionRuntime, type SharedMemoryBuffer, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
2
+ export type FileDescriptorMetadata = {
3
+ version: 1;
4
+ fd: number;
5
+ size: number;
6
+ byteLength: number;
7
+ runtime?: ConnectionRuntime;
8
+ kind?: SharedMemoryMapping["kind"];
9
+ baseAddressMod64?: number;
10
+ };
11
+ type FileDescriptorMapper = Pick<SharedMemoryConnectionPrimitives, "mapSharedMemory">;
12
+ export declare class FileDescriptor {
13
+ #private;
14
+ readonly fd: number;
15
+ readonly size: number;
16
+ readonly byteLength: number;
17
+ readonly runtime?: ConnectionRuntime;
18
+ readonly kind?: SharedMemoryMapping["kind"];
19
+ readonly baseAddressMod64?: number;
20
+ constructor(metadata: FileDescriptorMetadata, mapping?: SharedMemoryMapping);
21
+ static fromMapping(mapping: SharedMemoryMapping): FileDescriptor;
22
+ static fromMetadata(metadata: unknown): FileDescriptor;
23
+ static parse(serialized: string): FileDescriptor;
24
+ toMetadata(): FileDescriptorMetadata;
25
+ toJSON(): FileDescriptorMetadata;
26
+ stringify(): string;
27
+ stringifyMetadata(): string;
28
+ toString(): string;
29
+ attach(mapping: SharedMemoryMapping): this;
30
+ get mapping(): SharedMemoryMapping | undefined;
31
+ map(mapper: FileDescriptorMapper): SharedMemoryMapping;
32
+ getBuffer(mapper?: FileDescriptorMapper): SharedMemoryBuffer;
33
+ getSharedArrayBuffer(mapper?: FileDescriptorMapper): SharedArrayBuffer;
34
+ getSAB(mapper?: FileDescriptorMapper): SharedArrayBuffer;
35
+ }
36
+ export declare const parseFileDescriptorMetadata: (input: unknown) => FileDescriptorMetadata;
37
+ export {};
@@ -0,0 +1,139 @@
1
+ import { expectFd, expectPositiveSize, requireSharedArrayBuffer, } from "./types.js";
2
+ const isRecord = (value) => typeof value === "object" && value !== null;
3
+ const readOptionalRuntime = (value) => {
4
+ if (value === undefined)
5
+ return undefined;
6
+ if (value === "node" || value === "deno" || value === "bun")
7
+ return value;
8
+ throw new TypeError("file descriptor runtime is invalid");
9
+ };
10
+ const readOptionalKind = (value) => {
11
+ if (value === undefined)
12
+ return undefined;
13
+ if (value === "shared-array-buffer" ||
14
+ value === "external-array-buffer") {
15
+ return value;
16
+ }
17
+ throw new TypeError("file descriptor buffer kind is invalid");
18
+ };
19
+ const readOptionalNumber = (value, label) => {
20
+ if (value === undefined)
21
+ return undefined;
22
+ if (typeof value !== "number" || !Number.isFinite(value)) {
23
+ throw new TypeError(`file descriptor ${label} must be a finite number`);
24
+ }
25
+ return Math.trunc(value);
26
+ };
27
+ export class FileDescriptor {
28
+ fd;
29
+ size;
30
+ byteLength;
31
+ runtime;
32
+ kind;
33
+ baseAddressMod64;
34
+ #mapping;
35
+ constructor(metadata, mapping) {
36
+ this.fd = expectFd(metadata.fd);
37
+ this.size = expectPositiveSize(metadata.size);
38
+ this.byteLength = expectPositiveSize(metadata.byteLength);
39
+ this.runtime = metadata.runtime;
40
+ this.kind = metadata.kind;
41
+ this.baseAddressMod64 = metadata.baseAddressMod64;
42
+ this.#mapping = mapping;
43
+ }
44
+ static fromMapping(mapping) {
45
+ return new FileDescriptor({
46
+ version: 1,
47
+ fd: mapping.fd,
48
+ size: mapping.size,
49
+ byteLength: mapping.byteLength,
50
+ runtime: mapping.runtime,
51
+ kind: mapping.kind,
52
+ baseAddressMod64: mapping.baseAddressMod64,
53
+ }, mapping);
54
+ }
55
+ static fromMetadata(metadata) {
56
+ return new FileDescriptor(parseFileDescriptorMetadata(metadata));
57
+ }
58
+ static parse(serialized) {
59
+ return FileDescriptor.fromMetadata(serialized);
60
+ }
61
+ toMetadata() {
62
+ return {
63
+ version: 1,
64
+ fd: this.fd,
65
+ size: this.size,
66
+ byteLength: this.byteLength,
67
+ runtime: this.runtime,
68
+ kind: this.kind,
69
+ baseAddressMod64: this.baseAddressMod64,
70
+ };
71
+ }
72
+ toJSON() {
73
+ return this.toMetadata();
74
+ }
75
+ stringify() {
76
+ return JSON.stringify(this.toMetadata());
77
+ }
78
+ stringifyMetadata() {
79
+ // This describes an fd; it does not transfer fd ownership to another process.
80
+ return this.stringify();
81
+ }
82
+ toString() {
83
+ return this.stringify();
84
+ }
85
+ attach(mapping) {
86
+ this.#mapping = mapping;
87
+ return this;
88
+ }
89
+ get mapping() {
90
+ return this.#mapping;
91
+ }
92
+ map(mapper) {
93
+ const options = {
94
+ fd: this.fd,
95
+ size: this.size,
96
+ };
97
+ this.#mapping = mapper.mapSharedMemory(options);
98
+ return this.#mapping;
99
+ }
100
+ getBuffer(mapper) {
101
+ if (this.#mapping?.buffer !== undefined)
102
+ return this.#mapping.buffer;
103
+ if (mapper === undefined) {
104
+ throw new TypeError("file descriptor is not attached to a shared memory mapping");
105
+ }
106
+ return this.map(mapper).buffer;
107
+ }
108
+ getSharedArrayBuffer(mapper) {
109
+ if (this.#mapping?.sab !== undefined)
110
+ return this.#mapping.sab;
111
+ if (mapper === undefined) {
112
+ throw new TypeError("file descriptor is not attached to a SharedArrayBuffer mapping");
113
+ }
114
+ return requireSharedArrayBuffer(this.map(mapper));
115
+ }
116
+ getSAB(mapper) {
117
+ return this.getSharedArrayBuffer(mapper);
118
+ }
119
+ }
120
+ export const parseFileDescriptorMetadata = (input) => {
121
+ const value = typeof input === "string" ? JSON.parse(input) : input;
122
+ if (!isRecord(value)) {
123
+ throw new TypeError("file descriptor metadata must be an object");
124
+ }
125
+ if (value.version !== 1) {
126
+ throw new TypeError("unsupported file descriptor metadata version");
127
+ }
128
+ return {
129
+ version: 1,
130
+ fd: expectFd(readOptionalNumber(value.fd, "fd") ?? -1),
131
+ size: expectPositiveSize(readOptionalNumber(value.size, "size") ?? 0),
132
+ byteLength: expectPositiveSize(readOptionalNumber(value.byteLength, "byteLength") ??
133
+ readOptionalNumber(value.size, "size") ??
134
+ 0),
135
+ runtime: readOptionalRuntime(value.runtime),
136
+ kind: readOptionalKind(value.kind),
137
+ baseAddressMod64: readOptionalNumber(value.baseAddressMod64, "baseAddressMod64"),
138
+ };
139
+ };
@@ -0,0 +1,3 @@
1
+ export { alignToCacheLine, CACHE_LINE_SIZE, type ConnectionRuntime, type CreateSharedMemoryOptions, expectFd, expectPositiveSize, type MapSharedMemoryOptions, requireSharedArrayBuffer, type SharedMemoryBuffer, type SharedMemoryBufferKind, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping, } from "./types.js";
2
+ export { FileDescriptor, type FileDescriptorMetadata, parseFileDescriptorMetadata, } from "./file-descriptor.js";
3
+ export { getDefaultProcessSharedBufferPrimitives, parseProcessSharedBufferMetadata, PROCESS_SHARED_BUFFER_BRAND, ProcessSharedBuffer, type ProcessSharedBufferCreator, type ProcessSharedBufferMapper, type ProcessSharedBufferMetadata, type ProcessSharedBufferPrimitives, type ProcessSharedBufferRange, type ProcessSharedBufferView, type ProcessSharedBufferViewConstructor, setDefaultProcessSharedBufferPrimitives, } from "./process-shared-buffer.js";
@@ -0,0 +1,3 @@
1
+ export { alignToCacheLine, CACHE_LINE_SIZE, expectFd, expectPositiveSize, requireSharedArrayBuffer, } from "./types.js";
2
+ export { FileDescriptor, parseFileDescriptorMetadata, } from "./file-descriptor.js";
3
+ export { getDefaultProcessSharedBufferPrimitives, parseProcessSharedBufferMetadata, PROCESS_SHARED_BUFFER_BRAND, ProcessSharedBuffer, setDefaultProcessSharedBufferPrimitives, } from "./process-shared-buffer.js";
@@ -0,0 +1,5 @@
1
+ type NodeRequire = (specifier: string) => unknown;
2
+ export type NodeNativeAddonName = "knitting_shared_memory" | "knitting_shm";
3
+ export declare const nodeNativeAddonSpecifiers: (name: NodeNativeAddonName) => readonly string[];
4
+ export declare const loadNodeNativeAddon: <T>(require: NodeRequire, name: NodeNativeAddonName, specifier?: string) => T;
5
+ export {};
@@ -0,0 +1,43 @@
1
+ const readNodePlatformInfo = () => {
2
+ const processLike = globalThis.process;
3
+ if (typeof processLike?.platform !== "string" ||
4
+ typeof processLike.arch !== "string") {
5
+ return undefined;
6
+ }
7
+ return {
8
+ arch: processLike.arch,
9
+ modules: processLike.versions?.modules,
10
+ platform: processLike.platform,
11
+ };
12
+ };
13
+ export const nodeNativeAddonSpecifiers = (name) => {
14
+ const platformInfo = readNodePlatformInfo();
15
+ const fallback = `../../build/Release/${name}.node`;
16
+ if (platformInfo === undefined)
17
+ return [fallback];
18
+ const nodeModuleAbi = platformInfo.modules;
19
+ if (typeof nodeModuleAbi === "string" && nodeModuleAbi.length > 0) {
20
+ return [
21
+ `../../prebuilds/${platformInfo.platform}-${platformInfo.arch}-node-${nodeModuleAbi}/${name}.node`,
22
+ fallback,
23
+ ];
24
+ }
25
+ return [
26
+ `../../prebuilds/${platformInfo.platform}-${platformInfo.arch}/${name}.node`,
27
+ fallback,
28
+ ];
29
+ };
30
+ export const loadNodeNativeAddon = (require, name, specifier) => {
31
+ if (specifier !== undefined)
32
+ return require(specifier);
33
+ const errors = [];
34
+ for (const candidate of nodeNativeAddonSpecifiers(name)) {
35
+ try {
36
+ return require(candidate);
37
+ }
38
+ catch (error) {
39
+ errors.push(`${candidate}: ${String(error)}`);
40
+ }
41
+ }
42
+ throw new Error(`Could not load native addon ${name}. Tried:\n${errors.join("\n")}`);
43
+ };
@@ -0,0 +1,29 @@
1
+ import { type CreateSharedMemoryOptions, type MapSharedMemoryOptions, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
2
+ export type NodeSharedMemoryNativeMapping = {
3
+ sab: SharedArrayBuffer;
4
+ fd: number;
5
+ size: number;
6
+ baseAddressMod64?: number;
7
+ };
8
+ export type NodeSharedMemoryAddon = {
9
+ createSharedMemory: (size: number, name?: string, mode?: CreateSharedMemoryOptions["mode"]) => NodeSharedMemoryNativeMapping;
10
+ mapSharedMemory: (fd: number, size: number) => NodeSharedMemoryNativeMapping;
11
+ unlinkSharedMemory?: (name: string) => boolean;
12
+ };
13
+ export type NodeFutexWaitResult = "woken" | "changed" | "interrupted" | "timed-out";
14
+ export type NodeFutexAddon = {
15
+ waitU32: (buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, expected: number, timeoutMs?: number) => NodeFutexWaitResult;
16
+ wakeU32: (buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, count?: number) => number;
17
+ notifyU32?: (buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, count?: number) => number;
18
+ sleep?: (milliseconds?: number) => void;
19
+ yield?: () => void;
20
+ };
21
+ export declare const DEFAULT_NODE_SHARED_MEMORY_ADDON = "../../build/Release/knitting_shared_memory.node";
22
+ export declare const DEFAULT_NODE_FUTEX_ADDON = "../../build/Release/knitting_shm.node";
23
+ export declare const loadNodeSharedMemoryAddon: (specifier?: string) => NodeSharedMemoryAddon;
24
+ export declare const loadNodeFutexAddon: (specifier?: string) => NodeFutexAddon;
25
+ export declare const fromNodeNativeMapping: (mapped: NodeSharedMemoryNativeMapping) => SharedMemoryMapping<SharedArrayBuffer>;
26
+ export declare const createNodeSharedMemory: (options: number | CreateSharedMemoryOptions, addon?: NodeSharedMemoryAddon) => SharedMemoryMapping<SharedArrayBuffer>;
27
+ export declare const mapNodeSharedMemory: (options: MapSharedMemoryOptions, addon?: NodeSharedMemoryAddon) => SharedMemoryMapping<SharedArrayBuffer>;
28
+ export declare const createNodeConnectionPrimitives: (addon?: NodeSharedMemoryAddon) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<SharedArrayBuffer>>;
29
+ export declare const unlinkNodeSharedMemory: (name: string, addon?: NodeSharedMemoryAddon) => boolean;
@@ -0,0 +1,59 @@
1
+ import { getNodeBuiltinModule } from "../common/node-compat.js";
2
+ import { loadNodeNativeAddon } from "./node-addons.js";
3
+ import { assertPosixSharedMemoryPlatform } from "./posix.js";
4
+ import { expectFd, expectPositiveSize, readCreateMode, readCreateName, readRequiredCreateName, readCreateSize, } from "./types.js";
5
+ export const DEFAULT_NODE_SHARED_MEMORY_ADDON = "../../build/Release/knitting_shared_memory.node";
6
+ export const DEFAULT_NODE_FUTEX_ADDON = "../../build/Release/knitting_shm.node";
7
+ export const loadNodeSharedMemoryAddon = (specifier) => {
8
+ assertPosixSharedMemoryPlatform("Node native shared memory");
9
+ const nodeModule = getNodeBuiltinModule("node:module");
10
+ if (nodeModule === undefined) {
11
+ throw new Error("Node shared memory addon can only be loaded in Node");
12
+ }
13
+ const require = nodeModule.createRequire(import.meta.url);
14
+ return loadNodeNativeAddon(require, "knitting_shared_memory", specifier);
15
+ };
16
+ export const loadNodeFutexAddon = (specifier) => {
17
+ assertPosixSharedMemoryPlatform("Node native futex helpers");
18
+ const nodeModule = getNodeBuiltinModule("node:module");
19
+ if (nodeModule === undefined) {
20
+ throw new Error("Node futex addon can only be loaded in Node");
21
+ }
22
+ const require = nodeModule.createRequire(import.meta.url);
23
+ return loadNodeNativeAddon(require, "knitting_shm", specifier);
24
+ };
25
+ export const fromNodeNativeMapping = (mapped) => ({
26
+ runtime: "node",
27
+ fd: mapped.fd,
28
+ size: mapped.size,
29
+ byteLength: mapped.sab.byteLength,
30
+ buffer: mapped.sab,
31
+ kind: "shared-array-buffer",
32
+ sab: mapped.sab,
33
+ baseAddressMod64: mapped.baseAddressMod64,
34
+ });
35
+ export const createNodeSharedMemory = (options, addon = loadNodeSharedMemoryAddon()) => {
36
+ const size = expectPositiveSize(readCreateSize(options));
37
+ const mode = readCreateMode(options);
38
+ const name = mode === "anonymous"
39
+ ? readCreateName(options, "knitting_shared_memory")
40
+ : readRequiredCreateName(options);
41
+ return fromNodeNativeMapping(addon.createSharedMemory(size, name, mode));
42
+ };
43
+ export const mapNodeSharedMemory = (options, addon = loadNodeSharedMemoryAddon()) => {
44
+ const fd = expectFd(options.fd);
45
+ const size = expectPositiveSize(options.size);
46
+ return fromNodeNativeMapping(addon.mapSharedMemory(fd, size));
47
+ };
48
+ export const createNodeConnectionPrimitives = (addon = loadNodeSharedMemoryAddon()) => ({
49
+ runtime: "node",
50
+ createSharedMemory: (options) => createNodeSharedMemory(options, addon),
51
+ mapSharedMemory: (options) => mapNodeSharedMemory(options, addon),
52
+ unlinkSharedMemory: (name) => unlinkNodeSharedMemory(name, addon),
53
+ });
54
+ export const unlinkNodeSharedMemory = (name, addon = loadNodeSharedMemoryAddon()) => {
55
+ if (typeof addon.unlinkSharedMemory !== "function") {
56
+ throw new Error("Node shared memory addon cannot unlink named mappings");
57
+ }
58
+ return addon.unlinkSharedMemory(name);
59
+ };
@@ -0,0 +1,31 @@
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 toPosixSharedMemoryName: (name: string) => string;
27
+ export declare const detectPosixPlatform: () => PosixPlatform;
28
+ export declare const assertPosixSharedMemoryPlatform: (feature: string) => void;
29
+ export declare const getPosixLibcPath: (platform?: PosixPlatform) => string;
30
+ export declare const makeDarwinSharedMemoryName: (_name: string, runtime: string) => string;
31
+ export {};
@@ -0,0 +1,71 @@
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
+ export const toPosixSharedMemoryName = (name) => {
31
+ if (name.length === 0) {
32
+ throw new TypeError("shared memory name must be non-empty");
33
+ }
34
+ if (name.includes("\0")) {
35
+ throw new TypeError("shared memory name must not contain NUL bytes");
36
+ }
37
+ const out = name.startsWith("/") ? name : `/${name}`;
38
+ if (out.length < 2 || out.slice(1).includes("/")) {
39
+ throw new TypeError("POSIX shared memory name must not contain path separators");
40
+ }
41
+ return out;
42
+ };
43
+ export const detectPosixPlatform = () => {
44
+ const denoOs = globalThis.Deno?.build?.os;
45
+ if (denoOs === "darwin" || denoOs === "linux")
46
+ return denoOs;
47
+ const processPlatform = globalThis.process?.platform;
48
+ if (processPlatform === "darwin" || processPlatform === "linux") {
49
+ return processPlatform;
50
+ }
51
+ throw new Error("shared memory connections support Linux and macOS only");
52
+ };
53
+ export const assertPosixSharedMemoryPlatform = (feature) => {
54
+ try {
55
+ detectPosixPlatform();
56
+ }
57
+ catch {
58
+ throw new Error(`${feature} is supported on Linux and macOS only`);
59
+ }
60
+ };
61
+ export const getPosixLibcPath = (platform = detectPosixPlatform()) => platform === "darwin" ? DARWIN_LIBSYSTEM : LINUX_LIBC_SO;
62
+ export const makeDarwinSharedMemoryName = (_name, runtime) => {
63
+ const processId = globalThis.process?.pid ??
64
+ globalThis.Deno?.pid ??
65
+ 0;
66
+ const runtimeTag = runtime.slice(0, 1) || "x";
67
+ const pidTag = Math.abs(processId).toString(36).slice(-5);
68
+ const timeTag = Date.now().toString(36).slice(-6);
69
+ const nonce = Math.random().toString(36).slice(2, 6);
70
+ return `/knit_${runtimeTag}_${pidTag}_${timeTag}_${nonce}`;
71
+ };
@@ -0,0 +1,67 @@
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
+ export type ProcessSharedBufferView = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float32Array | Float64Array;
31
+ export type ProcessSharedBufferViewConstructor<View extends ProcessSharedBufferView = ProcessSharedBufferView> = {
32
+ readonly BYTES_PER_ELEMENT: number;
33
+ new (buffer: SharedBuffer, byteOffset: number, length: number): View;
34
+ };
35
+ export declare const setDefaultProcessSharedBufferPrimitives: (primitives: ProcessSharedBufferPrimitives | undefined) => void;
36
+ export declare const getDefaultProcessSharedBufferPrimitives: () => ProcessSharedBufferPrimitives;
37
+ export declare class ProcessSharedBuffer {
38
+ readonly [PROCESS_SHARED_BUFFER_BRAND] = true;
39
+ readonly [EXTERNAL_PAYLOAD_BRAND] = "knitting.processSharedBuffer";
40
+ readonly descriptor: FileDescriptor;
41
+ readonly byteOffset: number;
42
+ readonly byteLength: number;
43
+ constructor(descriptor: FileDescriptor, range?: ProcessSharedBufferRange);
44
+ static create(options: number | CreateSharedMemoryOptions, creator?: ProcessSharedBufferCreator): ProcessSharedBuffer;
45
+ static fromMapping(mapping: SharedMemoryMapping): ProcessSharedBuffer;
46
+ static fromDescriptor(descriptor: FileDescriptor, range?: ProcessSharedBufferRange): ProcessSharedBuffer;
47
+ static fromMetadata(metadata: unknown): ProcessSharedBuffer;
48
+ static parse(serialized: string): ProcessSharedBuffer;
49
+ static [PROCESS_SHARED_BUFFER_NUMERIC_TRANSFER](metadata: ProcessSharedBufferNumericMetadata): ProcessSharedBuffer;
50
+ get fd(): number;
51
+ get size(): number;
52
+ subbuffer(byteOffset: number, byteLength?: number): ProcessSharedBuffer;
53
+ getSharedArrayBuffer(mapper?: ProcessSharedBufferMapper): SharedArrayBuffer;
54
+ getSAB(mapper?: ProcessSharedBufferMapper): SharedArrayBuffer;
55
+ getBuffer(mapper?: ProcessSharedBufferMapper): SharedBuffer;
56
+ getRegion(mapper?: ProcessSharedBufferMapper): SharedBufferRegion;
57
+ view<View extends ProcessSharedBufferView>(constructor: ProcessSharedBufferViewConstructor<View>, mapper?: ProcessSharedBufferMapper): View;
58
+ bytes(mapper?: ProcessSharedBufferMapper): Uint8Array;
59
+ dataView(mapper?: ProcessSharedBufferMapper): DataView;
60
+ toMetadata(): ProcessSharedBufferMetadata;
61
+ toJSON(): ProcessSharedBufferMetadata;
62
+ stringify(): string;
63
+ stringifyMetadata(): string;
64
+ toString(): string;
65
+ }
66
+ export declare const parseProcessSharedBufferMetadata: (input: unknown) => ProcessSharedBufferMetadata;
67
+ export {};