knitting 0.1.55 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/knitting.d.ts +6 -797
- package/knitting.js +6 -9780
- package/map.md +12 -8
- package/package.json +14 -6
- package/process-shared-buffer.d.ts +1 -0
- package/process-shared-buffer.js +1 -0
- package/shared-memory.d.ts +1 -155
- package/shared-memory.js +1 -1342
- package/src/api.d.ts +69 -0
- package/src/api.js +619 -0
- package/src/common/envelope.d.ts +17 -0
- package/src/common/envelope.js +22 -0
- package/src/common/module-url.d.ts +1 -0
- package/src/common/module-url.js +24 -0
- package/src/common/node-compat.d.ts +20 -0
- package/src/common/node-compat.js +24 -0
- package/src/common/path-canonical.d.ts +6 -0
- package/src/common/path-canonical.js +41 -0
- package/src/common/runtime.d.ts +15 -0
- package/src/common/runtime.js +91 -0
- package/src/common/shared-buffer-region.d.ts +11 -0
- package/src/common/shared-buffer-region.js +21 -0
- package/src/common/shared-buffer-text.d.ts +16 -0
- package/src/common/shared-buffer-text.js +65 -0
- package/src/common/task-source.d.ts +3 -0
- package/src/common/task-source.js +84 -0
- package/src/common/task-symbol.d.ts +1 -0
- package/src/common/task-symbol.js +1 -0
- package/src/common/with-resolvers.d.ts +9 -0
- package/src/common/with-resolvers.js +23 -0
- package/src/common/worker-runtime.d.ts +42 -0
- package/src/common/worker-runtime.js +61 -0
- package/src/connections/buffer-reference-native.d.ts +56 -0
- package/src/connections/buffer-reference-native.js +217 -0
- package/src/connections/buffer-reference.d.ts +78 -0
- package/src/connections/buffer-reference.js +461 -0
- package/src/connections/bun.d.ts +22 -0
- package/src/connections/bun.js +214 -0
- package/src/connections/deno.d.ts +22 -0
- package/src/connections/deno.js +205 -0
- package/src/connections/file-descriptor.d.ts +39 -0
- package/src/connections/file-descriptor.js +161 -0
- package/src/connections/index.d.ts +4 -0
- package/src/connections/index.js +4 -0
- package/src/connections/node-addons.d.ts +5 -0
- package/src/connections/node-addons.js +44 -0
- package/src/connections/node-buffer-pointer.d.ts +20 -0
- package/src/connections/node-buffer-pointer.js +16 -0
- package/src/connections/node.d.ts +30 -0
- package/src/connections/node.js +62 -0
- package/src/connections/numeric-array.d.ts +5 -0
- package/src/connections/numeric-array.js +17 -0
- package/src/connections/package-assets.d.ts +2 -0
- package/src/connections/package-assets.js +45 -0
- package/src/connections/posix.d.ts +32 -0
- package/src/connections/posix.js +77 -0
- package/src/connections/process-shared-buffer.d.ts +75 -0
- package/src/connections/process-shared-buffer.js +279 -0
- package/src/connections/shared-array-buffer-payload.d.ts +36 -0
- package/src/connections/shared-array-buffer-payload.js +235 -0
- package/src/connections/types.d.ts +50 -0
- package/src/connections/types.js +37 -0
- package/src/connections/windows.d.ts +28 -0
- package/src/connections/windows.js +224 -0
- package/src/debug/env-diff.d.ts +26 -0
- package/src/debug/env-diff.js +49 -0
- package/src/debug/gate.d.ts +18 -0
- package/src/debug/gate.js +69 -0
- package/src/debug/handle.d.ts +23 -0
- package/src/debug/handle.js +48 -0
- package/src/error.d.ts +13 -0
- package/src/error.js +49 -0
- package/src/ipc/tools/ring-queue.d.ts +33 -0
- package/src/ipc/tools/ring-queue.js +159 -0
- package/src/ipc/transport/shared-memory.d.ts +23 -0
- package/src/ipc/transport/shared-memory.js +35 -0
- package/src/memory/byte-carpet.d.ts +73 -0
- package/src/memory/byte-carpet.js +157 -0
- package/src/memory/lock.d.ts +203 -0
- package/src/memory/lock.js +741 -0
- package/src/memory/payload-config.d.ts +31 -0
- package/src/memory/payload-config.js +67 -0
- package/src/memory/payloadCodec.d.ts +46 -0
- package/src/memory/payloadCodec.js +1377 -0
- package/src/memory/regionRegistry.d.ts +17 -0
- package/src/memory/regionRegistry.js +285 -0
- package/src/memory/shared-buffer-io.d.ts +55 -0
- package/src/memory/shared-buffer-io.js +403 -0
- package/src/permission/index.d.ts +2 -0
- package/src/permission/index.js +2 -0
- package/src/permission/protocol.d.ts +166 -0
- package/src/permission/protocol.js +650 -0
- package/src/runtime/balancer.d.ts +19 -0
- package/src/runtime/balancer.js +149 -0
- package/src/runtime/dispatcher.d.ts +40 -0
- package/src/runtime/dispatcher.js +142 -0
- package/src/runtime/inline-executor.d.ts +10 -0
- package/src/runtime/inline-executor.js +282 -0
- package/src/runtime/pool.d.ts +39 -0
- package/src/runtime/pool.js +404 -0
- package/src/runtime/process-worker.d.ts +92 -0
- package/src/runtime/process-worker.js +673 -0
- package/src/runtime/tx-queue.d.ts +26 -0
- package/src/runtime/tx-queue.js +155 -0
- package/src/shared/abortSignal.d.ts +23 -0
- package/src/shared/abortSignal.js +134 -0
- package/src/types.d.ts +357 -0
- package/src/types.js +2 -0
- package/src/utils/http.d.ts +29 -0
- package/src/utils/http.js +100 -0
- package/src/worker/bootstrap.d.ts +5 -0
- package/src/worker/bootstrap.js +78 -0
- package/src/worker/composable-runners.d.ts +12 -0
- package/src/worker/composable-runners.js +97 -0
- package/src/worker/loop.d.ts +2 -0
- package/src/worker/loop.js +382 -0
- package/src/worker/process-worker-bootstrap.d.ts +8 -0
- package/src/worker/process-worker-bootstrap.js +160 -0
- package/src/worker/rx-queue.d.ts +25 -0
- package/src/worker/rx-queue.js +173 -0
- package/src/worker/safety/index.d.ts +4 -0
- package/src/worker/safety/index.js +4 -0
- package/src/worker/safety/performance.d.ts +1 -0
- package/src/worker/safety/performance.js +17 -0
- package/src/worker/safety/process.d.ts +2 -0
- package/src/worker/safety/process.js +79 -0
- package/src/worker/safety/startup.d.ts +16 -0
- package/src/worker/safety/startup.js +30 -0
- package/src/worker/safety/worker-data.d.ts +2 -0
- package/src/worker/safety/worker-data.js +36 -0
- package/src/worker/task-loader.d.ts +27 -0
- package/src/worker/task-loader.js +70 -0
- package/src/worker/timers.d.ts +18 -0
- package/src/worker/timers.js +116 -0
- package/unsafe.d.ts +1 -71
- package/unsafe.js +1 -878
- package/utils.d.ts +1 -31
- package/utils.js +1 -116
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { RUNTIME } from "../common/runtime.js";
|
|
2
|
+
import { getNodeProcess } from "../common/node-compat.js";
|
|
3
|
+
import { getBufferReferenceCapabilities } from "./buffer-reference-native.js";
|
|
4
|
+
// Thread-worker SharedArrayBuffer transport by process-local pointer.
|
|
5
|
+
// SABs are shared, not moved or detached; process workers reject them.
|
|
6
|
+
export const SHARED_ARRAY_BUFFER_CODEC_ID = "knitting.sharedArrayBuffer";
|
|
7
|
+
export const SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER = Symbol.for("knitting.sharedArrayBuffer.numericTransfer");
|
|
8
|
+
export const SHARED_ARRAY_BUFFER_NUMERIC_WORDS = 8;
|
|
9
|
+
const SHARED_ARRAY_BUFFER_TOKEN_NUMERIC_WORDS = 2;
|
|
10
|
+
const EXTERNAL_PAYLOAD_BRAND = Symbol.for("knitting.payloadCodec");
|
|
11
|
+
const getProcessId = () => {
|
|
12
|
+
const proc = getNodeProcess();
|
|
13
|
+
if (proc !== undefined && typeof proc.pid === "number")
|
|
14
|
+
return proc.pid;
|
|
15
|
+
const deno = globalThis.Deno;
|
|
16
|
+
if (typeof deno?.pid === "number")
|
|
17
|
+
return deno.pid;
|
|
18
|
+
return 0;
|
|
19
|
+
};
|
|
20
|
+
const PROCESS_ORIGIN = `${RUNTIME}:${getProcessId()}`;
|
|
21
|
+
const hasSharedArrayBuffer = typeof SharedArrayBuffer === "function";
|
|
22
|
+
export const isSharedArrayBufferValue = (value) => hasSharedArrayBuffer && value instanceof SharedArrayBuffer;
|
|
23
|
+
const pinnedBySab = new WeakMap();
|
|
24
|
+
const payloadBySharedBuffer = new WeakMap();
|
|
25
|
+
const warmedTokensByTransport = new WeakMap();
|
|
26
|
+
const cachedSharedBuffersByToken = new Map();
|
|
27
|
+
const pinFinalizer = typeof FinalizationRegistry === "function"
|
|
28
|
+
? new FinalizationRegistry((token) => {
|
|
29
|
+
try {
|
|
30
|
+
getBufferReferenceCapabilities().releaseShared(token);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// best effort
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
: undefined;
|
|
37
|
+
const splitU64 = (value) => [
|
|
38
|
+
Number(value & 0xffffffffn) >>> 0,
|
|
39
|
+
Number((value >> 32n) & 0xffffffffn) >>> 0,
|
|
40
|
+
];
|
|
41
|
+
const joinU64 = (low, high) => (BigInt(high >>> 0) << 32n) | BigInt(low >>> 0);
|
|
42
|
+
const encodeRuntime = (runtime) => {
|
|
43
|
+
switch (runtime) {
|
|
44
|
+
case "node":
|
|
45
|
+
return 1;
|
|
46
|
+
case "deno":
|
|
47
|
+
return 2;
|
|
48
|
+
case "bun":
|
|
49
|
+
return 3;
|
|
50
|
+
default:
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const decodeRuntime = (value) => {
|
|
55
|
+
switch (value) {
|
|
56
|
+
case 1:
|
|
57
|
+
return "node";
|
|
58
|
+
case 2:
|
|
59
|
+
return "deno";
|
|
60
|
+
case 3:
|
|
61
|
+
return "bun";
|
|
62
|
+
default:
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const getWarmTokens = (transportKey) => {
|
|
67
|
+
if (transportKey === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
let warmTokens = warmedTokensByTransport.get(transportKey);
|
|
70
|
+
if (warmTokens === undefined) {
|
|
71
|
+
warmTokens = new Set();
|
|
72
|
+
warmedTokensByTransport.set(transportKey, warmTokens);
|
|
73
|
+
}
|
|
74
|
+
return warmTokens;
|
|
75
|
+
};
|
|
76
|
+
const pinSab = (sab) => {
|
|
77
|
+
let pin = pinnedBySab.get(sab);
|
|
78
|
+
if (pin === undefined) {
|
|
79
|
+
const produced = getBufferReferenceCapabilities().produceShared(sab);
|
|
80
|
+
pin = {
|
|
81
|
+
token: produced.token,
|
|
82
|
+
pointer: produced.pointer,
|
|
83
|
+
byteLength: produced.byteLength,
|
|
84
|
+
};
|
|
85
|
+
pinnedBySab.set(sab, pin);
|
|
86
|
+
pinFinalizer?.register(sab, pin.token);
|
|
87
|
+
}
|
|
88
|
+
return pin;
|
|
89
|
+
};
|
|
90
|
+
const makeMetadata = (pin) => ({
|
|
91
|
+
kind: SHARED_ARRAY_BUFFER_CODEC_ID,
|
|
92
|
+
origin: PROCESS_ORIGIN,
|
|
93
|
+
runtime: RUNTIME,
|
|
94
|
+
pointer: pin.pointer.toString(),
|
|
95
|
+
token: pin.token.toString(),
|
|
96
|
+
byteLength: pin.byteLength,
|
|
97
|
+
});
|
|
98
|
+
const makeFullNumericMetadata = (pin) => {
|
|
99
|
+
if (pin.byteLength > 0xffffffff)
|
|
100
|
+
return undefined;
|
|
101
|
+
const [tokenLow, tokenHigh] = splitU64(pin.token);
|
|
102
|
+
const [pointerLow, pointerHigh] = splitU64(pin.pointer);
|
|
103
|
+
return [
|
|
104
|
+
tokenLow,
|
|
105
|
+
tokenHigh,
|
|
106
|
+
pointerLow,
|
|
107
|
+
pointerHigh,
|
|
108
|
+
pin.byteLength >>> 0,
|
|
109
|
+
encodeRuntime(RUNTIME),
|
|
110
|
+
getProcessId() >>> 0,
|
|
111
|
+
0,
|
|
112
|
+
];
|
|
113
|
+
};
|
|
114
|
+
const makeTokenNumericMetadata = (pin) => {
|
|
115
|
+
const [tokenLow, tokenHigh] = splitU64(pin.token);
|
|
116
|
+
return [tokenLow, tokenHigh];
|
|
117
|
+
};
|
|
118
|
+
/** Wrap a SAB as external payload; GC-managed pins mean no settle finalizer. */
|
|
119
|
+
export const wrapSharedArrayBufferPayload = (sab) => {
|
|
120
|
+
let payload = payloadBySharedBuffer.get(sab);
|
|
121
|
+
if (payload !== undefined)
|
|
122
|
+
return payload;
|
|
123
|
+
const pin = pinSab(sab);
|
|
124
|
+
payload = createSharedArrayBufferPayload(sab, pin, makeMetadata(pin));
|
|
125
|
+
return payload;
|
|
126
|
+
};
|
|
127
|
+
const createSharedArrayBufferPayload = (buffer, pin, metadata) => {
|
|
128
|
+
let payload = payloadBySharedBuffer.get(buffer);
|
|
129
|
+
if (payload !== undefined)
|
|
130
|
+
return payload;
|
|
131
|
+
const fullNumeric = makeFullNumericMetadata(pin);
|
|
132
|
+
const tokenOnlyNumeric = makeTokenNumericMetadata(pin);
|
|
133
|
+
payload = {
|
|
134
|
+
[EXTERNAL_PAYLOAD_BRAND]: SHARED_ARRAY_BUFFER_CODEC_ID,
|
|
135
|
+
toMetadata: () => metadata,
|
|
136
|
+
[SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER]: (transportKey) => {
|
|
137
|
+
if (fullNumeric === undefined) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const warmTokens = getWarmTokens(transportKey);
|
|
141
|
+
if (warmTokens === undefined)
|
|
142
|
+
return fullNumeric;
|
|
143
|
+
if (warmTokens.has(pin.token))
|
|
144
|
+
return tokenOnlyNumeric;
|
|
145
|
+
warmTokens.add(pin.token);
|
|
146
|
+
return fullNumeric;
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
payloadBySharedBuffer.set(buffer, payload);
|
|
150
|
+
return payload;
|
|
151
|
+
};
|
|
152
|
+
export const getSharedArrayBufferPayload = (value) => {
|
|
153
|
+
if (isSharedArrayBufferValue(value))
|
|
154
|
+
return wrapSharedArrayBufferPayload(value);
|
|
155
|
+
return payloadBySharedBuffer.get(value);
|
|
156
|
+
};
|
|
157
|
+
const isSharedArrayBufferMetadata = (value) => {
|
|
158
|
+
if (value === null || typeof value !== "object")
|
|
159
|
+
return false;
|
|
160
|
+
const meta = value;
|
|
161
|
+
return (meta.kind === SHARED_ARRAY_BUFFER_CODEC_ID &&
|
|
162
|
+
typeof meta.origin === "string" &&
|
|
163
|
+
typeof meta.runtime === "string" &&
|
|
164
|
+
typeof meta.pointer === "string" &&
|
|
165
|
+
typeof meta.token === "string" &&
|
|
166
|
+
typeof meta.byteLength === "number" &&
|
|
167
|
+
Number.isInteger(meta.byteLength) &&
|
|
168
|
+
meta.byteLength >= 0);
|
|
169
|
+
};
|
|
170
|
+
const materializeSharedBuffer = (metadata, warmOnly) => {
|
|
171
|
+
if (metadata.origin !== PROCESS_ORIGIN) {
|
|
172
|
+
throw new Error(`SharedArrayBuffer cannot cross a process boundary (origin ${metadata.origin} ` +
|
|
173
|
+
`!= ${PROCESS_ORIGIN}); it is shared by reference to thread workers only.`);
|
|
174
|
+
}
|
|
175
|
+
const token = BigInt(metadata.token);
|
|
176
|
+
const cached = cachedSharedBuffersByToken.get(token);
|
|
177
|
+
if (cached !== undefined)
|
|
178
|
+
return cached;
|
|
179
|
+
if (warmOnly) {
|
|
180
|
+
throw new TypeError("SharedArrayBuffer cache miss for warm token payload");
|
|
181
|
+
}
|
|
182
|
+
const region = getBufferReferenceCapabilities().adoptShared({
|
|
183
|
+
token,
|
|
184
|
+
pointer: BigInt(metadata.pointer),
|
|
185
|
+
byteOffset: 0,
|
|
186
|
+
byteLength: metadata.byteLength,
|
|
187
|
+
});
|
|
188
|
+
cachedSharedBuffersByToken.set(token, region.buffer);
|
|
189
|
+
createSharedArrayBufferPayload(region.buffer, {
|
|
190
|
+
token,
|
|
191
|
+
pointer: BigInt(metadata.pointer),
|
|
192
|
+
byteLength: metadata.byteLength,
|
|
193
|
+
}, metadata);
|
|
194
|
+
return region.buffer;
|
|
195
|
+
};
|
|
196
|
+
const decode = (metadata) => {
|
|
197
|
+
if (!isSharedArrayBufferMetadata(metadata)) {
|
|
198
|
+
throw new TypeError("Invalid SharedArrayBuffer payload metadata");
|
|
199
|
+
}
|
|
200
|
+
return materializeSharedBuffer(metadata, false);
|
|
201
|
+
};
|
|
202
|
+
const decodeNumeric = (words) => {
|
|
203
|
+
if (words.length === SHARED_ARRAY_BUFFER_TOKEN_NUMERIC_WORDS) {
|
|
204
|
+
const token = joinU64(words[0] ?? 0, words[1] ?? 0);
|
|
205
|
+
const cached = cachedSharedBuffersByToken.get(token);
|
|
206
|
+
if (cached !== undefined)
|
|
207
|
+
return cached;
|
|
208
|
+
throw new TypeError("SharedArrayBuffer cache miss for warm token payload");
|
|
209
|
+
}
|
|
210
|
+
const runtime = decodeRuntime(words[5] ?? 0);
|
|
211
|
+
if (runtime === undefined) {
|
|
212
|
+
throw new TypeError("Invalid SharedArrayBuffer numeric runtime");
|
|
213
|
+
}
|
|
214
|
+
const originPid = words[6];
|
|
215
|
+
if (originPid === undefined ||
|
|
216
|
+
!Number.isInteger(originPid) ||
|
|
217
|
+
originPid < 0) {
|
|
218
|
+
throw new TypeError("Invalid SharedArrayBuffer numeric origin");
|
|
219
|
+
}
|
|
220
|
+
if (words.length !== SHARED_ARRAY_BUFFER_NUMERIC_WORDS) {
|
|
221
|
+
throw new TypeError("Invalid SharedArrayBuffer numeric word count");
|
|
222
|
+
}
|
|
223
|
+
const metadata = {
|
|
224
|
+
kind: SHARED_ARRAY_BUFFER_CODEC_ID,
|
|
225
|
+
origin: `${runtime}:${originPid >>> 0}`,
|
|
226
|
+
runtime,
|
|
227
|
+
pointer: joinU64(words[2] ?? 0, words[3] ?? 0).toString(),
|
|
228
|
+
token: joinU64(words[0] ?? 0, words[1] ?? 0).toString(),
|
|
229
|
+
byteLength: words[4] ?? 0,
|
|
230
|
+
};
|
|
231
|
+
return materializeSharedBuffer(metadata, false);
|
|
232
|
+
};
|
|
233
|
+
const codecGlobal = globalThis;
|
|
234
|
+
const codecs = codecGlobal.__KNITTING_PAYLOAD_CODECS__ ??= Object.create(null);
|
|
235
|
+
codecs[SHARED_ARRAY_BUFFER_CODEC_ID] = { decode, decodeNumeric };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare const CACHE_LINE_SIZE = 64;
|
|
2
|
+
export type ConnectionRuntime = "node" | "deno" | "bun";
|
|
3
|
+
export type SharedMemoryBuffer = ArrayBuffer | SharedArrayBuffer;
|
|
4
|
+
export type SharedMemoryBufferKind = "shared-array-buffer" | "external-array-buffer";
|
|
5
|
+
export type SharedMemoryCreateMode = "anonymous" | "create" | "open";
|
|
6
|
+
export type SharedMemoryMapping<Buffer extends SharedMemoryBuffer = SharedMemoryBuffer> = {
|
|
7
|
+
runtime: ConnectionRuntime;
|
|
8
|
+
fd: number;
|
|
9
|
+
name?: string;
|
|
10
|
+
size: number;
|
|
11
|
+
byteLength: number;
|
|
12
|
+
buffer: Buffer;
|
|
13
|
+
kind: SharedMemoryBufferKind;
|
|
14
|
+
sab?: SharedArrayBuffer;
|
|
15
|
+
arrayBuffer?: ArrayBuffer;
|
|
16
|
+
baseAddressMod64?: number;
|
|
17
|
+
unsafePointer?: unknown;
|
|
18
|
+
close?: () => void;
|
|
19
|
+
};
|
|
20
|
+
export type CreateSharedMemoryOptions = {
|
|
21
|
+
size: number;
|
|
22
|
+
/**
|
|
23
|
+
* `anonymous` keeps the current fd-backed private mapping behavior.
|
|
24
|
+
* `create` and `open` use a named shared-memory object so independent
|
|
25
|
+
* processes can rendezvous by name.
|
|
26
|
+
*/
|
|
27
|
+
mode?: SharedMemoryCreateMode;
|
|
28
|
+
name?: string;
|
|
29
|
+
};
|
|
30
|
+
export type MapSharedMemoryOptions = {
|
|
31
|
+
fd: number;
|
|
32
|
+
name?: string;
|
|
33
|
+
size: number;
|
|
34
|
+
duplicateFd?: boolean;
|
|
35
|
+
};
|
|
36
|
+
export type SharedMemoryConnectionPrimitives<Mapping extends SharedMemoryMapping = SharedMemoryMapping> = {
|
|
37
|
+
runtime: ConnectionRuntime;
|
|
38
|
+
createSharedMemory: (options: number | CreateSharedMemoryOptions) => Mapping;
|
|
39
|
+
mapSharedMemory: (options: MapSharedMemoryOptions) => Mapping;
|
|
40
|
+
unlinkSharedMemory?: (name: string) => boolean;
|
|
41
|
+
};
|
|
42
|
+
export declare const alignToCacheLine: (size: number) => number;
|
|
43
|
+
export declare const readCreateSize: (options: number | CreateSharedMemoryOptions) => number;
|
|
44
|
+
export declare const readCreateName: (options: number | CreateSharedMemoryOptions, fallback: string) => string;
|
|
45
|
+
export declare const readCreateMode: (options: number | CreateSharedMemoryOptions) => SharedMemoryCreateMode;
|
|
46
|
+
export declare const expectSharedMemoryName: (name: string) => string;
|
|
47
|
+
export declare const readRequiredCreateName: (options: number | CreateSharedMemoryOptions) => string;
|
|
48
|
+
export declare const expectPositiveSize: (size: number) => number;
|
|
49
|
+
export declare const expectFd: (fd: number) => number;
|
|
50
|
+
export declare const requireSharedArrayBuffer: (mapping: SharedMemoryMapping) => SharedArrayBuffer;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const CACHE_LINE_SIZE = 64;
|
|
2
|
+
export const alignToCacheLine = (size) => size + ((CACHE_LINE_SIZE - (size % CACHE_LINE_SIZE)) % CACHE_LINE_SIZE);
|
|
3
|
+
export const readCreateSize = (options) => typeof options === "number" ? options : options.size;
|
|
4
|
+
export const readCreateName = (options, fallback) => typeof options === "number" ? fallback : options.name ?? fallback;
|
|
5
|
+
export const readCreateMode = (options) => typeof options === "number" ? "anonymous" : options.mode ?? "anonymous";
|
|
6
|
+
export const expectSharedMemoryName = (name) => {
|
|
7
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
8
|
+
throw new TypeError("shared memory name must be a non-empty string");
|
|
9
|
+
}
|
|
10
|
+
if (name.includes("\0")) {
|
|
11
|
+
throw new TypeError("shared memory name must not contain NUL bytes");
|
|
12
|
+
}
|
|
13
|
+
return name;
|
|
14
|
+
};
|
|
15
|
+
export const readRequiredCreateName = (options) => {
|
|
16
|
+
if (typeof options === "number" || options.name === undefined) {
|
|
17
|
+
throw new TypeError("named shared memory requires a name");
|
|
18
|
+
}
|
|
19
|
+
return expectSharedMemoryName(options.name);
|
|
20
|
+
};
|
|
21
|
+
export const expectPositiveSize = (size) => {
|
|
22
|
+
if (!Number.isFinite(size) || size <= 0) {
|
|
23
|
+
throw new RangeError("shared memory size must be positive");
|
|
24
|
+
}
|
|
25
|
+
return alignToCacheLine(Math.trunc(size));
|
|
26
|
+
};
|
|
27
|
+
export const expectFd = (fd) => {
|
|
28
|
+
if (!Number.isInteger(fd) || fd < 0) {
|
|
29
|
+
throw new RangeError("shared memory fd must be non-negative");
|
|
30
|
+
}
|
|
31
|
+
return fd;
|
|
32
|
+
};
|
|
33
|
+
export const requireSharedArrayBuffer = (mapping) => {
|
|
34
|
+
if (mapping.sab !== undefined)
|
|
35
|
+
return mapping.sab;
|
|
36
|
+
throw new TypeError(`${mapping.runtime} mapping is ${mapping.kind}; a native SAB wrapper is required`);
|
|
37
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type CreateSharedMemoryOptions, type MapSharedMemoryOptions, type SharedMemoryConnectionPrimitives, type SharedMemoryMapping } from "./types.js";
|
|
2
|
+
type DenoWindowsLibrary = {
|
|
3
|
+
symbols: {
|
|
4
|
+
knitting_windows_create_shared_memory: (size: bigint, name: Uint16Array, mode: number, out: Uint8Array) => number;
|
|
5
|
+
knitting_windows_map_shared_memory: (handle: bigint, size: bigint, name: Uint16Array, out: Uint8Array) => number;
|
|
6
|
+
knitting_windows_close_shared_memory: (mapping: Uint8Array) => number;
|
|
7
|
+
};
|
|
8
|
+
close: () => void;
|
|
9
|
+
};
|
|
10
|
+
type BunWindowsLibrary = {
|
|
11
|
+
symbols: {
|
|
12
|
+
knitting_windows_create_shared_memory: (size: bigint, name: Uint16Array, mode: number, out: Uint8Array) => number;
|
|
13
|
+
knitting_windows_map_shared_memory: (handle: bigint, size: bigint, name: Uint16Array, out: Uint8Array) => number;
|
|
14
|
+
knitting_windows_close_shared_memory: (mapping: Uint8Array) => number;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare const isWindowsRuntime: () => boolean;
|
|
18
|
+
export declare const windowsSharedMemoryPrebuildPath: () => string;
|
|
19
|
+
export declare const makeWindowsAnonymousSharedMemoryName: (runtime: string) => string;
|
|
20
|
+
export declare const openDenoWindowsSharedMemoryLibrary: () => DenoWindowsLibrary;
|
|
21
|
+
export declare const openBunWindowsSharedMemoryLibrary: () => BunWindowsLibrary;
|
|
22
|
+
export declare const createDenoWindowsSharedMemory: (options: number | CreateSharedMemoryOptions, lib?: DenoWindowsLibrary) => SharedMemoryMapping<ArrayBuffer>;
|
|
23
|
+
export declare const mapDenoWindowsSharedMemory: (options: MapSharedMemoryOptions, lib?: DenoWindowsLibrary) => SharedMemoryMapping<ArrayBuffer>;
|
|
24
|
+
export declare const createDenoWindowsConnectionPrimitives: (lib?: DenoWindowsLibrary) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
|
|
25
|
+
export declare const createBunWindowsSharedMemory: (options: number | CreateSharedMemoryOptions, lib?: BunWindowsLibrary) => SharedMemoryMapping<ArrayBuffer>;
|
|
26
|
+
export declare const mapBunWindowsSharedMemory: (options: MapSharedMemoryOptions, lib?: BunWindowsLibrary) => SharedMemoryMapping<ArrayBuffer>;
|
|
27
|
+
export declare const createBunWindowsConnectionPrimitives: (lib?: BunWindowsLibrary) => SharedMemoryConnectionPrimitives<SharedMemoryMapping<ArrayBuffer>>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { resolveKnittingPackageAsset } from "./package-assets.js";
|
|
2
|
+
import { expectFd, expectPositiveSize, readCreateMode, readCreateSize, readRequiredCreateName, } from "./types.js";
|
|
3
|
+
const WINDOWS_CREATE_MODE_CREATE = 1;
|
|
4
|
+
const WINDOWS_CREATE_MODE_OPEN = 2;
|
|
5
|
+
const WINDOWS_MAPPING_RECORD_BYTES = 32;
|
|
6
|
+
const POINTER_OFFSET = 0;
|
|
7
|
+
const HANDLE_OFFSET = 8;
|
|
8
|
+
const SIZE_OFFSET = 16;
|
|
9
|
+
const BASE_ADDRESS_MOD_64_OFFSET = 24;
|
|
10
|
+
const FFIType = {
|
|
11
|
+
i32: 5,
|
|
12
|
+
u32: 6,
|
|
13
|
+
i64: 7,
|
|
14
|
+
ptr: 12,
|
|
15
|
+
};
|
|
16
|
+
let anonymousNameCounter = 0;
|
|
17
|
+
const getDeno = () => {
|
|
18
|
+
const deno = globalThis.Deno;
|
|
19
|
+
if (deno === undefined) {
|
|
20
|
+
throw new Error("Deno Windows shared memory primitives can only run in Deno");
|
|
21
|
+
}
|
|
22
|
+
return deno;
|
|
23
|
+
};
|
|
24
|
+
const getBunFFI = () => {
|
|
25
|
+
const ffi = globalThis.Bun?.FFI;
|
|
26
|
+
if (typeof ffi?.dlopen !== "function" ||
|
|
27
|
+
typeof ffi?.toArrayBuffer !== "function") {
|
|
28
|
+
throw new Error("Bun FFI is not available in this runtime");
|
|
29
|
+
}
|
|
30
|
+
return ffi;
|
|
31
|
+
};
|
|
32
|
+
export const isWindowsRuntime = () => {
|
|
33
|
+
const denoOs = globalThis.Deno?.build?.os;
|
|
34
|
+
if (denoOs !== undefined)
|
|
35
|
+
return denoOs === "windows";
|
|
36
|
+
const processPlatform = globalThis.process?.platform;
|
|
37
|
+
return processPlatform === "win32";
|
|
38
|
+
};
|
|
39
|
+
const windowsArch = () => {
|
|
40
|
+
const denoArch = globalThis.Deno?.build?.arch;
|
|
41
|
+
if (denoArch === "x86_64")
|
|
42
|
+
return "x64";
|
|
43
|
+
const processArch = globalThis.process?.arch;
|
|
44
|
+
if (processArch === "x64")
|
|
45
|
+
return processArch;
|
|
46
|
+
throw new Error("Windows shared memory prebuilds support x64 only");
|
|
47
|
+
};
|
|
48
|
+
export const windowsSharedMemoryPrebuildPath = () => resolveKnittingPackageAsset("prebuilds", `win32-${windowsArch()}`, "knitting_windows_shared_memory.dll");
|
|
49
|
+
export const makeWindowsAnonymousSharedMemoryName = (runtime) => {
|
|
50
|
+
const processId = globalThis.process?.pid ??
|
|
51
|
+
globalThis.Deno?.pid ??
|
|
52
|
+
0;
|
|
53
|
+
const next = anonymousNameCounter++;
|
|
54
|
+
const timeTag = Date.now().toString(36);
|
|
55
|
+
const randomTag = Math.random().toString(36).slice(2, 10);
|
|
56
|
+
const runtimeTag = runtime.replace(/[^a-z0-9_-]/gi, "").slice(0, 12) || "js";
|
|
57
|
+
return `Local\\knit_${runtimeTag}_${processId}_${timeTag}_${next}_${randomTag}`;
|
|
58
|
+
};
|
|
59
|
+
const readWindowsCreate = (options, runtime) => {
|
|
60
|
+
const mode = readCreateMode(options);
|
|
61
|
+
if (mode === "anonymous") {
|
|
62
|
+
return {
|
|
63
|
+
mode: WINDOWS_CREATE_MODE_CREATE,
|
|
64
|
+
name: makeWindowsAnonymousSharedMemoryName(runtime),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
mode: mode === "open"
|
|
69
|
+
? WINDOWS_CREATE_MODE_OPEN
|
|
70
|
+
: WINDOWS_CREATE_MODE_CREATE,
|
|
71
|
+
name: readRequiredCreateName(options),
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const encodeWideCString = (value) => {
|
|
75
|
+
const out = new Uint16Array((value?.length ?? 0) + 1);
|
|
76
|
+
if (value === undefined)
|
|
77
|
+
return out;
|
|
78
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
79
|
+
out[index] = value.charCodeAt(index);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
};
|
|
83
|
+
const readWindowsNativeMapping = (record) => {
|
|
84
|
+
const view = new DataView(record.buffer, record.byteOffset, record.byteLength);
|
|
85
|
+
const address = view.getBigUint64(POINTER_OFFSET, true);
|
|
86
|
+
const handle = view.getBigUint64(HANDLE_OFFSET, true);
|
|
87
|
+
const size = view.getBigUint64(SIZE_OFFSET, true);
|
|
88
|
+
if (address === 0n || handle === 0n) {
|
|
89
|
+
throw new Error("Windows shared memory backend returned an empty mapping");
|
|
90
|
+
}
|
|
91
|
+
if (size > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
92
|
+
throw new RangeError("Windows shared memory mapping is too large");
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
address,
|
|
96
|
+
handle,
|
|
97
|
+
size: Number(size),
|
|
98
|
+
baseAddressMod64: view.getUint32(BASE_ADDRESS_MOD_64_OFFSET, true),
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const fdFromHandle = (handle) => handle <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(handle) : 0;
|
|
102
|
+
const checkWindowsResult = (result, message) => {
|
|
103
|
+
if (result !== 0) {
|
|
104
|
+
throw new Error(`${message} failed with Windows error ${result}`);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const createRecord = () => new Uint8Array(WINDOWS_MAPPING_RECORD_BYTES);
|
|
108
|
+
export const openDenoWindowsSharedMemoryLibrary = () => getDeno().dlopen(windowsSharedMemoryPrebuildPath(), {
|
|
109
|
+
knitting_windows_create_shared_memory: {
|
|
110
|
+
parameters: ["u64", "buffer", "u32", "buffer"],
|
|
111
|
+
result: "i32",
|
|
112
|
+
},
|
|
113
|
+
knitting_windows_map_shared_memory: {
|
|
114
|
+
parameters: ["u64", "u64", "buffer", "buffer"],
|
|
115
|
+
result: "i32",
|
|
116
|
+
},
|
|
117
|
+
knitting_windows_close_shared_memory: {
|
|
118
|
+
parameters: ["buffer"],
|
|
119
|
+
result: "i32",
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
export const openBunWindowsSharedMemoryLibrary = () => getBunFFI().dlopen(windowsSharedMemoryPrebuildPath(), {
|
|
123
|
+
knitting_windows_create_shared_memory: {
|
|
124
|
+
args: [FFIType.i64, FFIType.ptr, FFIType.u32, FFIType.ptr],
|
|
125
|
+
returns: FFIType.i32,
|
|
126
|
+
},
|
|
127
|
+
knitting_windows_map_shared_memory: {
|
|
128
|
+
args: [FFIType.i64, FFIType.i64, FFIType.ptr, FFIType.ptr],
|
|
129
|
+
returns: FFIType.i32,
|
|
130
|
+
},
|
|
131
|
+
knitting_windows_close_shared_memory: {
|
|
132
|
+
args: [FFIType.ptr],
|
|
133
|
+
returns: FFIType.i32,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
const closeWindowsMapping = (record, closeSharedMemory) => {
|
|
137
|
+
let closed = false;
|
|
138
|
+
return () => {
|
|
139
|
+
if (closed)
|
|
140
|
+
return;
|
|
141
|
+
closed = true;
|
|
142
|
+
checkWindowsResult(closeSharedMemory(record), "knitting_windows_close_shared_memory");
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
const fromDenoWindowsRecord = (record, name, lib) => {
|
|
146
|
+
const mapping = readWindowsNativeMapping(record);
|
|
147
|
+
const pointer = getDeno().UnsafePointer?.create(mapping.address);
|
|
148
|
+
if (pointer === undefined || pointer === null || typeof pointer !== "object") {
|
|
149
|
+
throw new Error("Deno UnsafePointer.create is not available");
|
|
150
|
+
}
|
|
151
|
+
const arrayBuffer = new (getDeno().UnsafePointerView)(pointer)
|
|
152
|
+
.getArrayBuffer(mapping.size);
|
|
153
|
+
return {
|
|
154
|
+
runtime: "deno",
|
|
155
|
+
fd: fdFromHandle(mapping.handle),
|
|
156
|
+
name,
|
|
157
|
+
size: mapping.size,
|
|
158
|
+
byteLength: arrayBuffer.byteLength,
|
|
159
|
+
buffer: arrayBuffer,
|
|
160
|
+
kind: "external-array-buffer",
|
|
161
|
+
arrayBuffer,
|
|
162
|
+
unsafePointer: pointer,
|
|
163
|
+
baseAddressMod64: mapping.baseAddressMod64,
|
|
164
|
+
close: closeWindowsMapping(record, lib.symbols.knitting_windows_close_shared_memory),
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
const fromBunWindowsRecord = (record, name, lib) => {
|
|
168
|
+
const mapping = readWindowsNativeMapping(record);
|
|
169
|
+
if (mapping.address > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
170
|
+
throw new RangeError("Windows mapping pointer is too large for Bun FFI");
|
|
171
|
+
}
|
|
172
|
+
const arrayBuffer = getBunFFI().toArrayBuffer(Number(mapping.address), 0, mapping.size);
|
|
173
|
+
return {
|
|
174
|
+
runtime: "bun",
|
|
175
|
+
fd: fdFromHandle(mapping.handle),
|
|
176
|
+
name,
|
|
177
|
+
size: mapping.size,
|
|
178
|
+
byteLength: arrayBuffer.byteLength,
|
|
179
|
+
buffer: arrayBuffer,
|
|
180
|
+
kind: "external-array-buffer",
|
|
181
|
+
arrayBuffer,
|
|
182
|
+
unsafePointer: Number(mapping.address),
|
|
183
|
+
baseAddressMod64: mapping.baseAddressMod64,
|
|
184
|
+
close: closeWindowsMapping(record, lib.symbols.knitting_windows_close_shared_memory),
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
export const createDenoWindowsSharedMemory = (options, lib = openDenoWindowsSharedMemoryLibrary()) => {
|
|
188
|
+
const size = expectPositiveSize(readCreateSize(options));
|
|
189
|
+
const { mode, name } = readWindowsCreate(options, "deno");
|
|
190
|
+
const record = createRecord();
|
|
191
|
+
checkWindowsResult(lib.symbols.knitting_windows_create_shared_memory(BigInt(size), encodeWideCString(name), mode, record), "knitting_windows_create_shared_memory");
|
|
192
|
+
return fromDenoWindowsRecord(record, name, lib);
|
|
193
|
+
};
|
|
194
|
+
export const mapDenoWindowsSharedMemory = (options, lib = openDenoWindowsSharedMemoryLibrary()) => {
|
|
195
|
+
const fd = expectFd(options.fd);
|
|
196
|
+
const size = expectPositiveSize(options.size);
|
|
197
|
+
const record = createRecord();
|
|
198
|
+
checkWindowsResult(lib.symbols.knitting_windows_map_shared_memory(BigInt(fd), BigInt(size), encodeWideCString(options.name), record), "knitting_windows_map_shared_memory");
|
|
199
|
+
return fromDenoWindowsRecord(record, options.name, lib);
|
|
200
|
+
};
|
|
201
|
+
export const createDenoWindowsConnectionPrimitives = (lib = openDenoWindowsSharedMemoryLibrary()) => ({
|
|
202
|
+
runtime: "deno",
|
|
203
|
+
createSharedMemory: (options) => createDenoWindowsSharedMemory(options, lib),
|
|
204
|
+
mapSharedMemory: (options) => mapDenoWindowsSharedMemory(options, lib),
|
|
205
|
+
});
|
|
206
|
+
export const createBunWindowsSharedMemory = (options, lib = openBunWindowsSharedMemoryLibrary()) => {
|
|
207
|
+
const size = expectPositiveSize(readCreateSize(options));
|
|
208
|
+
const { mode, name } = readWindowsCreate(options, "bun");
|
|
209
|
+
const record = createRecord();
|
|
210
|
+
checkWindowsResult(lib.symbols.knitting_windows_create_shared_memory(BigInt(size), encodeWideCString(name), mode, record), "knitting_windows_create_shared_memory");
|
|
211
|
+
return fromBunWindowsRecord(record, name, lib);
|
|
212
|
+
};
|
|
213
|
+
export const mapBunWindowsSharedMemory = (options, lib = openBunWindowsSharedMemoryLibrary()) => {
|
|
214
|
+
const fd = expectFd(options.fd);
|
|
215
|
+
const size = expectPositiveSize(options.size);
|
|
216
|
+
const record = createRecord();
|
|
217
|
+
checkWindowsResult(lib.symbols.knitting_windows_map_shared_memory(BigInt(fd), BigInt(size), encodeWideCString(options.name), record), "knitting_windows_map_shared_memory");
|
|
218
|
+
return fromBunWindowsRecord(record, options.name, lib);
|
|
219
|
+
};
|
|
220
|
+
export const createBunWindowsConnectionPrimitives = (lib = openBunWindowsSharedMemoryLibrary()) => ({
|
|
221
|
+
runtime: "bun",
|
|
222
|
+
createSharedMemory: (options) => createBunWindowsSharedMemory(options, lib),
|
|
223
|
+
mapSharedMemory: (options) => mapBunWindowsSharedMemory(options, lib),
|
|
224
|
+
});
|
|
@@ -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;
|