knitting 0.1.55 → 0.1.60

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 (157) hide show
  1. package/README.md +254 -40
  2. package/knitting.d.ts +7 -797
  3. package/knitting.js +6 -9780
  4. package/map.md +31 -15
  5. package/package.json +20 -6
  6. package/process-shared-buffer.d.ts +1 -0
  7. package/process-shared-buffer.js +1 -0
  8. package/scripts/build-compiled-worker.ts +276 -0
  9. package/scripts/compiled-worker/porffor.d.ts +5 -0
  10. package/scripts/compiled-worker/runtime.ts +529 -0
  11. package/scripts/compiled-worker/task-shim.ts +32 -0
  12. package/shared-memory.d.ts +1 -155
  13. package/shared-memory.js +1 -1342
  14. package/src/api.d.ts +92 -0
  15. package/src/api.js +798 -0
  16. package/src/common/envelope.d.ts +17 -0
  17. package/src/common/envelope.js +22 -0
  18. package/src/common/module-url.d.ts +2 -0
  19. package/src/common/module-url.js +52 -0
  20. package/src/common/node-compat.d.ts +22 -0
  21. package/src/common/node-compat.js +24 -0
  22. package/src/common/path-canonical.d.ts +6 -0
  23. package/src/common/path-canonical.js +55 -0
  24. package/src/common/runtime.d.ts +16 -0
  25. package/src/common/runtime.js +105 -0
  26. package/src/common/shared-buffer-region.d.ts +11 -0
  27. package/src/common/shared-buffer-region.js +21 -0
  28. package/src/common/shared-buffer-text.d.ts +16 -0
  29. package/src/common/shared-buffer-text.js +65 -0
  30. package/src/common/task-source.d.ts +5 -0
  31. package/src/common/task-source.js +100 -0
  32. package/src/common/task-symbol.d.ts +1 -0
  33. package/src/common/task-symbol.js +1 -0
  34. package/src/common/with-resolvers.d.ts +9 -0
  35. package/src/common/with-resolvers.js +23 -0
  36. package/src/common/worker-runtime.d.ts +42 -0
  37. package/src/common/worker-runtime.js +70 -0
  38. package/src/connections/buffer-reference-native.d.ts +56 -0
  39. package/src/connections/buffer-reference-native.js +241 -0
  40. package/src/connections/buffer-reference.d.ts +99 -0
  41. package/src/connections/buffer-reference.js +575 -0
  42. package/src/connections/bun.d.ts +22 -0
  43. package/src/connections/bun.js +225 -0
  44. package/src/connections/deno.d.ts +24 -0
  45. package/src/connections/deno.js +226 -0
  46. package/src/connections/external-array-buffer.d.ts +2 -0
  47. package/src/connections/external-array-buffer.js +33 -0
  48. package/src/connections/file-descriptor.d.ts +39 -0
  49. package/src/connections/file-descriptor.js +161 -0
  50. package/src/connections/index.d.ts +4 -0
  51. package/src/connections/index.js +4 -0
  52. package/src/connections/node-addons.d.ts +16 -0
  53. package/src/connections/node-addons.js +92 -0
  54. package/src/connections/node-buffer-pointer.d.ts +20 -0
  55. package/src/connections/node-buffer-pointer.js +16 -0
  56. package/src/connections/node-ffi-api.d.ts +19 -0
  57. package/src/connections/node-ffi-api.js +28 -0
  58. package/src/connections/node-ffi.d.ts +27 -0
  59. package/src/connections/node-ffi.js +262 -0
  60. package/src/connections/node.d.ts +30 -0
  61. package/src/connections/node.js +62 -0
  62. package/src/connections/numeric-array.d.ts +5 -0
  63. package/src/connections/numeric-array.js +17 -0
  64. package/src/connections/package-assets.d.ts +2 -0
  65. package/src/connections/package-assets.js +55 -0
  66. package/src/connections/posix.d.ts +74 -0
  67. package/src/connections/posix.js +178 -0
  68. package/src/connections/process-shared-buffer.d.ts +76 -0
  69. package/src/connections/process-shared-buffer.js +245 -0
  70. package/src/connections/shared-array-buffer-payload.d.ts +36 -0
  71. package/src/connections/shared-array-buffer-payload.js +235 -0
  72. package/src/connections/types.d.ts +54 -0
  73. package/src/connections/types.js +37 -0
  74. package/src/connections/windows.d.ts +37 -0
  75. package/src/connections/windows.js +245 -0
  76. package/src/debug/env-diff.d.ts +26 -0
  77. package/src/debug/env-diff.js +49 -0
  78. package/src/debug/gate.d.ts +18 -0
  79. package/src/debug/gate.js +69 -0
  80. package/src/debug/handle.d.ts +23 -0
  81. package/src/debug/handle.js +48 -0
  82. package/src/error.d.ts +14 -0
  83. package/src/error.js +50 -0
  84. package/src/ipc/tools/ring-queue.d.ts +33 -0
  85. package/src/ipc/tools/ring-queue.js +159 -0
  86. package/src/ipc/transport/shared-memory.d.ts +23 -0
  87. package/src/ipc/transport/shared-memory.js +35 -0
  88. package/src/memory/byte-carpet.d.ts +73 -0
  89. package/src/memory/byte-carpet.js +157 -0
  90. package/src/memory/lock.d.ts +213 -0
  91. package/src/memory/lock.js +760 -0
  92. package/src/memory/payload-config.d.ts +31 -0
  93. package/src/memory/payload-config.js +67 -0
  94. package/src/memory/payloadCodec.d.ts +55 -0
  95. package/src/memory/payloadCodec.js +1473 -0
  96. package/src/memory/regionRegistry.d.ts +17 -0
  97. package/src/memory/regionRegistry.js +285 -0
  98. package/src/memory/shared-buffer-io.d.ts +55 -0
  99. package/src/memory/shared-buffer-io.js +403 -0
  100. package/src/permission/compatibility.d.ts +28 -0
  101. package/src/permission/compatibility.js +239 -0
  102. package/src/permission/index.d.ts +4 -0
  103. package/src/permission/index.js +3 -0
  104. package/src/permission/protocol.d.ts +167 -0
  105. package/src/permission/protocol.js +690 -0
  106. package/src/runtime/balancer.d.ts +19 -0
  107. package/src/runtime/balancer.js +149 -0
  108. package/src/runtime/compiled-artifact.d.ts +21 -0
  109. package/src/runtime/compiled-artifact.js +260 -0
  110. package/src/runtime/compiled-builder.d.ts +8 -0
  111. package/src/runtime/compiled-builder.js +42 -0
  112. package/src/runtime/compiled-worker.d.ts +27 -0
  113. package/src/runtime/compiled-worker.js +562 -0
  114. package/src/runtime/dispatcher.d.ts +40 -0
  115. package/src/runtime/dispatcher.js +142 -0
  116. package/src/runtime/inline-executor.d.ts +10 -0
  117. package/src/runtime/inline-executor.js +294 -0
  118. package/src/runtime/pool.d.ts +44 -0
  119. package/src/runtime/pool.js +472 -0
  120. package/src/runtime/process-worker.d.ts +92 -0
  121. package/src/runtime/process-worker.js +713 -0
  122. package/src/runtime/tx-queue.d.ts +28 -0
  123. package/src/runtime/tx-queue.js +158 -0
  124. package/src/shared/abortSignal.d.ts +23 -0
  125. package/src/shared/abortSignal.js +134 -0
  126. package/src/types.d.ts +399 -0
  127. package/src/types.js +2 -0
  128. package/src/utils/http.d.ts +29 -0
  129. package/src/utils/http.js +100 -0
  130. package/src/worker/bootstrap.d.ts +5 -0
  131. package/src/worker/bootstrap.js +78 -0
  132. package/src/worker/composable-runners.d.ts +12 -0
  133. package/src/worker/composable-runners.js +98 -0
  134. package/src/worker/loop.d.ts +3 -0
  135. package/src/worker/loop.js +395 -0
  136. package/src/worker/process-worker-bootstrap.d.ts +8 -0
  137. package/src/worker/process-worker-bootstrap.js +160 -0
  138. package/src/worker/rx-queue.d.ts +25 -0
  139. package/src/worker/rx-queue.js +173 -0
  140. package/src/worker/safety/index.d.ts +4 -0
  141. package/src/worker/safety/index.js +4 -0
  142. package/src/worker/safety/performance.d.ts +1 -0
  143. package/src/worker/safety/performance.js +17 -0
  144. package/src/worker/safety/process.d.ts +2 -0
  145. package/src/worker/safety/process.js +79 -0
  146. package/src/worker/safety/startup.d.ts +16 -0
  147. package/src/worker/safety/startup.js +30 -0
  148. package/src/worker/safety/worker-data.d.ts +2 -0
  149. package/src/worker/safety/worker-data.js +36 -0
  150. package/src/worker/task-loader.d.ts +28 -0
  151. package/src/worker/task-loader.js +76 -0
  152. package/src/worker/timers.d.ts +18 -0
  153. package/src/worker/timers.js +118 -0
  154. package/unsafe.d.ts +1 -71
  155. package/unsafe.js +1 -878
  156. package/utils.d.ts +1 -31
  157. package/utils.js +1 -116
@@ -0,0 +1,161 @@
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
+ const readOptionalName = (value) => {
28
+ if (value === undefined)
29
+ return undefined;
30
+ if (typeof value !== "string" || value.length === 0) {
31
+ throw new TypeError("file descriptor name must be a non-empty string when provided");
32
+ }
33
+ if (value.includes("\0")) {
34
+ throw new TypeError("file descriptor name must not contain NUL bytes");
35
+ }
36
+ return value;
37
+ };
38
+ export class FileDescriptor {
39
+ fd;
40
+ name;
41
+ size;
42
+ byteLength;
43
+ runtime;
44
+ kind;
45
+ baseAddressMod64;
46
+ #mapping;
47
+ constructor(metadata, mapping) {
48
+ this.fd = expectFd(metadata.fd);
49
+ this.name = metadata.name;
50
+ this.size = expectPositiveSize(metadata.size);
51
+ this.byteLength = expectPositiveSize(metadata.byteLength);
52
+ this.runtime = metadata.runtime;
53
+ this.kind = metadata.kind;
54
+ this.baseAddressMod64 = metadata.baseAddressMod64;
55
+ this.#mapping = mapping;
56
+ }
57
+ static fromMapping(mapping) {
58
+ return new FileDescriptor({
59
+ version: 1,
60
+ fd: mapping.fd,
61
+ name: mapping.name,
62
+ size: mapping.size,
63
+ byteLength: mapping.byteLength,
64
+ runtime: mapping.runtime,
65
+ kind: mapping.kind,
66
+ baseAddressMod64: mapping.baseAddressMod64,
67
+ }, mapping);
68
+ }
69
+ static fromMetadata(metadata) {
70
+ return new FileDescriptor(parseFileDescriptorMetadata(metadata));
71
+ }
72
+ static parse(serialized) {
73
+ return FileDescriptor.fromMetadata(serialized);
74
+ }
75
+ toMetadata() {
76
+ const metadata = {
77
+ version: 1,
78
+ fd: this.fd,
79
+ size: this.size,
80
+ byteLength: this.byteLength,
81
+ runtime: this.runtime,
82
+ kind: this.kind,
83
+ baseAddressMod64: this.baseAddressMod64,
84
+ };
85
+ if (this.name !== undefined)
86
+ metadata.name = this.name;
87
+ return metadata;
88
+ }
89
+ toJSON() {
90
+ return this.toMetadata();
91
+ }
92
+ stringify() {
93
+ return JSON.stringify(this.toMetadata());
94
+ }
95
+ stringifyMetadata() {
96
+ // This describes an fd; it does not transfer fd ownership to another process.
97
+ return this.stringify();
98
+ }
99
+ toString() {
100
+ return this.stringify();
101
+ }
102
+ attach(mapping) {
103
+ this.#mapping = mapping;
104
+ return this;
105
+ }
106
+ get mapping() {
107
+ return this.#mapping;
108
+ }
109
+ map(mapper) {
110
+ const options = {
111
+ fd: this.fd,
112
+ name: this.name,
113
+ size: this.size,
114
+ };
115
+ this.#mapping = mapper.mapSharedMemory(options);
116
+ return this.#mapping;
117
+ }
118
+ getBuffer(mapper) {
119
+ if (this.#mapping?.buffer !== undefined)
120
+ return this.#mapping.buffer;
121
+ if (mapper === undefined) {
122
+ throw new TypeError("file descriptor is not attached to a shared memory mapping");
123
+ }
124
+ return this.map(mapper).buffer;
125
+ }
126
+ getSharedArrayBuffer(mapper) {
127
+ if (this.#mapping?.sab !== undefined)
128
+ return this.#mapping.sab;
129
+ if (mapper === undefined) {
130
+ throw new TypeError("file descriptor is not attached to a SharedArrayBuffer mapping");
131
+ }
132
+ return requireSharedArrayBuffer(this.map(mapper));
133
+ }
134
+ getSAB(mapper) {
135
+ return this.getSharedArrayBuffer(mapper);
136
+ }
137
+ }
138
+ export const parseFileDescriptorMetadata = (input) => {
139
+ const value = typeof input === "string" ? JSON.parse(input) : input;
140
+ if (!isRecord(value)) {
141
+ throw new TypeError("file descriptor metadata must be an object");
142
+ }
143
+ if (value.version !== 1) {
144
+ throw new TypeError("unsupported file descriptor metadata version");
145
+ }
146
+ const parsed = {
147
+ version: 1,
148
+ fd: expectFd(readOptionalNumber(value.fd, "fd") ?? -1),
149
+ size: expectPositiveSize(readOptionalNumber(value.size, "size") ?? 0),
150
+ byteLength: expectPositiveSize(readOptionalNumber(value.byteLength, "byteLength") ??
151
+ readOptionalNumber(value.size, "size") ??
152
+ 0),
153
+ runtime: readOptionalRuntime(value.runtime),
154
+ kind: readOptionalKind(value.kind),
155
+ baseAddressMod64: readOptionalNumber(value.baseAddressMod64, "baseAddressMod64"),
156
+ };
157
+ const name = readOptionalName(value.name);
158
+ if (name !== undefined)
159
+ parsed.name = name;
160
+ return parsed;
161
+ };
@@ -0,0 +1,4 @@
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 { BufferReference, BUFFER_REFERENCE_KIND, type BufferReferenceMetadata, type BufferReferenceRuntime, isBufferReferenceMetadata, } from "./buffer-reference.js";
3
+ export { FileDescriptor, type FileDescriptorMetadata, parseFileDescriptorMetadata, } from "./file-descriptor.js";
4
+ 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,4 @@
1
+ export { alignToCacheLine, CACHE_LINE_SIZE, expectFd, expectPositiveSize, requireSharedArrayBuffer, } from "./types.js";
2
+ export { BufferReference, BUFFER_REFERENCE_KIND, isBufferReferenceMetadata, } from "./buffer-reference.js";
3
+ export { FileDescriptor, parseFileDescriptorMetadata, } from "./file-descriptor.js";
4
+ export { getDefaultProcessSharedBufferPrimitives, parseProcessSharedBufferMetadata, PROCESS_SHARED_BUFFER_BRAND, ProcessSharedBuffer, setDefaultProcessSharedBufferPrimitives, } from "./process-shared-buffer.js";
@@ -0,0 +1,16 @@
1
+ type NodeRequire = (specifier: string) => unknown;
2
+ export type NodeNativeAddonName = "knitting_shared_memory" | "knitting_shm" | "knitting_buffer_pointer";
3
+ export type NodePlatformInfo = {
4
+ arch: string;
5
+ modules?: string;
6
+ platform: string;
7
+ version?: string;
8
+ };
9
+ export declare const SUPPORTED_NODE_NATIVE_ADDON_ABIS: {
10
+ readonly "127": "22";
11
+ readonly "137": "24";
12
+ };
13
+ export declare const formatNodeNativeAddonLoadError: (name: NodeNativeAddonName, platformInfo: NodePlatformInfo | undefined, errors: readonly string[]) => string;
14
+ export declare const nodeNativeAddonSpecifiers: (name: NodeNativeAddonName) => readonly string[];
15
+ export declare const loadNodeNativeAddon: <T>(require: NodeRequire, name: NodeNativeAddonName, specifier?: string) => T;
16
+ export {};
@@ -0,0 +1,92 @@
1
+ import { resolveKnittingPackageAsset } from "./package-assets.js";
2
+ export const SUPPORTED_NODE_NATIVE_ADDON_ABIS = {
3
+ "127": "22",
4
+ "137": "24",
5
+ };
6
+ const NODE_26_MODULE_ABI = "147";
7
+ const SUPPORTED_NODE_NATIVE_TARGETS = new Set([
8
+ "darwin-arm64",
9
+ "darwin-x64",
10
+ "linux-x64",
11
+ "win32-x64",
12
+ ]);
13
+ const readNodePlatformInfo = () => {
14
+ const processLike = globalThis.process;
15
+ if (typeof processLike?.platform !== "string" ||
16
+ typeof processLike.arch !== "string") {
17
+ return undefined;
18
+ }
19
+ return {
20
+ arch: processLike.arch,
21
+ modules: processLike.versions?.modules,
22
+ platform: processLike.platform,
23
+ version: processLike.versions?.node,
24
+ };
25
+ };
26
+ const formatAttemptErrors = (errors) => errors.length > 0 ? `\nTried:\n${errors.join("\n")}` : "";
27
+ export const formatNodeNativeAddonLoadError = (name, platformInfo, errors) => {
28
+ const attempts = formatAttemptErrors(errors);
29
+ if (platformInfo === undefined) {
30
+ return `knitting: could not load native addon ${name}.${attempts}`;
31
+ }
32
+ const abi = platformInfo.modules;
33
+ const version = platformInfo.version === undefined
34
+ ? "an unknown version"
35
+ : `v${platformInfo.version}`;
36
+ const target = `${platformInfo.platform}-${platformInfo.arch}`;
37
+ if (abi === NODE_26_MODULE_ABI) {
38
+ return (`knitting: Node.js 26 uses node:ffi instead of ABI-specific addons. ` +
39
+ `Restart Node with the --experimental-ffi flag` +
40
+ ` (--allow-ffi is also required when using Node's Permission Model). ` +
41
+ `No Node ABI 147 addon is shipped.${attempts}`);
42
+ }
43
+ if (typeof abi === "string" &&
44
+ !(abi in SUPPORTED_NODE_NATIVE_ADDON_ABIS)) {
45
+ return (`knitting: no native addon is shipped for Node.js ${version} ` +
46
+ `(ABI ${abi}). Packaged native features support Node.js 22 ` +
47
+ `(ABI 127) and Node.js 24 (ABI 137) only. Plain thread workers that do ` +
48
+ `not use ProcessSharedBuffer or BufferReference do not need these ` +
49
+ `addons.${attempts}`);
50
+ }
51
+ if (!SUPPORTED_NODE_NATIVE_TARGETS.has(target)) {
52
+ return (`knitting: Node.js ${version} uses a supported ABI (${abi ?? "unknown"}), ` +
53
+ `but no packaged native addon target is shipped for ${target}. ` +
54
+ `Published targets are linux-x64, darwin-x64, darwin-arm64, and ` +
55
+ `win32-x64. You can build locally with "bun run build:native".${attempts}`);
56
+ }
57
+ return (`knitting: could not load native addon ${name} for Node.js ${version} ` +
58
+ `(ABI ${abi ?? "unknown"}, ${target}). The prebuild may be missing, ` +
59
+ `damaged, or incompatible; reinstall the package or build locally with ` +
60
+ `"bun run build:native".${attempts}`);
61
+ };
62
+ export const nodeNativeAddonSpecifiers = (name) => {
63
+ const platformInfo = readNodePlatformInfo();
64
+ const fallback = resolveKnittingPackageAsset("build", "Release", `${name}.node`);
65
+ if (platformInfo === undefined)
66
+ return [fallback];
67
+ const nodeModuleAbi = platformInfo.modules;
68
+ if (typeof nodeModuleAbi === "string" && nodeModuleAbi.length > 0) {
69
+ return [
70
+ resolveKnittingPackageAsset("prebuilds", `${platformInfo.platform}-${platformInfo.arch}-node-${nodeModuleAbi}`, `${name}.node`),
71
+ fallback,
72
+ ];
73
+ }
74
+ return [
75
+ resolveKnittingPackageAsset("prebuilds", `${platformInfo.platform}-${platformInfo.arch}`, `${name}.node`),
76
+ fallback,
77
+ ];
78
+ };
79
+ export const loadNodeNativeAddon = (require, name, specifier) => {
80
+ if (specifier !== undefined)
81
+ return require(specifier);
82
+ const errors = [];
83
+ for (const candidate of nodeNativeAddonSpecifiers(name)) {
84
+ try {
85
+ return require(candidate);
86
+ }
87
+ catch (error) {
88
+ errors.push(`${candidate}: ${String(error)}`);
89
+ }
90
+ }
91
+ throw new Error(formatNodeNativeAddonLoadError(name, readNodePlatformInfo(), errors));
92
+ };
@@ -0,0 +1,20 @@
1
+ export type NodeBufferPointerAddon = {
2
+ getPointer: (buffer: ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => bigint;
3
+ retainPointer: (buffer: ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => {
4
+ pointer: bigint;
5
+ byteLength: number;
6
+ token: bigint;
7
+ };
8
+ releasePointer: (token: bigint) => boolean;
9
+ wrapPointer: (pointer: bigint, byteLength: number) => ArrayBuffer;
10
+ detachArrayBuffer: (buffer: ArrayBuffer) => boolean;
11
+ retainBackingStore?: (buffer: ArrayBufferView | ArrayBuffer) => {
12
+ pointer: bigint;
13
+ byteOffset: number;
14
+ byteLength: number;
15
+ token: bigint;
16
+ };
17
+ adoptBackingStore?: (token: bigint) => ArrayBuffer;
18
+ releaseBackingStore?: (token: bigint) => boolean;
19
+ };
20
+ export declare const loadNodeBufferPointerAddon: (specifier?: string) => NodeBufferPointerAddon;
@@ -0,0 +1,16 @@
1
+ import { getNodeBuiltinModule } from "../common/node-compat.js";
2
+ import { loadNodeNativeAddon } from "./node-addons.js";
3
+ let cached;
4
+ export const loadNodeBufferPointerAddon = (specifier) => {
5
+ if (specifier === undefined && cached !== undefined)
6
+ return cached;
7
+ const nodeModule = getNodeBuiltinModule("node:module");
8
+ if (nodeModule === undefined) {
9
+ throw new Error("Node buffer-pointer addon can only be loaded in Node");
10
+ }
11
+ const require = nodeModule.createRequire(import.meta.url);
12
+ const addon = loadNodeNativeAddon(require, "knitting_buffer_pointer", specifier);
13
+ if (specifier === undefined)
14
+ cached = addon;
15
+ return addon;
16
+ };
@@ -0,0 +1,19 @@
1
+ export type NodeFfiFunctionSignature = {
2
+ arguments?: string[];
3
+ return?: string;
4
+ };
5
+ export type NodeFfiLibrary = {
6
+ close: () => void;
7
+ };
8
+ export type NodeFfiDlopenResult<Functions extends Record<string, (...args: never[]) => unknown>> = {
9
+ functions: Functions;
10
+ lib: NodeFfiLibrary;
11
+ };
12
+ export type NodeFfiApi = {
13
+ dlopen: <Functions extends Record<string, (...args: never[]) => unknown>>(path: string | null, definitions: Record<string, NodeFfiFunctionSignature>) => NodeFfiDlopenResult<Functions>;
14
+ getRawPointer: (source: ArrayBuffer | SharedArrayBuffer | ArrayBufferView) => bigint;
15
+ suffix: string;
16
+ toArrayBuffer: (pointer: bigint, length: number, copy?: boolean) => ArrayBuffer;
17
+ };
18
+ export declare const isNodeFfiTarget: () => boolean;
19
+ export declare const getNodeFfi: () => NodeFfiApi;
@@ -0,0 +1,28 @@
1
+ import { getNodeBuiltinModule, getNodeProcess } from "../common/node-compat.js";
2
+ const readNodeMajor = () => {
3
+ const version = getNodeProcess()?.versions?.node;
4
+ if (typeof version !== "string")
5
+ return undefined;
6
+ const major = Number.parseInt(version.split(".", 1)[0] ?? "", 10);
7
+ return Number.isInteger(major) ? major : undefined;
8
+ };
9
+ export const isNodeFfiTarget = () => {
10
+ const major = readNodeMajor() ?? 0;
11
+ return major >= 26 && major % 2 === 0;
12
+ };
13
+ let cachedNodeFfi;
14
+ export const getNodeFfi = () => {
15
+ if (cachedNodeFfi !== undefined)
16
+ return cachedNodeFfi;
17
+ const ffi = getNodeBuiltinModule("node:ffi");
18
+ if (ffi === undefined ||
19
+ typeof ffi.dlopen !== "function" ||
20
+ typeof ffi.getRawPointer !== "function" ||
21
+ typeof ffi.toArrayBuffer !== "function") {
22
+ throw new Error("knitting: Node.js 26 native features require node:ffi. " +
23
+ "Restart Node with --experimental-ffi" +
24
+ " (--allow-ffi is also required when using Node's Permission Model).");
25
+ }
26
+ cachedNodeFfi = ffi;
27
+ return ffi;
28
+ };
@@ -0,0 +1,27 @@
1
+ import { type NodeFfiLibrary } from "./node-ffi-api.js";
2
+ import { type CreateSharedMemoryOptions, type MapSharedMemoryOptions, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
3
+ export { getNodeFfi, isNodeFfiTarget } from "./node-ffi-api.js";
4
+ export type { NodeFfiApi, NodeFfiDlopenResult, NodeFfiFunctionSignature, NodeFfiLibrary, } from "./node-ffi-api.js";
5
+ type NodePosixFunctions = {
6
+ memfd_create?: (name: Uint8Array, flags: number) => number;
7
+ shm_open?: (name: Uint8Array, flags: number, ...rest: number[]) => number;
8
+ shm_unlink?: (name: Uint8Array) => number;
9
+ __error?: () => bigint;
10
+ __errno_location?: () => bigint;
11
+ ftruncate: (fd: number, length: bigint) => number;
12
+ dup: (fd: number) => number;
13
+ fcntl: (fd: number, cmd: number, arg: number) => number;
14
+ mmap: (address: null, length: bigint, protection: number, flags: number, fd: number, offset: bigint) => bigint;
15
+ munmap: (address: bigint, length: bigint) => number;
16
+ close: (fd: number) => number;
17
+ };
18
+ type NodePosixLibrary = {
19
+ functions: NodePosixFunctions;
20
+ lib: NodeFfiLibrary;
21
+ };
22
+ export declare const openNodePosixLibrary: () => NodePosixLibrary;
23
+ export declare const mapNodeFfiSharedMemory: (options: MapSharedMemoryOptions, library?: NodePosixLibrary) => SharedMemoryMapping<ArrayBuffer>;
24
+ export declare const createNodeFfiSharedMemory: (options: number | CreateSharedMemoryOptions, library?: NodePosixLibrary) => SharedMemoryMapping<ArrayBuffer>;
25
+ export declare const unlinkNodeFfiSharedMemory: (name: string, library?: NodePosixLibrary) => boolean;
26
+ export declare const createNodeFfiPosixConnectionPrimitives: (library?: NodePosixLibrary) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
27
+ export declare const createNodeFfiConnectionPrimitives: () => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
@@ -0,0 +1,262 @@
1
+ import { assertNamedSharedMemoryIsReopenable, checkPosixResult, DARWIN_O_CREAT, DARWIN_O_EXCL, DARWIN_SHM_MODE, detectPosixPlatform, encodeCString, getErrnoSymbolName, getPosixLibcPath, LINUX_O_CREAT, LINUX_O_EXCL, makeDarwinSharedMemoryName, makeErrnoReader, MAP_SHARED, O_RDWR, POSIX_SHM_MODE, posixError, PROT_READ, PROT_WRITE, setCloseOnExec, SHM_OPEN_STACK_PAD, shmOpenModeIsStackPassed, toPosixSharedMemoryName, } from "./posix.js";
2
+ import { createNodeWindowsConnectionPrimitives, isWindowsRuntime, } from "./windows.js";
3
+ import { requireDetachedExternalArrayBuffer } from "./external-array-buffer.js";
4
+ import { getNodeFfi, } from "./node-ffi-api.js";
5
+ import { expectFd, expectPositiveSize, readCreateMode, readCreateName, readCreateSize, readRequiredCreateName, } from "./types.js";
6
+ export { getNodeFfi, isNodeFfiTarget } from "./node-ffi-api.js";
7
+ let cachedNodePosixLibrary;
8
+ const getNodeCreateSymbols = (platform = detectPosixPlatform()) => {
9
+ if (platform === "darwin") {
10
+ return {
11
+ shm_open: {
12
+ arguments: shmOpenModeIsStackPassed(platform)
13
+ ? ["pointer", "i32", ...SHM_OPEN_STACK_PAD.map(() => "i32"), "u32"]
14
+ : ["pointer", "i32", "u32"],
15
+ return: "i32",
16
+ },
17
+ shm_unlink: {
18
+ arguments: ["pointer"],
19
+ return: "i32",
20
+ },
21
+ };
22
+ }
23
+ return {
24
+ memfd_create: {
25
+ arguments: ["pointer", "u32"],
26
+ return: "i32",
27
+ },
28
+ shm_open: {
29
+ arguments: ["pointer", "i32", "u32"],
30
+ return: "i32",
31
+ },
32
+ shm_unlink: {
33
+ arguments: ["pointer"],
34
+ return: "i32",
35
+ },
36
+ };
37
+ };
38
+ const getNodeErrnoSymbols = (platform = detectPosixPlatform()) => ({
39
+ [getErrnoSymbolName(platform)]: {
40
+ arguments: [],
41
+ return: "pointer",
42
+ },
43
+ });
44
+ const getNodePosixSymbols = () => ({
45
+ ...getNodeCreateSymbols(),
46
+ ftruncate: {
47
+ arguments: ["i32", "i64"],
48
+ return: "i32",
49
+ },
50
+ dup: {
51
+ arguments: ["i32"],
52
+ return: "i32",
53
+ },
54
+ fcntl: {
55
+ arguments: ["i32", "i32", "i32"],
56
+ return: "i32",
57
+ },
58
+ mmap: {
59
+ arguments: ["pointer", "u64", "i32", "i32", "i32", "i64"],
60
+ return: "pointer",
61
+ },
62
+ munmap: {
63
+ arguments: ["pointer", "u64"],
64
+ return: "i32",
65
+ },
66
+ close: {
67
+ arguments: ["i32"],
68
+ return: "i32",
69
+ },
70
+ });
71
+ export const openNodePosixLibrary = () => {
72
+ if (cachedNodePosixLibrary !== undefined)
73
+ return cachedNodePosixLibrary;
74
+ const symbols = getNodePosixSymbols();
75
+ const ffi = getNodeFfi();
76
+ // errno is diagnostics only: if this libc or FFI build will not bind the
77
+ // nullary errno accessor, open without it rather than lose shared memory.
78
+ let opened;
79
+ try {
80
+ opened = ffi.dlopen(getPosixLibcPath(), {
81
+ ...symbols,
82
+ ...getNodeErrnoSymbols(),
83
+ });
84
+ }
85
+ catch {
86
+ opened = ffi.dlopen(getPosixLibcPath(), symbols);
87
+ }
88
+ cachedNodePosixLibrary = opened;
89
+ return opened;
90
+ };
91
+ const callShmOpen = (shmOpen, name, flags, mode, platform) => shmOpenModeIsStackPassed(platform)
92
+ ? shmOpen(name, flags, ...SHM_OPEN_STACK_PAD, mode)
93
+ : shmOpen(name, flags, mode);
94
+ const errnoOf = (libc, platform = detectPosixPlatform()) => makeErrnoReader(platform === "darwin" ? libc.__error : libc.__errno_location, (pointer) => new Int32Array(getNodeFfi().toArrayBuffer(pointer, 4, true))[0]);
95
+ const isMapFailed = (pointer) => pointer === 0n ||
96
+ pointer === -1n ||
97
+ pointer === BigInt.asUintN(64, -1n);
98
+ const createAnonymousFd = (name, platform, libc) => {
99
+ if (platform === "darwin") {
100
+ const shmOpen = libc.shm_open;
101
+ const shmUnlink = libc.shm_unlink;
102
+ if (shmOpen === undefined || shmUnlink === undefined) {
103
+ throw new Error("shm_open symbols are not available");
104
+ }
105
+ const shmName = encodeCString(makeDarwinSharedMemoryName(name, "node"));
106
+ const fd = checkPosixResult(callShmOpen(shmOpen, shmName, O_RDWR | DARWIN_O_CREAT | DARWIN_O_EXCL, DARWIN_SHM_MODE, platform), "shm_open failed", errnoOf(libc, platform), platform);
107
+ shmUnlink(shmName);
108
+ try {
109
+ return setCloseOnExec({ symbols: libc }, fd);
110
+ }
111
+ catch (error) {
112
+ libc.close(fd);
113
+ throw error;
114
+ }
115
+ }
116
+ const memfdCreate = libc.memfd_create;
117
+ if (memfdCreate === undefined) {
118
+ throw new Error("memfd_create symbol is not available");
119
+ }
120
+ const fd = checkPosixResult(memfdCreate(encodeCString(name), 0), "memfd_create failed", errnoOf(libc, platform), platform);
121
+ try {
122
+ return setCloseOnExec({ symbols: libc }, fd);
123
+ }
124
+ catch (error) {
125
+ libc.close(fd);
126
+ throw error;
127
+ }
128
+ };
129
+ const createNamedFd = (name, mode, platform, libc) => {
130
+ const shmOpen = libc.shm_open;
131
+ if (shmOpen === undefined) {
132
+ throw new Error("shm_open symbol is not available");
133
+ }
134
+ const createFlag = platform === "darwin" ? DARWIN_O_CREAT : LINUX_O_CREAT;
135
+ const exclusiveFlag = platform === "darwin" ? DARWIN_O_EXCL : LINUX_O_EXCL;
136
+ const flags = mode === "create"
137
+ ? O_RDWR | createFlag | exclusiveFlag
138
+ : O_RDWR;
139
+ const shmMode = platform === "darwin" ? DARWIN_SHM_MODE : POSIX_SHM_MODE;
140
+ const shmName = encodeCString(toPosixSharedMemoryName(name));
141
+ const fd = checkPosixResult(callShmOpen(shmOpen, shmName, flags, shmMode, platform), "shm_open failed", errnoOf(libc, platform), platform);
142
+ if (mode === "create") {
143
+ assertNamedSharedMemoryIsReopenable(platform, fd, () => callShmOpen(shmOpen, shmName, O_RDWR, shmMode, platform), libc.close, errnoOf(libc, platform));
144
+ }
145
+ try {
146
+ return setCloseOnExec({ symbols: libc }, fd);
147
+ }
148
+ catch (error) {
149
+ libc.close(fd);
150
+ throw error;
151
+ }
152
+ };
153
+ const mapFd = (fd, size, name, libc, closeFdOnFailure) => {
154
+ const pointer = libc.mmap(null, BigInt(size), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0n);
155
+ if (isMapFailed(pointer)) {
156
+ // errno first: fcntl below overwrites it.
157
+ const error = posixError("mmap failed", errnoOf(libc));
158
+ const descriptorFlags = libc.fcntl(fd, 1, 0);
159
+ if (closeFdOnFailure)
160
+ libc.close(fd);
161
+ error.message +=
162
+ ` (fd ${fd}, size ${size}, descriptor flags ${descriptorFlags})`;
163
+ throw error;
164
+ }
165
+ let arrayBuffer;
166
+ try {
167
+ arrayBuffer = getNodeFfi().toArrayBuffer(pointer, size, false);
168
+ }
169
+ catch (error) {
170
+ libc.munmap(pointer, BigInt(size));
171
+ if (closeFdOnFailure)
172
+ libc.close(fd);
173
+ throw error;
174
+ }
175
+ let closed = false;
176
+ return {
177
+ runtime: "node",
178
+ fd,
179
+ name,
180
+ size,
181
+ byteLength: arrayBuffer.byteLength,
182
+ buffer: arrayBuffer,
183
+ kind: "external-array-buffer",
184
+ arrayBuffer,
185
+ unsafePointer: pointer,
186
+ baseAddressMod64: Number(pointer % 64n),
187
+ close: () => {
188
+ if (closed)
189
+ return;
190
+ requireDetachedExternalArrayBuffer(arrayBuffer);
191
+ closed = true;
192
+ try {
193
+ libc.munmap(pointer, BigInt(size));
194
+ }
195
+ finally {
196
+ libc.close(fd);
197
+ }
198
+ },
199
+ };
200
+ };
201
+ export const mapNodeFfiSharedMemory = (options, library = openNodePosixLibrary()) => {
202
+ const libc = library.functions;
203
+ const size = expectPositiveSize(options.size);
204
+ let fd;
205
+ let closeFdOnFailure = true;
206
+ if (options.name !== undefined) {
207
+ fd = createNamedFd(options.name, "open", detectPosixPlatform(), libc);
208
+ }
209
+ else {
210
+ const sourceFd = expectFd(options.fd);
211
+ closeFdOnFailure = options.duplicateFd !== false;
212
+ fd = closeFdOnFailure
213
+ ? checkPosixResult(libc.dup(sourceFd), "dup(fd) failed", errnoOf(libc))
214
+ : sourceFd;
215
+ if (closeFdOnFailure) {
216
+ try {
217
+ setCloseOnExec({ symbols: libc }, fd);
218
+ }
219
+ catch (error) {
220
+ libc.close(fd);
221
+ throw error;
222
+ }
223
+ }
224
+ }
225
+ // A non-duplicated descriptor remains caller-owned until mmap and the
226
+ // JavaScript wrapper both succeed. The returned mapping owns it thereafter.
227
+ return mapFd(fd, size, options.name, libc, closeFdOnFailure);
228
+ };
229
+ export const createNodeFfiSharedMemory = (options, library = openNodePosixLibrary()) => {
230
+ const libc = library.functions;
231
+ const size = expectPositiveSize(readCreateSize(options));
232
+ const mode = readCreateMode(options);
233
+ const name = mode === "anonymous"
234
+ ? readCreateName(options, "knitting_shared_memory")
235
+ : readRequiredCreateName(options);
236
+ const platform = detectPosixPlatform();
237
+ const fd = mode === "anonymous"
238
+ ? createAnonymousFd(name, platform, libc)
239
+ : createNamedFd(name, mode, platform, libc);
240
+ if (mode !== "open" && libc.ftruncate(fd, BigInt(size)) < 0) {
241
+ const error = posixError("ftruncate failed", errnoOf(libc, platform), platform);
242
+ libc.close(fd);
243
+ throw error;
244
+ }
245
+ return mapFd(fd, size, mode === "anonymous" ? undefined : name, libc, true);
246
+ };
247
+ export const unlinkNodeFfiSharedMemory = (name, library = openNodePosixLibrary()) => {
248
+ const shmUnlink = library.functions.shm_unlink;
249
+ if (shmUnlink === undefined) {
250
+ throw new Error("shm_unlink symbol is not available");
251
+ }
252
+ return shmUnlink(encodeCString(toPosixSharedMemoryName(name))) === 0;
253
+ };
254
+ export const createNodeFfiPosixConnectionPrimitives = (library = openNodePosixLibrary()) => ({
255
+ runtime: "node",
256
+ createSharedMemory: (options) => createNodeFfiSharedMemory(options, library),
257
+ mapSharedMemory: (options) => mapNodeFfiSharedMemory(options, library),
258
+ unlinkSharedMemory: (name) => unlinkNodeFfiSharedMemory(name, library),
259
+ });
260
+ export const createNodeFfiConnectionPrimitives = () => isWindowsRuntime()
261
+ ? createNodeWindowsConnectionPrimitives()
262
+ : createNodeFfiPosixConnectionPrimitives();