@volter/browser-wali 0.1.0

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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -0
  3. package/dist/browser-wali-command.d.ts +22 -0
  4. package/dist/browser-wali-command.d.ts.map +1 -0
  5. package/dist/browser-wali-command.js +44 -0
  6. package/dist/browser-wali-command.js.map +1 -0
  7. package/dist/capabilities.d.ts +3 -0
  8. package/dist/capabilities.d.ts.map +1 -0
  9. package/dist/capabilities.js +23 -0
  10. package/dist/capabilities.js.map +1 -0
  11. package/dist/coherent-file-locks.d.ts +23 -0
  12. package/dist/coherent-file-locks.d.ts.map +1 -0
  13. package/dist/coherent-file-locks.js +134 -0
  14. package/dist/coherent-file-locks.js.map +1 -0
  15. package/dist/events.d.ts +30 -0
  16. package/dist/events.d.ts.map +1 -0
  17. package/dist/events.js +289 -0
  18. package/dist/events.js.map +1 -0
  19. package/dist/filesystem-protocol.d.ts +48 -0
  20. package/dist/filesystem-protocol.d.ts.map +1 -0
  21. package/dist/filesystem-protocol.js +27 -0
  22. package/dist/filesystem-protocol.js.map +1 -0
  23. package/dist/filesystem-remote.d.ts +65 -0
  24. package/dist/filesystem-remote.d.ts.map +1 -0
  25. package/dist/filesystem-remote.js +271 -0
  26. package/dist/filesystem-remote.js.map +1 -0
  27. package/dist/filesystem-worker.d.ts +2 -0
  28. package/dist/filesystem-worker.d.ts.map +1 -0
  29. package/dist/filesystem-worker.js +269 -0
  30. package/dist/filesystem-worker.js.map +1 -0
  31. package/dist/filesystem.d.ts +115 -0
  32. package/dist/filesystem.d.ts.map +1 -0
  33. package/dist/filesystem.js +1179 -0
  34. package/dist/filesystem.js.map +1 -0
  35. package/dist/index.d.ts +20 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +18 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/inspect.d.ts +7 -0
  40. package/dist/inspect.d.ts.map +1 -0
  41. package/dist/inspect.js +32 -0
  42. package/dist/inspect.js.map +1 -0
  43. package/dist/local-sockets.d.ts +13 -0
  44. package/dist/local-sockets.d.ts.map +1 -0
  45. package/dist/local-sockets.js +139 -0
  46. package/dist/local-sockets.js.map +1 -0
  47. package/dist/loopback-host.d.ts +14 -0
  48. package/dist/loopback-host.d.ts.map +1 -0
  49. package/dist/loopback-host.js +476 -0
  50. package/dist/loopback-host.js.map +1 -0
  51. package/dist/loopback-protocol.d.ts +41 -0
  52. package/dist/loopback-protocol.d.ts.map +1 -0
  53. package/dist/loopback-protocol.js +2 -0
  54. package/dist/loopback-protocol.js.map +1 -0
  55. package/dist/memory.d.ts +39 -0
  56. package/dist/memory.d.ts.map +1 -0
  57. package/dist/memory.js +520 -0
  58. package/dist/memory.js.map +1 -0
  59. package/dist/module-layout.d.ts +9 -0
  60. package/dist/module-layout.d.ts.map +1 -0
  61. package/dist/module-layout.js +105 -0
  62. package/dist/module-layout.js.map +1 -0
  63. package/dist/network-protocol.d.ts +43 -0
  64. package/dist/network-protocol.d.ts.map +1 -0
  65. package/dist/network-protocol.js +26 -0
  66. package/dist/network-protocol.js.map +1 -0
  67. package/dist/network-worker.d.ts +2 -0
  68. package/dist/network-worker.d.ts.map +1 -0
  69. package/dist/network-worker.js +943 -0
  70. package/dist/network-worker.js.map +1 -0
  71. package/dist/network.d.ts +52 -0
  72. package/dist/network.d.ts.map +1 -0
  73. package/dist/network.js +427 -0
  74. package/dist/network.js.map +1 -0
  75. package/dist/process-adapter.d.ts +14 -0
  76. package/dist/process-adapter.d.ts.map +1 -0
  77. package/dist/process-adapter.js +45 -0
  78. package/dist/process-adapter.js.map +1 -0
  79. package/dist/process-bridge.d.ts +54 -0
  80. package/dist/process-bridge.d.ts.map +1 -0
  81. package/dist/process-bridge.js +150 -0
  82. package/dist/process-bridge.js.map +1 -0
  83. package/dist/program.d.ts +49 -0
  84. package/dist/program.d.ts.map +1 -0
  85. package/dist/program.js +204 -0
  86. package/dist/program.js.map +1 -0
  87. package/dist/runtime.d.ts +9 -0
  88. package/dist/runtime.d.ts.map +1 -0
  89. package/dist/runtime.js +528 -0
  90. package/dist/runtime.js.map +1 -0
  91. package/dist/shared-pipes.d.ts +44 -0
  92. package/dist/shared-pipes.d.ts.map +1 -0
  93. package/dist/shared-pipes.js +306 -0
  94. package/dist/shared-pipes.js.map +1 -0
  95. package/dist/snapshot-filesystem.d.ts +31 -0
  96. package/dist/snapshot-filesystem.d.ts.map +1 -0
  97. package/dist/snapshot-filesystem.js +247 -0
  98. package/dist/snapshot-filesystem.js.map +1 -0
  99. package/dist/stdin.d.ts +36 -0
  100. package/dist/stdin.d.ts.map +1 -0
  101. package/dist/stdin.js +143 -0
  102. package/dist/stdin.js.map +1 -0
  103. package/dist/symlink.d.ts +8 -0
  104. package/dist/symlink.d.ts.map +1 -0
  105. package/dist/symlink.js +66 -0
  106. package/dist/symlink.js.map +1 -0
  107. package/dist/system.d.ts +41 -0
  108. package/dist/system.d.ts.map +1 -0
  109. package/dist/system.js +305 -0
  110. package/dist/system.js.map +1 -0
  111. package/dist/thread-coordinator-worker.d.ts +25 -0
  112. package/dist/thread-coordinator-worker.d.ts.map +1 -0
  113. package/dist/thread-coordinator-worker.js +49 -0
  114. package/dist/thread-coordinator-worker.js.map +1 -0
  115. package/dist/thread-worker.d.ts +2 -0
  116. package/dist/thread-worker.d.ts.map +1 -0
  117. package/dist/thread-worker.js +725 -0
  118. package/dist/thread-worker.js.map +1 -0
  119. package/dist/threads.d.ts +44 -0
  120. package/dist/threads.d.ts.map +1 -0
  121. package/dist/threads.js +154 -0
  122. package/dist/threads.js.map +1 -0
  123. package/dist/types.d.ts +112 -0
  124. package/dist/types.d.ts.map +1 -0
  125. package/dist/types.js +2 -0
  126. package/dist/types.js.map +1 -0
  127. package/dist/websocket-peer.d.ts +38 -0
  128. package/dist/websocket-peer.d.ts.map +1 -0
  129. package/dist/websocket-peer.js +407 -0
  130. package/dist/websocket-peer.js.map +1 -0
  131. package/dist/worker-program-protocol.d.ts +71 -0
  132. package/dist/worker-program-protocol.d.ts.map +1 -0
  133. package/dist/worker-program-protocol.js +2 -0
  134. package/dist/worker-program-protocol.js.map +1 -0
  135. package/dist/worker-program-worker.d.ts +2 -0
  136. package/dist/worker-program-worker.d.ts.map +1 -0
  137. package/dist/worker-program-worker.js +109 -0
  138. package/dist/worker-program-worker.js.map +1 -0
  139. package/dist/worker-program.d.ts +58 -0
  140. package/dist/worker-program.d.ts.map +1 -0
  141. package/dist/worker-program.js +425 -0
  142. package/dist/worker-program.js.map +1 -0
  143. package/package.json +23 -0
@@ -0,0 +1,725 @@
1
+ import { WaliMemoryManager } from "./memory.js";
2
+ import { WaliNetworkHost } from "./network.js";
3
+ import { SharedWaliStdinReader } from "./stdin.js";
4
+ import { WaliSystemHost } from "./system.js";
5
+ import { WaliFileDescriptorTable } from "./filesystem.js";
6
+ import { WaliFilesystemClient, WaliSharedFileDescriptorHost } from "./filesystem-remote.js";
7
+ import { WaliSharedPipeHost } from "./shared-pipes.js";
8
+ import { BROWSER_PROCESS_RESPONSE_BYTES, applyProcessFilesystemPatch, closeSharedPipe, decodeProcessSnapshot, retainSharedPipe, } from "./process-bridge.js";
9
+ class ThreadExit extends Error {
10
+ }
11
+ const scope = globalThis;
12
+ let instance;
13
+ let memory;
14
+ let descriptors = [];
15
+ let processPid = 2;
16
+ let processExecutablePath = "/usr/bin/wali-program";
17
+ let processArgvBytes = [new TextEncoder().encode("wali-program\0")];
18
+ let processCwd = "/";
19
+ let threadId = 3;
20
+ let mmapCursor;
21
+ let sharedMmapRegistry;
22
+ let fatalState;
23
+ let networkChannel;
24
+ let filesystemChannel;
25
+ let stdin;
26
+ let stdioAliasRegistry;
27
+ let sharedPipes;
28
+ let childPidAllocator;
29
+ let browserProcessPort;
30
+ let outputPort;
31
+ let slotIndex = -1;
32
+ let coordinatorPort;
33
+ let diagnosticLength;
34
+ let diagnosticBytes;
35
+ const localStdioAliases = new Map();
36
+ const localSpecialDescriptors = new Set();
37
+ let nextLocalDescriptor = 60_000;
38
+ const reportedUnsupportedImports = new Set();
39
+ const sleepWord = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
40
+ const diagnosticHistory = [];
41
+ const DIAGNOSTIC_HISTORY_LIMIT = 32;
42
+ const DETACHED_THREAD_RUNTIME_NOTICE = new TextEncoder().encode("== Detached Thread End: unmapself TBD");
43
+ scope.onmessage = (event) => {
44
+ if (event.data.kind === "initialize") {
45
+ void initialize(event.data);
46
+ return;
47
+ }
48
+ if (!instance || !memory) {
49
+ scope.postMessage({ kind: "error", message: "WALI pthread worker was started before initialization." });
50
+ return;
51
+ }
52
+ runThread(event.data);
53
+ };
54
+ function runThread(message) {
55
+ threadId = message.tid;
56
+ diagnosticHistory.length = 0;
57
+ writeDiagnostic(`tid ${threadId} started function ${message.functionPointer}`);
58
+ try {
59
+ const currentInstance = instance;
60
+ if (!currentInstance)
61
+ throw new Error("WALI pthread worker was started before initialization.");
62
+ const table = currentInstance.exports.__indirect_function_table;
63
+ const indirect = table instanceof WebAssembly.Table ? table.get(message.functionPointer) : undefined;
64
+ if (typeof indirect === "function") {
65
+ indirect(threadId, message.argumentPointer);
66
+ }
67
+ else {
68
+ const start = currentInstance.exports.__wasm_thread_start_libc;
69
+ if (typeof start !== "function")
70
+ throw new Error("WALI module does not expose its pthread entry point or function table.");
71
+ start(threadId, message.argumentPointer);
72
+ }
73
+ writeDiagnostic(`tid ${threadId} completed`);
74
+ coordinatorPort?.postMessage({ kind: "released", slotIndex });
75
+ scope.postMessage({ kind: "done", tid: threadId });
76
+ }
77
+ catch (error) {
78
+ coordinatorPort?.postMessage({ kind: "released", slotIndex });
79
+ if (error instanceof ThreadExit) {
80
+ writeDiagnostic(`tid ${threadId} exited`);
81
+ scope.postMessage({ kind: "done", tid: threadId });
82
+ }
83
+ else {
84
+ if (fatalState && Atomics.compareExchange(fatalState, 1, -1, slotIndex) === -1) {
85
+ Atomics.store(fatalState, 0, 1);
86
+ Atomics.notify(fatalState, 0);
87
+ }
88
+ const detail = error instanceof Error ? error.stack ?? error.message : String(error);
89
+ const recentCalls = diagnosticHistory.join("\n");
90
+ const errorMessage = `spawn(function=${message.functionPointer}, argument=${message.argumentPointer}): ${detail}`
91
+ + (recentCalls ? `\nRecent WALI activity:\n${recentCalls}` : "");
92
+ writeDiagnostic(`tid ${threadId} error: ${errorMessage}`);
93
+ console.error(`[WALI pthread ${threadId}] ${errorMessage}`);
94
+ outputPort?.postMessage({ type: "error", message: errorMessage, tid: threadId });
95
+ scope.postMessage({ kind: "error", message: errorMessage, tid: threadId });
96
+ }
97
+ }
98
+ }
99
+ async function initialize(message) {
100
+ try {
101
+ memory = message.memory;
102
+ mmapCursor = message.mmapCursor;
103
+ sharedMmapRegistry = message.sharedMmapRegistry;
104
+ fatalState = new Int32Array(message.fatalState);
105
+ descriptors = message.imports;
106
+ processPid = message.pid;
107
+ processExecutablePath = message.executablePath;
108
+ processArgvBytes = message.argv.map((argument) => new TextEncoder().encode(`${argument}\0`));
109
+ processCwd = message.cwd;
110
+ stdioAliasRegistry = new Int32Array(message.stdioAliasRegistry);
111
+ sharedPipes = message.sharedPipes;
112
+ childPidAllocator = new Int32Array(message.childPidAllocator);
113
+ browserProcessPort = message.browserProcessPort;
114
+ browserProcessPort?.start();
115
+ outputPort = message.outputPort;
116
+ outputPort?.start();
117
+ slotIndex = message.slotIndex;
118
+ coordinatorPort = message.coordinatorPort;
119
+ coordinatorPort.onmessage = (event) => runThread(event.data);
120
+ coordinatorPort.start();
121
+ diagnosticLength = new Int32Array(message.diagnostic, 0, 1);
122
+ diagnosticBytes = new Uint8Array(message.diagnostic, Int32Array.BYTES_PER_ELEMENT);
123
+ stdin = message.stdin ? new SharedWaliStdinReader(message.stdin) : undefined;
124
+ networkChannel = message.network;
125
+ filesystemChannel = message.filesystem;
126
+ const imports = createImports();
127
+ instance = await WebAssembly.instantiate(message.module, imports);
128
+ scope.postMessage({ kind: "ready" });
129
+ }
130
+ catch (error) {
131
+ scope.postMessage({ kind: "error", message: error instanceof Error ? error.stack ?? error.message : String(error) });
132
+ }
133
+ }
134
+ function createImports() {
135
+ const wali = {};
136
+ const handlers = threadHandlers();
137
+ if (networkChannel) {
138
+ const fallback = { ...handlers };
139
+ Object.assign(handlers, new WaliNetworkHost(requiredMemory, networkChannel).imports(fallback));
140
+ }
141
+ for (const descriptor of descriptors) {
142
+ if (descriptor.module !== "wali" || descriptor.kind !== "function")
143
+ continue;
144
+ const handler = handlers[descriptor.name] ?? (() => {
145
+ if (!reportedUnsupportedImports.has(descriptor.name)) {
146
+ reportedUnsupportedImports.add(descriptor.name);
147
+ console.error(`[WALI pthread ${threadId}] unsupported import: ${descriptor.name}`);
148
+ }
149
+ return -38n;
150
+ });
151
+ wali[descriptor.name] = (...args) => {
152
+ const call = `${descriptor.name}(${args.map(String).join(",")})`;
153
+ writeDiagnostic(call);
154
+ try {
155
+ const result = handler(...args);
156
+ writeDiagnostic(`${call} => ${String(result)}`);
157
+ return result;
158
+ }
159
+ catch (error) {
160
+ writeDiagnostic(`${call} threw ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
161
+ throw error;
162
+ }
163
+ };
164
+ }
165
+ return {
166
+ wali,
167
+ env: {
168
+ memory: requiredMemory(),
169
+ _Unwind_Backtrace: () => 5,
170
+ _Unwind_FindEnclosingFunction: (programCounter) => programCounter,
171
+ _Unwind_GetCFA: () => 0,
172
+ },
173
+ };
174
+ }
175
+ function threadHandlers() {
176
+ const systemImports = new WaliSystemHost(requiredMemory, processPid, stdin, fatalState).imports();
177
+ const ioctl = systemImports.SYS_ioctl;
178
+ const filesystemClient = filesystemChannel ? new WaliFilesystemClient(filesystemChannel) : undefined;
179
+ const fileDescriptors = filesystemChannel ? new WaliFileDescriptorTable({
180
+ cwd: processCwd,
181
+ stdin: stdin ?? new Uint8Array(),
182
+ executablePath: processExecutablePath,
183
+ memory: requiredMemory,
184
+ emit: (fd, value) => {
185
+ if (fd !== 1 && fd !== 2)
186
+ return -9n;
187
+ const chunk = value.slice();
188
+ emitThread(fd === 1 ? "stdout" : "stderr", chunk);
189
+ return BigInt(value.byteLength);
190
+ },
191
+ ...(stdioAliasRegistry ? { stdioAliasRegistry } : {}),
192
+ specialDescriptorMinimum: 10_000,
193
+ descriptorMaximum: 19_999,
194
+ }) : undefined;
195
+ const localFileImports = fileDescriptors?.imports() ?? {};
196
+ const sharedFileHost = filesystemClient
197
+ ? new WaliSharedFileDescriptorHost(filesystemClient, stdioAliasRegistry) : undefined;
198
+ const memoryManager = new WaliMemoryManager(requiredMemory, (fd, offset, length) => sharedFileHost?.readForMapping(fd, offset, length)
199
+ ?? fileDescriptors?.readForMapping(fd, offset, length), requiredMmapCursor(), (fd) => sharedFileHost?.mappingPath(fd) ?? fileDescriptors?.mappingPath(fd), (path, offset, value) => sharedFileHost?.writeForMapping(path, offset, value)
200
+ ?? fileDescriptors?.writeForMapping(path, offset, value) ?? false, requiredSharedMmapRegistry());
201
+ const fileImports = sharedFileHost?.imports(localFileImports) ?? localFileImports;
202
+ const sharedPipeHost = sharedPipes ? new WaliSharedPipeHost(requiredMemory, sharedPipes) : undefined;
203
+ const pipeImports = sharedPipeHost?.imports(fileImports) ?? fileImports;
204
+ const childProcesses = new Map();
205
+ return {
206
+ ...systemImports,
207
+ __init: () => 0,
208
+ __deinit: () => 0,
209
+ __proc_exit: () => { throw new ThreadExit(); },
210
+ __get_init_envfile: () => 0,
211
+ __cl_get_argc: () => processArgvBytes.length,
212
+ __cl_get_argv_len: (index) => processArgvBytes[Number(index)]?.byteLength ?? 0,
213
+ __cl_copy_argv: (pointer, index) => {
214
+ const value = processArgvBytes[Number(index)];
215
+ if (!value || Number(pointer) < 0 || Number(pointer) + value.byteLength > bytes().byteLength)
216
+ return -1;
217
+ bytes().set(value, Number(pointer));
218
+ return 0;
219
+ },
220
+ __wasm_thread_spawn: (functionPointer, argumentPointer) => spawnNestedThread(Number(functionPointer), Number(argumentPointer)),
221
+ SYS_exit: () => { throw new ThreadExit(); },
222
+ SYS_exit_group: (status) => {
223
+ if (fatalState) {
224
+ Atomics.store(fatalState, 2, Number(status) & 0xff);
225
+ Atomics.store(fatalState, 0, 2);
226
+ Atomics.notify(fatalState, 0);
227
+ }
228
+ throw new ThreadExit();
229
+ },
230
+ SYS_getpid: () => BigInt(processPid),
231
+ SYS_getppid: () => 1n,
232
+ SYS_getpgid: (pid) => processIdentity(Number(pid)),
233
+ SYS_getsid: (pid) => processIdentity(Number(pid)),
234
+ SYS_gettid: () => BigInt(threadId),
235
+ SYS_getuid: () => 1000n,
236
+ SYS_geteuid: () => 1000n,
237
+ SYS_getgid: () => 1000n,
238
+ SYS_getegid: () => 1000n,
239
+ SYS_set_tid_address: () => BigInt(threadId),
240
+ SYS_set_robust_list: () => 0n,
241
+ SYS_sched_yield: () => 0n,
242
+ SYS_sched_setscheduler: () => 0n,
243
+ SYS_prctl: () => 0n,
244
+ SYS_tkill: () => 0n,
245
+ SYS_rt_sigaction: () => 0n,
246
+ SYS_rt_sigprocmask: (_how, _set, previous, size) => zeroMemory(Number(previous), Number(size)),
247
+ SYS_sigaltstack: (_next, previous) => zeroMemory(Number(previous), 12),
248
+ SYS_mprotect: () => 0n,
249
+ SYS_munmap: () => 0n,
250
+ SYS_futex: (address, operation, value, timeout) => futex(Number(address), Number(operation), Number(value), Number(timeout)),
251
+ SYS_clock_gettime: (clock, pointer) => clockGettime(Number(clock), Number(pointer)),
252
+ SYS_clock_nanosleep: (clock, flags, request, remaining) => clockNanosleep(Number(clock), Number(flags), Number(request), Number(remaining)),
253
+ SYS_nanosleep: (request, remaining) => clockNanosleep(1, 0, Number(request), Number(remaining)),
254
+ SYS_epoll_wait: (_fd, _events, _maximum, timeout) => idlePoll(Number(timeout)),
255
+ SYS_epoll_pwait: (_fd, _events, _maximum, timeout) => idlePoll(Number(timeout)),
256
+ SYS_gettimeofday: (pointer) => gettimeofday(Number(pointer)),
257
+ SYS_getrandom: (pointer, length) => getrandom(Number(pointer), Number(length)),
258
+ SYS_browser_spawn: (pathname, argvPointer, envPointer, cwdPointer, stdinFd, stdoutFd, stderrFd) => {
259
+ if (!browserProcessPort || !childPidAllocator || !sharedPipeHost || !filesystemClient)
260
+ return -38;
261
+ const executablePath = readCString(Number(pathname));
262
+ const argv = readCStringArray(Number(argvPointer));
263
+ if (argv.length === 0)
264
+ argv.push(executablePath);
265
+ const env = {};
266
+ for (const entry of readCStringArray(Number(envPointer))) {
267
+ const separator = entry.indexOf("=");
268
+ if (separator > 0)
269
+ env[entry.slice(0, separator)] = entry.slice(separator + 1);
270
+ }
271
+ const childPid = Atomics.add(childPidAllocator, 0, 1);
272
+ const process = {
273
+ control: new SharedArrayBuffer(8 * Int32Array.BYTES_PER_ELEMENT),
274
+ response: new SharedArrayBuffer(BROWSER_PROCESS_RESPONSE_BYTES),
275
+ snapshotApplied: false,
276
+ };
277
+ const exportPipe = (fd, role) => {
278
+ const handle = sharedPipeHost.exportBrowserProcessPipe(fd);
279
+ return handle?.role === role ? retainSharedPipe(handle) : undefined;
280
+ };
281
+ const stdin = exportPipe(Number(stdinFd), "reader");
282
+ const stdout = exportPipe(Number(stdoutFd), "writer");
283
+ const stderr = exportPipe(Number(stderrFd), "writer");
284
+ childProcesses.set(childPid, process);
285
+ try {
286
+ const message = {
287
+ type: "spawn",
288
+ pid: childPid,
289
+ executablePath,
290
+ argv,
291
+ env,
292
+ cwd: Number(cwdPointer) === 0 ? processCwd : readCString(Number(cwdPointer)),
293
+ control: process.control,
294
+ response: process.response,
295
+ // Mutations are already mirrored continuously by the process
296
+ // filesystem coordinator; helper launch must not serialize the VFS.
297
+ filesystemPatch: { files: [], removed: [] },
298
+ ...(stdin ? { stdin } : {}),
299
+ ...(stdout ? { stdout } : {}),
300
+ ...(stderr ? { stderr } : {}),
301
+ };
302
+ browserProcessPort.postMessage(message);
303
+ return childPid;
304
+ }
305
+ catch {
306
+ childProcesses.delete(childPid);
307
+ if (stdin)
308
+ closeSharedPipe(stdin);
309
+ if (stdout)
310
+ closeSharedPipe(stdout);
311
+ if (stderr)
312
+ closeSharedPipe(stderr);
313
+ return -5;
314
+ }
315
+ },
316
+ SYS_wait4: (requestedPid, status, options) => filesystemClient
317
+ ? waitForChild(childProcesses, filesystemClient, Number(requestedPid), Number(status), Number(options))
318
+ : -38n,
319
+ SYS_kill: (requestedPid, signal) => {
320
+ const childPid = Math.abs(Number(requestedPid));
321
+ if (!childProcesses.has(childPid) || !browserProcessPort)
322
+ return -3n;
323
+ browserProcessPort.postMessage({ type: "kill", pid: childPid, signal: Number(signal) });
324
+ return 0n;
325
+ },
326
+ SYS_setpgid: () => 0n,
327
+ SYS_open: () => -2n,
328
+ SYS_openat: () => -2n,
329
+ SYS_stat: () => -2n,
330
+ SYS_lstat: () => -2n,
331
+ SYS_access: () => -2n,
332
+ SYS_getcwd: (pointer, size) => getcwd(Number(pointer), Number(size)),
333
+ SYS_mkdir: () => 0n,
334
+ SYS_mkdirat: () => 0n,
335
+ SYS_statfs: (_path, pointer) => statfs(Number(pointer)),
336
+ SYS_fstatfs: (_fd, pointer) => statfs(Number(pointer)),
337
+ SYS_inotify_init: () => allocateSpecialDescriptor(),
338
+ SYS_inotify_init1: () => allocateSpecialDescriptor(),
339
+ SYS_inotify_add_watch: (fd) => localSpecialDescriptors.has(Number(fd)) ? 1n : -9n,
340
+ SYS_inotify_rm_watch: (fd) => localSpecialDescriptors.has(Number(fd)) ? 0n : -9n,
341
+ SYS_readlink: (path, pointer, length) => readlink(Number(path), Number(pointer), Number(length)),
342
+ SYS_read: (fd, pointer, length) => read(Number(fd), Number(pointer), Number(length)),
343
+ SYS_poll: (pointer, count, timeout) => poll(Number(pointer), Number(count), Number(timeout)),
344
+ SYS_write: (fd, pointer, length) => write(Number(fd), Number(pointer), Number(length)),
345
+ SYS_writev: (fd, pointer, count) => writev(Number(fd), Number(pointer), Number(count)),
346
+ SYS_dup: (fd) => duplicateDescriptor(Number(fd)),
347
+ SYS_dup2: (fd, target) => duplicateDescriptor(Number(fd), Number(target)),
348
+ SYS_dup3: (fd, target) => duplicateDescriptor(Number(fd), Number(target)),
349
+ SYS_close: (fd) => closeDescriptor(Number(fd)),
350
+ SYS_fcntl: (fd, command, argument) => fcntl(Number(fd), Number(command), Number(BigInt.asIntN(32, BigInt(argument)))),
351
+ ...memoryManager.imports(),
352
+ ...pipeImports,
353
+ SYS_ioctl: (fd, request, argument) => sharedPipeHost?.ioctl(Number(fd), Number(request))
354
+ ?? fileDescriptors?.ioctl(Number(fd), Number(request))
355
+ ?? ioctl(stdioDescriptor(Number(fd)) ?? Number(fd), request, argument),
356
+ };
357
+ }
358
+ function processIdentity(pid) {
359
+ return pid === 0 || pid === processPid ? BigInt(processPid) : -3n;
360
+ }
361
+ function spawnNestedThread(functionPointer, argumentPointer) {
362
+ if (!coordinatorPort)
363
+ return -11;
364
+ const response = new Int32Array(new SharedArrayBuffer(2 * Int32Array.BYTES_PER_ELEMENT));
365
+ coordinatorPort.postMessage({ kind: "spawn", functionPointer, argumentPointer, response: response.buffer });
366
+ Atomics.wait(response, 0, 0);
367
+ return Atomics.load(response, 1);
368
+ }
369
+ function writeDiagnostic(message) {
370
+ if (!diagnosticLength || !diagnosticBytes)
371
+ return;
372
+ diagnosticHistory.push(message);
373
+ if (diagnosticHistory.length > DIAGNOSTIC_HISTORY_LIMIT)
374
+ diagnosticHistory.shift();
375
+ const encoded = new TextEncoder().encode(diagnosticHistory.join("\n")).subarray(-diagnosticBytes.byteLength);
376
+ diagnosticBytes.fill(0);
377
+ diagnosticBytes.set(encoded);
378
+ Atomics.store(diagnosticLength, 0, encoded.byteLength);
379
+ }
380
+ function readlink(pathPointer, outputPointer, length) {
381
+ if (readCString(pathPointer) !== "/proc/self/exe")
382
+ return -2n;
383
+ const value = new TextEncoder().encode(processExecutablePath).subarray(0, Math.max(0, length));
384
+ bytes().set(value, outputPointer);
385
+ return BigInt(value.byteLength);
386
+ }
387
+ function getcwd(pointer, size) {
388
+ const value = new TextEncoder().encode(`${processCwd}\0`);
389
+ if (size < value.byteLength)
390
+ return -34n;
391
+ bytes().set(value, pointer);
392
+ return BigInt(value.byteLength);
393
+ }
394
+ function statfs(pointer) {
395
+ if (pointer < 0 || pointer + 120 > bytes().byteLength)
396
+ return -14n;
397
+ bytes().fill(0, pointer, pointer + 120);
398
+ view().setBigInt64(pointer, 0x01021994n, true);
399
+ view().setBigInt64(pointer + 8, 4096n, true);
400
+ view().setBigUint64(pointer + 16, 262144n, true);
401
+ view().setBigUint64(pointer + 24, 196608n, true);
402
+ view().setBigUint64(pointer + 32, 196608n, true);
403
+ view().setBigUint64(pointer + 40, 1048576n, true);
404
+ view().setBigUint64(pointer + 48, 1048000n, true);
405
+ view().setBigUint64(pointer + 64, 255n, true);
406
+ view().setBigUint64(pointer + 72, 4096n, true);
407
+ return 0n;
408
+ }
409
+ function read(fd, pointer, length) {
410
+ if (stdioDescriptor(fd) !== 0)
411
+ return -9n;
412
+ if (!stdin)
413
+ return 0n;
414
+ const chunk = stdin.read(length);
415
+ bytes().set(chunk, pointer);
416
+ return BigInt(chunk.byteLength);
417
+ }
418
+ function poll(pointer, count, timeout) {
419
+ if (!Number.isSafeInteger(count) || count < 0 || !Number.isSafeInteger(timeout))
420
+ return -22n;
421
+ const scan = () => {
422
+ let ready = 0;
423
+ for (let index = 0; index < count; index += 1) {
424
+ const offset = pointer + index * 8;
425
+ const fd = view().getInt32(offset, true);
426
+ const stdio = stdioDescriptor(fd);
427
+ const events = view().getInt16(offset + 4, true);
428
+ let returned = 0;
429
+ if (stdio === 0 && (events & 1) && stdin?.poll())
430
+ returned |= 1;
431
+ if ((stdio === 1 || stdio === 2) && (events & 4))
432
+ returned |= 4;
433
+ if (stdio === undefined)
434
+ returned |= 0x20;
435
+ view().setInt16(offset + 6, returned, true);
436
+ if (returned !== 0)
437
+ ready += 1;
438
+ }
439
+ return ready;
440
+ };
441
+ const ready = scan();
442
+ if (ready !== 0 || timeout === 0)
443
+ return BigInt(ready);
444
+ stdin?.wait(timeout);
445
+ return BigInt(scan());
446
+ }
447
+ function readCString(pointer) {
448
+ const memory = bytes();
449
+ const end = memory.indexOf(0, pointer);
450
+ if (pointer < 0 || end < pointer || end - pointer > 1_048_576)
451
+ throw new WebAssembly.RuntimeError("WALI string is not null terminated.");
452
+ return new TextDecoder("utf-8", { fatal: true }).decode(Uint8Array.from(memory.subarray(pointer, end)));
453
+ }
454
+ function readCStringArray(pointer) {
455
+ if (pointer === 0)
456
+ return [];
457
+ const values = [];
458
+ for (let index = 0; index < 65_536; index += 1) {
459
+ const offset = pointer + index * 4;
460
+ if (offset < 0 || offset + 4 > bytes().byteLength)
461
+ throw new WebAssembly.RuntimeError("WALI process string array is out of bounds.");
462
+ const item = view().getUint32(offset, true);
463
+ if (item === 0)
464
+ return values;
465
+ values.push(readCString(item));
466
+ }
467
+ throw new WebAssembly.RuntimeError("WALI process string array is not terminated.");
468
+ }
469
+ function waitForChild(children, filesystem, requestedPid, statusPointer, options) {
470
+ let childPid;
471
+ let process;
472
+ if (requestedPid === -1) {
473
+ const entry = children.entries().next().value;
474
+ if (!entry)
475
+ return -10n;
476
+ [childPid, process] = entry;
477
+ }
478
+ else {
479
+ const exact = children.get(requestedPid);
480
+ if (!exact)
481
+ return -10n;
482
+ childPid = requestedPid;
483
+ process = exact;
484
+ }
485
+ const control = new Int32Array(process.control);
486
+ if (Atomics.load(control, 0) === 0) {
487
+ if ((options & 1) !== 0)
488
+ return 0n;
489
+ while (Atomics.load(control, 0) === 0)
490
+ Atomics.wait(control, 0, 0, 1_000);
491
+ }
492
+ if (!process.snapshotApplied) {
493
+ process.snapshotApplied = true;
494
+ const patch = decodeProcessSnapshot(process);
495
+ if (patch)
496
+ applyProcessFilesystemPatch(filesystem, patch);
497
+ }
498
+ if (statusPointer !== 0) {
499
+ if (statusPointer < 0 || statusPointer + 4 > bytes().byteLength)
500
+ return -14n;
501
+ view().setInt32(statusPointer, (Atomics.load(control, 1) & 0xff) << 8, true);
502
+ }
503
+ children.delete(childPid);
504
+ return BigInt(childPid);
505
+ }
506
+ function requiredMemory() {
507
+ if (!memory)
508
+ throw new Error("WALI pthread memory is unavailable.");
509
+ return memory;
510
+ }
511
+ function requiredMmapCursor() {
512
+ if (!mmapCursor)
513
+ throw new Error("WALI pthread mmap cursor is unavailable.");
514
+ return mmapCursor;
515
+ }
516
+ function requiredSharedMmapRegistry() {
517
+ if (!sharedMmapRegistry)
518
+ throw new Error("WALI pthread shared mmap registry is unavailable.");
519
+ return sharedMmapRegistry;
520
+ }
521
+ function bytes() { return new Uint8Array(requiredMemory().buffer); }
522
+ function view() { return new DataView(requiredMemory().buffer); }
523
+ function zeroMemory(pointer, length) {
524
+ if (pointer !== 0)
525
+ bytes().fill(0, pointer, pointer + length);
526
+ return 0n;
527
+ }
528
+ function futex(address, operation, value, timeoutPointer) {
529
+ if (address % 4 !== 0)
530
+ return -22n;
531
+ const words = new Int32Array(requiredMemory().buffer);
532
+ const index = address / 4;
533
+ const command = operation & 0x7f;
534
+ if (command === 1 || command === 10)
535
+ return BigInt(Atomics.notify(words, index, Math.max(0, value)));
536
+ if (command !== 0 && command !== 9)
537
+ return -38n;
538
+ let timeout;
539
+ if (timeoutPointer !== 0) {
540
+ const seconds = view().getBigInt64(timeoutPointer, true);
541
+ const nanoseconds = view().getBigInt64(timeoutPointer + 8, true);
542
+ if (seconds < 0n || nanoseconds < 0n || nanoseconds >= 1000000000n)
543
+ return -22n;
544
+ const milliseconds = Number(seconds * 1000n + nanoseconds / 1000000n);
545
+ timeout = command === 9
546
+ ? Math.max(0, milliseconds - ((operation & 0x100) !== 0 ? Date.now() : performance.now()))
547
+ : milliseconds;
548
+ }
549
+ const result = Atomics.wait(words, index, value, timeout);
550
+ return result === "not-equal" ? -11n : result === "timed-out" ? -110n : 0n;
551
+ }
552
+ function clockGettime(clock, pointer) {
553
+ const milliseconds = clock === 0 ? performance.timeOrigin + performance.now() : performance.now();
554
+ const nanoseconds = BigInt(Math.floor(milliseconds * 1_000_000));
555
+ view().setBigInt64(pointer, nanoseconds / 1000000000n, true);
556
+ view().setBigInt64(pointer + 8, nanoseconds % 1000000000n, true);
557
+ return 0n;
558
+ }
559
+ function gettimeofday(pointer) {
560
+ if (pointer !== 0) {
561
+ const microseconds = BigInt(Date.now()) * 1000n;
562
+ view().setBigInt64(pointer, microseconds / 1000000n, true);
563
+ view().setBigInt64(pointer + 8, microseconds % 1000000n, true);
564
+ }
565
+ return 0n;
566
+ }
567
+ function clockNanosleep(clock, flags, request, remaining) {
568
+ if (![0, 1, 4].includes(clock) || (flags & ~1) !== 0 || request < 0 || request + 16 > bytes().byteLength)
569
+ return -22n;
570
+ const seconds = view().getBigInt64(request, true);
571
+ const nanoseconds = view().getBigInt64(request + 8, true);
572
+ if (seconds < 0n || nanoseconds < 0n || nanoseconds >= 1000000000n)
573
+ return -22n;
574
+ let duration = Number(seconds * 1000n + nanoseconds / 1000000n);
575
+ if (flags & 1)
576
+ duration = Math.max(0, duration - (clock === 0 ? Date.now() : performance.now()));
577
+ Atomics.wait(sleepWord, 0, 0, duration);
578
+ if (remaining !== 0)
579
+ bytes().fill(0, remaining, remaining + 16);
580
+ return 0n;
581
+ }
582
+ function idlePoll(timeout) {
583
+ if (timeout > 0)
584
+ Atomics.wait(sleepWord, 0, 0, timeout);
585
+ return 0n;
586
+ }
587
+ function getrandom(pointer, length) {
588
+ if (!Number.isSafeInteger(length) || length < 0 || pointer < 0 || pointer + length > bytes().byteLength)
589
+ return -22n;
590
+ const destination = bytes().subarray(pointer, pointer + length);
591
+ for (let offset = 0; offset < destination.byteLength; offset += 65_536) {
592
+ const random = new Uint8Array(Math.min(65_536, destination.byteLength - offset));
593
+ crypto.getRandomValues(random);
594
+ destination.set(random, offset);
595
+ }
596
+ return BigInt(length);
597
+ }
598
+ function write(fd, pointer, length) {
599
+ const stdio = stdioDescriptor(fd);
600
+ if (stdio !== 1 && stdio !== 2)
601
+ return -9n;
602
+ const chunk = bytes().slice(pointer, pointer + length);
603
+ emitThread(stdio === 1 ? "stdout" : "stderr", chunk);
604
+ return BigInt(length);
605
+ }
606
+ function emitThread(type, chunk) {
607
+ if (isWaliRuntimeNotice(chunk))
608
+ return;
609
+ if (outputPort) {
610
+ // A fatal pthread can wake and terminate the root runtime before an
611
+ // asynchronous MessagePort delivery reaches the terminal. Wait until the
612
+ // browser-side owner has consumed this write so panic output and ordinary
613
+ // stdout/stderr cannot be dropped during process teardown.
614
+ const ack = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
615
+ outputPort.postMessage({ type, bytes: chunk, ack: ack.buffer }, [chunk.buffer]);
616
+ Atomics.wait(ack, 0, 0, 5_000);
617
+ }
618
+ else {
619
+ scope.postMessage({ kind: type, bytes: chunk }, [chunk.buffer]);
620
+ }
621
+ }
622
+ function writev(fd, pointer, count) {
623
+ if (!Number.isSafeInteger(count) || count < 0 || count > 65_536)
624
+ return -22n;
625
+ const stdio = stdioDescriptor(fd);
626
+ if (stdio !== 1 && stdio !== 2)
627
+ return -9n;
628
+ const chunks = [];
629
+ let total = 0;
630
+ for (let index = 0; index < count; index += 1) {
631
+ const offset = pointer + index * 8;
632
+ const chunkPointer = view().getUint32(offset, true);
633
+ const length = view().getUint32(offset + 4, true);
634
+ if (chunkPointer + length > bytes().byteLength)
635
+ return -14n;
636
+ chunks.push(bytes().slice(chunkPointer, chunkPointer + length));
637
+ total += length;
638
+ }
639
+ const combined = new Uint8Array(total);
640
+ let destination = 0;
641
+ for (const chunk of chunks) {
642
+ combined.set(chunk, destination);
643
+ destination += chunk.byteLength;
644
+ }
645
+ // The WALI libc currently prints this implementation note whenever a
646
+ // detached pthread exits. It is runtime noise, not process stdout, and an
647
+ // out-of-band write corrupts diff-based TUIs such as ratatui.
648
+ if (isWaliRuntimeNotice(combined)) {
649
+ return BigInt(total);
650
+ }
651
+ emitThread(stdio === 1 ? "stdout" : "stderr", combined);
652
+ return BigInt(total);
653
+ }
654
+ function isWaliRuntimeNotice(value) {
655
+ let length = value.byteLength;
656
+ while (length > 0 && (value[length - 1] === 0x0a || value[length - 1] === 0x0d))
657
+ length -= 1;
658
+ if (length === DETACHED_THREAD_RUNTIME_NOTICE.byteLength
659
+ && DETACHED_THREAD_RUNTIME_NOTICE.every((byte, index) => value[index] === byte))
660
+ return true;
661
+ if (length < 31 || length > 64 || value[0] !== 0x49)
662
+ return false;
663
+ const text = new TextDecoder().decode(value.subarray(0, length));
664
+ return /^Invalid syscall var call -- NR \d+$/u.test(text);
665
+ }
666
+ function stdioDescriptor(fd) {
667
+ if (fd >= 0 && fd <= 2)
668
+ return fd;
669
+ const local = localStdioAliases.get(fd);
670
+ if (local !== undefined)
671
+ return local;
672
+ if (!stdioAliasRegistry || fd < 0 || fd >= stdioAliasRegistry.length)
673
+ return;
674
+ const shared = Atomics.load(stdioAliasRegistry, fd);
675
+ return shared === 0 || shared === 1 || shared === 2 ? shared : undefined;
676
+ }
677
+ function duplicateDescriptor(fd, target, minimum = 3) {
678
+ const stdio = stdioDescriptor(fd);
679
+ if (stdio === undefined)
680
+ return -9n;
681
+ const next = target ?? allocateLocalDescriptor(minimum);
682
+ if (!Number.isSafeInteger(next) || next < 0 || next > 65_535)
683
+ return -9n;
684
+ if (next !== fd)
685
+ localStdioAliases.set(next, stdio);
686
+ return BigInt(next);
687
+ }
688
+ function allocateLocalDescriptor(minimum) {
689
+ nextLocalDescriptor = Math.max(nextLocalDescriptor, minimum);
690
+ while (nextLocalDescriptor <= 65_535 && (stdioDescriptor(nextLocalDescriptor) !== undefined || localSpecialDescriptors.has(nextLocalDescriptor))) {
691
+ nextLocalDescriptor += 1;
692
+ }
693
+ return nextLocalDescriptor++;
694
+ }
695
+ function allocateSpecialDescriptor() {
696
+ const fd = allocateLocalDescriptor(3);
697
+ localSpecialDescriptors.add(fd);
698
+ return BigInt(fd);
699
+ }
700
+ function closeDescriptor(fd) {
701
+ if (fd >= 0 && fd <= 2)
702
+ return 0n;
703
+ if (localSpecialDescriptors.delete(fd))
704
+ return 0n;
705
+ return localStdioAliases.delete(fd) ? 0n : -9n;
706
+ }
707
+ function fcntl(fd, command, argument) {
708
+ if (localSpecialDescriptors.has(fd)) {
709
+ if (command === 1 || command === 2 || command === 4)
710
+ return 0n;
711
+ if (command === 3)
712
+ return 0n;
713
+ return -22n;
714
+ }
715
+ if (stdioDescriptor(fd) === undefined)
716
+ return -9n;
717
+ if (command === 0 || command === 1030)
718
+ return duplicateDescriptor(fd, undefined, Math.max(argument, 3));
719
+ if (command === 1 || command === 2 || command === 4)
720
+ return 0n;
721
+ if (command === 3)
722
+ return 2n;
723
+ return -22n;
724
+ }
725
+ //# sourceMappingURL=thread-worker.js.map