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,245 @@
1
+ import { resolveKnittingPackageAsset } from "./package-assets.js";
2
+ import { requireDetachedExternalArrayBuffer } from "./external-array-buffer.js";
3
+ import { getNodeFfi, } from "./node-ffi-api.js";
4
+ import { expectFd, expectPositiveSize, readCreateMode, readCreateSize, readRequiredCreateName, } from "./types.js";
5
+ const WINDOWS_CREATE_MODE_CREATE = 1;
6
+ const WINDOWS_CREATE_MODE_OPEN = 2;
7
+ const WINDOWS_MAPPING_RECORD_BYTES = 32;
8
+ const POINTER_OFFSET = 0;
9
+ const HANDLE_OFFSET = 8;
10
+ const SIZE_OFFSET = 16;
11
+ const BASE_ADDRESS_MOD_64_OFFSET = 24;
12
+ const WINDOWS_CREATE_SHARED_MEMORY = "knitting_windows_create_shared_memory";
13
+ const WINDOWS_MAP_SHARED_MEMORY = "knitting_windows_map_shared_memory";
14
+ const WINDOWS_CLOSE_SHARED_MEMORY = "knitting_windows_close_shared_memory";
15
+ const FFIType = {
16
+ i32: 5,
17
+ u32: 6,
18
+ i64: 7,
19
+ ptr: 12,
20
+ };
21
+ let anonymousNameCounter = 0;
22
+ const getDeno = () => {
23
+ const deno = globalThis.Deno;
24
+ if (deno === undefined) {
25
+ throw new Error("Deno Windows shared memory primitives can only run in Deno");
26
+ }
27
+ return deno;
28
+ };
29
+ const getBunFFI = () => {
30
+ const ffi = globalThis.Bun?.FFI;
31
+ if (typeof ffi?.dlopen !== "function" ||
32
+ typeof ffi?.toArrayBuffer !== "function") {
33
+ throw new Error("Bun FFI is not available in this runtime");
34
+ }
35
+ return ffi;
36
+ };
37
+ export const isWindowsRuntime = () => {
38
+ const denoOs = globalThis.Deno?.build?.os;
39
+ if (denoOs !== undefined)
40
+ return denoOs === "windows";
41
+ const processPlatform = globalThis.process?.platform;
42
+ return processPlatform === "win32";
43
+ };
44
+ const windowsArch = () => {
45
+ const denoArch = globalThis.Deno?.build?.arch;
46
+ if (denoArch === "x86_64")
47
+ return "x64";
48
+ const processArch = globalThis.process?.arch;
49
+ if (processArch === "x64")
50
+ return processArch;
51
+ throw new Error("Windows shared memory prebuilds support x64 only");
52
+ };
53
+ export const windowsSharedMemoryPrebuildPath = () => resolveKnittingPackageAsset("prebuilds", `win32-${windowsArch()}`, "knitting_windows_shared_memory.dll");
54
+ export const makeWindowsAnonymousSharedMemoryName = (runtime) => {
55
+ const processId = globalThis.process?.pid ??
56
+ globalThis.Deno?.pid ??
57
+ 0;
58
+ const next = anonymousNameCounter++;
59
+ const timeTag = Date.now().toString(36);
60
+ const randomTag = Math.random().toString(36).slice(2, 10);
61
+ const runtimeTag = runtime.replace(/[^a-z0-9_-]/gi, "").slice(0, 12) || "js";
62
+ return `Local\\knit_${runtimeTag}_${processId}_${timeTag}_${next}_${randomTag}`;
63
+ };
64
+ const readWindowsCreate = (options, runtime) => {
65
+ const mode = readCreateMode(options);
66
+ if (mode === "anonymous") {
67
+ return {
68
+ mode: WINDOWS_CREATE_MODE_CREATE,
69
+ name: makeWindowsAnonymousSharedMemoryName(runtime),
70
+ };
71
+ }
72
+ return {
73
+ mode: mode === "open"
74
+ ? WINDOWS_CREATE_MODE_OPEN
75
+ : WINDOWS_CREATE_MODE_CREATE,
76
+ name: readRequiredCreateName(options),
77
+ };
78
+ };
79
+ const encodeWideCString = (value) => {
80
+ const out = new Uint16Array((value?.length ?? 0) + 1);
81
+ if (value === undefined)
82
+ return out;
83
+ for (let index = 0; index < value.length; index += 1) {
84
+ out[index] = value.charCodeAt(index);
85
+ }
86
+ return out;
87
+ };
88
+ const readWindowsNativeMapping = (record) => {
89
+ const view = new DataView(record.buffer, record.byteOffset, record.byteLength);
90
+ const address = view.getBigUint64(POINTER_OFFSET, true);
91
+ const handle = view.getBigUint64(HANDLE_OFFSET, true);
92
+ const size = view.getBigUint64(SIZE_OFFSET, true);
93
+ if (address === 0n || handle === 0n) {
94
+ throw new Error("Windows shared memory backend returned an empty mapping");
95
+ }
96
+ if (size > BigInt(Number.MAX_SAFE_INTEGER)) {
97
+ throw new RangeError("Windows shared memory mapping is too large");
98
+ }
99
+ return {
100
+ address,
101
+ handle,
102
+ size: Number(size),
103
+ baseAddressMod64: view.getUint32(BASE_ADDRESS_MOD_64_OFFSET, true),
104
+ };
105
+ };
106
+ const fdFromHandle = (handle) => handle <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(handle) : 0;
107
+ const checkWindowsResult = (result, message) => {
108
+ if (result !== 0) {
109
+ throw new Error(`${message} failed with Windows error ${result}`);
110
+ }
111
+ };
112
+ const createRecord = () => new Uint8Array(WINDOWS_MAPPING_RECORD_BYTES);
113
+ const DENO_WINDOWS_SYMBOLS = {
114
+ [WINDOWS_CREATE_SHARED_MEMORY]: {
115
+ parameters: ["u64", "buffer", "u32", "buffer"],
116
+ result: "i32",
117
+ },
118
+ [WINDOWS_MAP_SHARED_MEMORY]: {
119
+ parameters: ["u64", "u64", "buffer", "buffer"],
120
+ result: "i32",
121
+ },
122
+ [WINDOWS_CLOSE_SHARED_MEMORY]: {
123
+ parameters: ["buffer"],
124
+ result: "i32",
125
+ },
126
+ };
127
+ const BUN_WINDOWS_SYMBOLS = {
128
+ [WINDOWS_CREATE_SHARED_MEMORY]: {
129
+ args: [FFIType.i64, FFIType.ptr, FFIType.u32, FFIType.ptr],
130
+ returns: FFIType.i32,
131
+ },
132
+ [WINDOWS_MAP_SHARED_MEMORY]: {
133
+ args: [FFIType.i64, FFIType.i64, FFIType.ptr, FFIType.ptr],
134
+ returns: FFIType.i32,
135
+ },
136
+ [WINDOWS_CLOSE_SHARED_MEMORY]: {
137
+ args: [FFIType.ptr],
138
+ returns: FFIType.i32,
139
+ },
140
+ };
141
+ const NODE_WINDOWS_SYMBOLS = {
142
+ [WINDOWS_CREATE_SHARED_MEMORY]: {
143
+ arguments: ["u64", "pointer", "u32", "pointer"],
144
+ return: "i32",
145
+ },
146
+ [WINDOWS_MAP_SHARED_MEMORY]: {
147
+ arguments: ["u64", "u64", "pointer", "pointer"],
148
+ return: "i32",
149
+ },
150
+ [WINDOWS_CLOSE_SHARED_MEMORY]: {
151
+ arguments: ["pointer"],
152
+ return: "i32",
153
+ },
154
+ };
155
+ export const openDenoWindowsSharedMemoryLibrary = () => getDeno().dlopen(windowsSharedMemoryPrebuildPath(), DENO_WINDOWS_SYMBOLS);
156
+ export const openBunWindowsSharedMemoryLibrary = () => getBunFFI().dlopen(windowsSharedMemoryPrebuildPath(), BUN_WINDOWS_SYMBOLS);
157
+ let cachedNodeWindowsLibrary;
158
+ export const openNodeWindowsSharedMemoryLibrary = () => {
159
+ if (cachedNodeWindowsLibrary !== undefined) {
160
+ return cachedNodeWindowsLibrary;
161
+ }
162
+ const opened = getNodeFfi().dlopen(windowsSharedMemoryPrebuildPath(), NODE_WINDOWS_SYMBOLS);
163
+ cachedNodeWindowsLibrary = opened;
164
+ return opened;
165
+ };
166
+ const closeWindowsMapping = (record, closeSharedMemory) => {
167
+ let closed = false;
168
+ return () => {
169
+ if (closed)
170
+ return;
171
+ closed = true;
172
+ checkWindowsResult(closeSharedMemory(record), "knitting_windows_close_shared_memory");
173
+ };
174
+ };
175
+ const makeWindowsMapping = (runtime, mapping, name, arrayBuffer, unsafePointer, close) => ({
176
+ runtime,
177
+ fd: fdFromHandle(mapping.handle),
178
+ name,
179
+ size: mapping.size,
180
+ byteLength: arrayBuffer.byteLength,
181
+ buffer: arrayBuffer,
182
+ kind: "external-array-buffer",
183
+ arrayBuffer,
184
+ unsafePointer,
185
+ baseAddressMod64: mapping.baseAddressMod64,
186
+ close,
187
+ });
188
+ const fromDenoWindowsRecord = (record, name, lib) => {
189
+ const mapping = readWindowsNativeMapping(record);
190
+ const pointer = getDeno().UnsafePointer?.create(mapping.address);
191
+ if (pointer === undefined || pointer === null || typeof pointer !== "object") {
192
+ throw new Error("Deno UnsafePointer.create is not available");
193
+ }
194
+ const arrayBuffer = new (getDeno().UnsafePointerView)(pointer)
195
+ .getArrayBuffer(mapping.size);
196
+ return makeWindowsMapping("deno", mapping, name, arrayBuffer, pointer, closeWindowsMapping(record, lib.symbols[WINDOWS_CLOSE_SHARED_MEMORY]));
197
+ };
198
+ const fromBunWindowsRecord = (record, name, lib) => {
199
+ const mapping = readWindowsNativeMapping(record);
200
+ if (mapping.address > BigInt(Number.MAX_SAFE_INTEGER)) {
201
+ throw new RangeError("Windows mapping pointer is too large for Bun FFI");
202
+ }
203
+ const arrayBuffer = getBunFFI().toArrayBuffer(Number(mapping.address), 0, mapping.size);
204
+ return makeWindowsMapping("bun", mapping, name, arrayBuffer, Number(mapping.address), closeWindowsMapping(record, lib.symbols[WINDOWS_CLOSE_SHARED_MEMORY]));
205
+ };
206
+ const fromNodeWindowsRecord = (record, name, lib) => {
207
+ const mapping = readWindowsNativeMapping(record);
208
+ const arrayBuffer = getNodeFfi().toArrayBuffer(mapping.address, mapping.size, false);
209
+ let closed = false;
210
+ return makeWindowsMapping("node", mapping, name, arrayBuffer, mapping.address, () => {
211
+ if (closed)
212
+ return;
213
+ requireDetachedExternalArrayBuffer(arrayBuffer);
214
+ checkWindowsResult(lib.functions[WINDOWS_CLOSE_SHARED_MEMORY](record), WINDOWS_CLOSE_SHARED_MEMORY);
215
+ closed = true;
216
+ });
217
+ };
218
+ const createWindowsSharedMemory = (options, runtime, lib, createSharedMemory, fromRecord) => {
219
+ const size = expectPositiveSize(readCreateSize(options));
220
+ const { mode, name } = readWindowsCreate(options, runtime);
221
+ const record = createRecord();
222
+ checkWindowsResult(createSharedMemory(BigInt(size), encodeWideCString(name), mode, record), WINDOWS_CREATE_SHARED_MEMORY);
223
+ return fromRecord(record, name, lib);
224
+ };
225
+ const mapWindowsSharedMemory = (options, lib, mapSharedMemory, fromRecord) => {
226
+ const fd = expectFd(options.fd);
227
+ const size = expectPositiveSize(options.size);
228
+ const record = createRecord();
229
+ checkWindowsResult(mapSharedMemory(BigInt(fd), BigInt(size), encodeWideCString(options.name), record), WINDOWS_MAP_SHARED_MEMORY);
230
+ return fromRecord(record, options.name, lib);
231
+ };
232
+ export const createDenoWindowsSharedMemory = (options, lib = openDenoWindowsSharedMemoryLibrary()) => createWindowsSharedMemory(options, "deno", lib, lib.symbols[WINDOWS_CREATE_SHARED_MEMORY], fromDenoWindowsRecord);
233
+ export const mapDenoWindowsSharedMemory = (options, lib = openDenoWindowsSharedMemoryLibrary()) => mapWindowsSharedMemory(options, lib, lib.symbols[WINDOWS_MAP_SHARED_MEMORY], fromDenoWindowsRecord);
234
+ const createWindowsConnectionPrimitives = (runtime, lib, createSharedMemory, mapSharedMemory) => ({
235
+ runtime,
236
+ createSharedMemory: (options) => createSharedMemory(options, lib),
237
+ mapSharedMemory: (options) => mapSharedMemory(options, lib),
238
+ });
239
+ export const createDenoWindowsConnectionPrimitives = (lib = openDenoWindowsSharedMemoryLibrary()) => createWindowsConnectionPrimitives("deno", lib, createDenoWindowsSharedMemory, mapDenoWindowsSharedMemory);
240
+ export const createBunWindowsSharedMemory = (options, lib = openBunWindowsSharedMemoryLibrary()) => createWindowsSharedMemory(options, "bun", lib, lib.symbols[WINDOWS_CREATE_SHARED_MEMORY], fromBunWindowsRecord);
241
+ export const mapBunWindowsSharedMemory = (options, lib = openBunWindowsSharedMemoryLibrary()) => mapWindowsSharedMemory(options, lib, lib.symbols[WINDOWS_MAP_SHARED_MEMORY], fromBunWindowsRecord);
242
+ export const createBunWindowsConnectionPrimitives = (lib = openBunWindowsSharedMemoryLibrary()) => createWindowsConnectionPrimitives("bun", lib, createBunWindowsSharedMemory, mapBunWindowsSharedMemory);
243
+ export const createNodeWindowsSharedMemory = (options, lib = openNodeWindowsSharedMemoryLibrary()) => createWindowsSharedMemory(options, "node", lib, lib.functions[WINDOWS_CREATE_SHARED_MEMORY], fromNodeWindowsRecord);
244
+ export const mapNodeWindowsSharedMemory = (options, lib = openNodeWindowsSharedMemoryLibrary()) => mapWindowsSharedMemory(options, lib, lib.functions[WINDOWS_MAP_SHARED_MEMORY], fromNodeWindowsRecord);
245
+ export const createNodeWindowsConnectionPrimitives = (lib = openNodeWindowsSharedMemoryLibrary()) => createWindowsConnectionPrimitives("node", lib, createNodeWindowsSharedMemory, mapNodeWindowsSharedMemory);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Environment-diff primitive: snapshot the keys present on `globalThis`, then
3
+ * later compare to see what the loaded code added, removed, or redefined.
4
+ *
5
+ * This is the core of "check what happens" debugging — a concrete before/after
6
+ * of the real runtime a worker's modules created, not an aggregate metric. The
7
+ * same snapshot/diff shape generalises to other ambient state (process
8
+ * listeners, open handles, prototype patches); `globalThis` keys are the first
9
+ * instance.
10
+ */
11
+ export type EnvSnapshot = {
12
+ readonly keys: ReadonlySet<string | symbol>;
13
+ };
14
+ /** Capture every own key on `globalThis`, including symbols. */
15
+ export declare const snapshotGlobals: () => EnvSnapshot;
16
+ export type GlobalsDiff = {
17
+ readonly added: (string | symbol)[];
18
+ readonly removed: (string | symbol)[];
19
+ };
20
+ export declare const diffGlobals: (before: EnvSnapshot, after: EnvSnapshot) => GlobalsDiff;
21
+ /**
22
+ * Render a key with enough provenance to tell a fresh global from a
23
+ * monkeypatch: its `typeof`/accessor kind plus writable/configurable/enumerable
24
+ * flags (`w`/`c`/`e`).
25
+ */
26
+ export declare const describeGlobalKey: (key: string | symbol) => string;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Environment-diff primitive: snapshot the keys present on `globalThis`, then
3
+ * later compare to see what the loaded code added, removed, or redefined.
4
+ *
5
+ * This is the core of "check what happens" debugging — a concrete before/after
6
+ * of the real runtime a worker's modules created, not an aggregate metric. The
7
+ * same snapshot/diff shape generalises to other ambient state (process
8
+ * listeners, open handles, prototype patches); `globalThis` keys are the first
9
+ * instance.
10
+ */
11
+ /** Capture every own key on `globalThis`, including symbols. */
12
+ export const snapshotGlobals = () => ({
13
+ keys: new Set(Reflect.ownKeys(globalThis)),
14
+ });
15
+ export const diffGlobals = (before, after) => {
16
+ const added = [];
17
+ const removed = [];
18
+ for (const key of after.keys) {
19
+ if (!before.keys.has(key))
20
+ added.push(key);
21
+ }
22
+ for (const key of before.keys) {
23
+ if (!after.keys.has(key))
24
+ removed.push(key);
25
+ }
26
+ return { added, removed };
27
+ };
28
+ /**
29
+ * Render a key with enough provenance to tell a fresh global from a
30
+ * monkeypatch: its `typeof`/accessor kind plus writable/configurable/enumerable
31
+ * flags (`w`/`c`/`e`).
32
+ */
33
+ export const describeGlobalKey = (key) => {
34
+ const name = typeof key === "symbol" ? key.toString() : key;
35
+ let descriptor;
36
+ try {
37
+ descriptor = Object.getOwnPropertyDescriptor(globalThis, key);
38
+ }
39
+ catch {
40
+ return name;
41
+ }
42
+ if (descriptor === undefined)
43
+ return name;
44
+ const kind = descriptor.get !== undefined || descriptor.set !== undefined
45
+ ? "accessor"
46
+ : typeof descriptor.value;
47
+ const flags = `${descriptor.writable === false ? "" : "w"}${descriptor.configurable ? "c" : ""}${descriptor.enumerable ? "e" : ""}`;
48
+ return flags.length > 0 ? `${name} (${kind} ${flags})` : `${name} (${kind})`;
49
+ };
@@ -0,0 +1,18 @@
1
+ import type { DebugOptions } from "../types.js";
2
+ /** Namespaces explicitly requested via `KNITTING_DEBUG`. */
3
+ export declare const DEBUG_NAMESPACES: ReadonlySet<string>;
4
+ /**
5
+ * True when at least one namespace is requested via the env var alone. Guards
6
+ * the env-only paths; callers that also accept a `debug` option should use
7
+ * {@link resolveDebugNamespaces} instead.
8
+ */
9
+ export declare const DEBUG_ENABLED: boolean;
10
+ /**
11
+ * Merge the namespaces requested through the `debug` pool option with those from
12
+ * `KNITTING_DEBUG`; either source can enable a namespace. Returns an empty set
13
+ * when nothing is requested, so callers use `.size === 0` to keep the rest of
14
+ * `src/debug` unloaded — zero cost when off.
15
+ */
16
+ export declare const resolveDebugNamespaces: (debug?: DebugOptions) => Set<string>;
17
+ /** True when `namespace` (or `"*"`) is active for the given `debug` config + env. */
18
+ export declare const debugHas: (debug: DebugOptions | undefined, namespace: string) => boolean;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Zero-cost debug gate.
3
+ *
4
+ * Read once at module load from `KNITTING_DEBUG`. This module is deliberately
5
+ * tiny and dependency-light: importing it must never pull in the logger or the
6
+ * environment-diff machinery. Callers branch on {@link DEBUG_ENABLED} and only
7
+ * then `await import("./handle.ts")`, so when debug is off nothing else under
8
+ * `src/debug` is ever loaded — literally zero cost, not merely cheap.
9
+ *
10
+ * `KNITTING_DEBUG` is a comma-separated list of namespaces:
11
+ * KNITTING_DEBUG=host,imports # only those
12
+ * KNITTING_DEBUG=* # everything
13
+ */
14
+ import { getNodeProcess } from "../common/node-compat.js";
15
+ const readEnv = (key) => {
16
+ // Deno: `process.env` exists under node-compat, but reading it may require
17
+ // --allow-env. Prefer the typed `Deno.env` and swallow permission errors.
18
+ const denoEnv = globalThis.Deno?.env;
19
+ if (typeof denoEnv?.get === "function") {
20
+ try {
21
+ return denoEnv.get(key);
22
+ }
23
+ catch {
24
+ /* env permission denied — fall through to node/bun */
25
+ }
26
+ }
27
+ try {
28
+ return getNodeProcess()?.env?.[key];
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ };
34
+ const raw = readEnv("KNITTING_DEBUG");
35
+ /** Namespaces explicitly requested via `KNITTING_DEBUG`. */
36
+ export const DEBUG_NAMESPACES = new Set((raw ?? "")
37
+ .split(",")
38
+ .map((part) => part.trim())
39
+ .filter((part) => part.length > 0));
40
+ /**
41
+ * True when at least one namespace is requested via the env var alone. Guards
42
+ * the env-only paths; callers that also accept a `debug` option should use
43
+ * {@link resolveDebugNamespaces} instead.
44
+ */
45
+ export const DEBUG_ENABLED = DEBUG_NAMESPACES.size > 0;
46
+ /**
47
+ * Merge the namespaces requested through the `debug` pool option with those from
48
+ * `KNITTING_DEBUG`; either source can enable a namespace. Returns an empty set
49
+ * when nothing is requested, so callers use `.size === 0` to keep the rest of
50
+ * `src/debug` unloaded — zero cost when off.
51
+ */
52
+ export const resolveDebugNamespaces = (debug) => {
53
+ const namespaces = new Set(DEBUG_NAMESPACES);
54
+ if (debug === true) {
55
+ namespaces.add("*");
56
+ }
57
+ else if (debug !== undefined && debug !== false) {
58
+ for (const [key, value] of Object.entries(debug)) {
59
+ if (value === true)
60
+ namespaces.add(key);
61
+ }
62
+ }
63
+ return namespaces;
64
+ };
65
+ /** True when `namespace` (or `"*"`) is active for the given `debug` config + env. */
66
+ export const debugHas = (debug, namespace) => {
67
+ const namespaces = resolveDebugNamespaces(debug);
68
+ return namespaces.has("*") || namespaces.has(namespace);
69
+ };
@@ -0,0 +1,23 @@
1
+ export type DebugInit = {
2
+ /** Identity prefix for log tags, e.g. `"w0"` for a worker or `"main"`. */
3
+ readonly name: string;
4
+ readonly runtime: string;
5
+ readonly namespaces: ReadonlySet<string>;
6
+ };
7
+ export type Debug = {
8
+ /**
9
+ * Is a namespace active? Capture this once before a hot loop and branch on
10
+ * the boolean — never call per-iteration.
11
+ */
12
+ enabled: (namespace: string) => boolean;
13
+ /** Emit a tagged line to stderr when `namespace` is active. */
14
+ log: (namespace: string, message: string) => void;
15
+ /**
16
+ * Re-snapshot `globalThis` and report what changed since the previous phase.
17
+ * Drives two-phase pollution attribution (e.g. `"bootstrap"` then
18
+ * `"tasks"`), so you can see which loader injected which global. No-op unless
19
+ * the `globals` namespace is active.
20
+ */
21
+ envPhase: (label: string) => void;
22
+ };
23
+ export declare const initDebug: ({ name, runtime, namespaces }: DebugInit) => Debug;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Worker-side debug handle. Lazily imported (see {@link ./gate.ts}) only when
3
+ * `KNITTING_DEBUG` names at least one namespace, so neither this code nor the
4
+ * baseline snapshot it takes exists when debug is off.
5
+ *
6
+ * Diagnostics go to stderr so they never corrupt a worker's stdout, and every
7
+ * line is tagged with the worker id, runtime, and a clock relative to when this
8
+ * worker's debug initialised. The clock is worker-local on purpose: a main-thread
9
+ * timestamp can't be compared against `performance.now()` here because the time
10
+ * origins differ across the thread/process boundary (it would read negative).
11
+ */
12
+ import { describeGlobalKey, diffGlobals, snapshotGlobals, } from "./env-diff.js";
13
+ export const initDebug = ({ name, runtime, namespaces }) => {
14
+ const all = namespaces.has("*");
15
+ const enabled = (namespace) => all || namespaces.has(namespace);
16
+ const base = performance.now();
17
+ const tag = `${name}·${runtime}`;
18
+ const log = (namespace, message) => {
19
+ if (!enabled(namespace))
20
+ return;
21
+ const elapsed = (performance.now() - base).toFixed(1);
22
+ console.error(`[${tag}·+${elapsed}ms] ${namespace}: ${message}`);
23
+ };
24
+ // Baseline for the environment diff, taken the moment debug initialises:
25
+ // before worker bootstrap and before any task module is imported. Only paid
26
+ // when `globals` tracing is actually on.
27
+ let previous = enabled("globals")
28
+ ? snapshotGlobals()
29
+ : undefined;
30
+ const envPhase = (label) => {
31
+ if (previous === undefined)
32
+ return;
33
+ const current = snapshotGlobals();
34
+ const { added, removed } = diffGlobals(previous, current);
35
+ previous = current;
36
+ if (added.length === 0 && removed.length === 0) {
37
+ log("globals", `${label}: no new globals`);
38
+ return;
39
+ }
40
+ if (added.length > 0) {
41
+ log("globals", `${label} +${added.length}: ${added.map(describeGlobalKey).join(" ")}`);
42
+ }
43
+ if (removed.length > 0) {
44
+ log("globals", `${label} -${removed.length}: ${removed.map(String).join(" ")}`);
45
+ }
46
+ };
47
+ return { enabled, log, envPhase };
48
+ };
package/src/error.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { type PromisePayloadHandler, type Task } from "./memory/lock.js";
2
+ export declare const ErrorKnitting: {
3
+ readonly Function: 0;
4
+ readonly Symbol: 1;
5
+ readonly Json: 2;
6
+ readonly Serializable: 3;
7
+ };
8
+ export type ErrorKnitting = typeof ErrorKnitting[keyof typeof ErrorKnitting];
9
+ export declare const encoderError: ({ task, type, onPromise, detail, }: {
10
+ task: Task;
11
+ type: ErrorKnitting;
12
+ onPromise?: PromisePayloadHandler;
13
+ detail?: string;
14
+ }) => false;
package/src/error.js ADDED
@@ -0,0 +1,50 @@
1
+ import { beginPromisePayload, finishPromisePayload, TaskFlag, TaskIndex, } from "./memory/lock.js";
2
+ import { RUNTIME_IS_MAIN_THREAD } from "./common/worker-runtime.js";
3
+ // const object, not `enum`: Andromeda's Nova engine can't parse `enum`. Same
4
+ // value access and emit on Node/Deno/Bun.
5
+ export const ErrorKnitting = {
6
+ Function: 0,
7
+ Symbol: 1,
8
+ Json: 2,
9
+ Serializable: 3,
10
+ };
11
+ const reasonFrom = (task, type, detail) => {
12
+ switch (type) {
13
+ case ErrorKnitting.Function: {
14
+ const name = typeof task.value === "function"
15
+ ? (task.value.name || "<anonymous>")
16
+ : "<unknown>";
17
+ return `KNT_ERROR_0: Function is not a valid type; name: ${name}`;
18
+ }
19
+ case ErrorKnitting.Symbol:
20
+ return "KNT_ERROR_1: Symbol must use Symbol.for(...) keys";
21
+ case ErrorKnitting.Json:
22
+ return detail == null || detail.length === 0
23
+ ? "KNT_ERROR_2: JSON stringify failed; payload must be JSON-safe"
24
+ : `KNT_ERROR_2: JSON stringify failed; ${detail}`;
25
+ case ErrorKnitting.Serializable:
26
+ return detail == null || detail.length === 0
27
+ ? "KNT_ERROR_3: Unsupported payload type; serialize it yourself"
28
+ : `KNT_ERROR_3: Unsupported payload type; ${detail}`;
29
+ }
30
+ };
31
+ export const encoderError = ({ task, type, onPromise, detail, }) => {
32
+ const reason = reasonFrom(task, type, detail);
33
+ if (!RUNTIME_IS_MAIN_THREAD) {
34
+ task.value = reason;
35
+ task[TaskIndex.FlagsToHost] = TaskFlag.Reject;
36
+ return false;
37
+ }
38
+ // Fallback for direct codec usage where no async settle callback is wired.
39
+ if (onPromise == null) {
40
+ throw new TypeError(reason);
41
+ }
42
+ if (!beginPromisePayload(task))
43
+ return false;
44
+ queueMicrotask(() => {
45
+ finishPromisePayload(task);
46
+ task.value = reason;
47
+ onPromise(task, true, reason);
48
+ });
49
+ return false;
50
+ };
@@ -0,0 +1,33 @@
1
+ export default class RingQueue<T> implements Iterable<T> {
2
+ #private;
3
+ constructor(capacity?: number);
4
+ get size(): number;
5
+ get isEmpty(): boolean;
6
+ get capacity(): number;
7
+ clear(): void;
8
+ peek(): T | undefined;
9
+ /** Ensure internal capacity >= requested (rounds up to next power of two). */
10
+ reserve(minCapacity: number): void;
11
+ /**
12
+ * Push to back
13
+ * Always succeeds (grows if full)
14
+ */
15
+ push(value: T): true;
16
+ /**
17
+ * Push to front (unshift)
18
+ * Always succeeds (grows if full)
19
+ */
20
+ unshift(value: T): true;
21
+ /**
22
+ * Pop from front (shift)
23
+ */
24
+ shift(): T | undefined;
25
+ /**
26
+ * Pop from front (shift) without clearing the slot.
27
+ * Use only for internal pooled-object queues where retaining references is acceptable.
28
+ */
29
+ shiftNoClear(): T | undefined;
30
+ [Symbol.iterator](): Generator<T, void, void>;
31
+ toArray(): T[];
32
+ get [Symbol.toStringTag](): string;
33
+ }