@volter/tabnode 0.2.14-volter.88

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 (429) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +78 -0
  3. package/THIRD-PARTY-NOTICES.md +12 -0
  4. package/dist/__sw__.js +526 -0
  5. package/dist/assets/runtime-worker-4MKaHFVb.js +75221 -0
  6. package/dist/assets/runtime-worker-4MKaHFVb.js.map +1 -0
  7. package/dist/code-transforms.d.ts +21 -0
  8. package/dist/code-transforms.d.ts.map +1 -0
  9. package/dist/config/cdn.d.ts +12 -0
  10. package/dist/config/cdn.d.ts.map +1 -0
  11. package/dist/create-runtime.d.ts +22 -0
  12. package/dist/create-runtime.d.ts.map +1 -0
  13. package/dist/host-globals.d.ts +62 -0
  14. package/dist/host-globals.d.ts.map +1 -0
  15. package/dist/index.cjs +36021 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.ts +127 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.mjs +36020 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/node-internals.d.ts +68 -0
  22. package/dist/node-internals.d.ts.map +1 -0
  23. package/dist/node-lib/binding/blob.d.ts +12 -0
  24. package/dist/node-lib/binding/blob.d.ts.map +1 -0
  25. package/dist/node-lib/binding/buffer.d.ts +144 -0
  26. package/dist/node-lib/binding/buffer.d.ts.map +1 -0
  27. package/dist/node-lib/binding/cares_wrap.d.ts +14 -0
  28. package/dist/node-lib/binding/cares_wrap.d.ts.map +1 -0
  29. package/dist/node-lib/binding/fds.d.ts +30 -0
  30. package/dist/node-lib/binding/fds.d.ts.map +1 -0
  31. package/dist/node-lib/binding/fs.d.ts +157 -0
  32. package/dist/node-lib/binding/fs.d.ts.map +1 -0
  33. package/dist/node-lib/binding/handles.d.ts +81 -0
  34. package/dist/node-lib/binding/handles.d.ts.map +1 -0
  35. package/dist/node-lib/binding/http_parser.d.ts +122 -0
  36. package/dist/node-lib/binding/http_parser.d.ts.map +1 -0
  37. package/dist/node-lib/binding/index.d.ts +3 -0
  38. package/dist/node-lib/binding/index.d.ts.map +1 -0
  39. package/dist/node-lib/binding/llhttp-wasm.d.ts +22 -0
  40. package/dist/node-lib/binding/llhttp-wasm.d.ts.map +1 -0
  41. package/dist/node-lib/binding/misc.d.ts +123 -0
  42. package/dist/node-lib/binding/misc.d.ts.map +1 -0
  43. package/dist/node-lib/binding/pipe_wrap.d.ts +84 -0
  44. package/dist/node-lib/binding/pipe_wrap.d.ts.map +1 -0
  45. package/dist/node-lib/binding/process_wrap.d.ts +173 -0
  46. package/dist/node-lib/binding/process_wrap.d.ts.map +1 -0
  47. package/dist/node-lib/binding/spawn_sync.d.ts +30 -0
  48. package/dist/node-lib/binding/spawn_sync.d.ts.map +1 -0
  49. package/dist/node-lib/binding/stream_wrap.d.ts +191 -0
  50. package/dist/node-lib/binding/stream_wrap.d.ts.map +1 -0
  51. package/dist/node-lib/binding/stubs.d.ts +23 -0
  52. package/dist/node-lib/binding/stubs.d.ts.map +1 -0
  53. package/dist/node-lib/binding/tcp_wrap.d.ts +98 -0
  54. package/dist/node-lib/binding/tcp_wrap.d.ts.map +1 -0
  55. package/dist/node-lib/binding/tty_wrap.d.ts +36 -0
  56. package/dist/node-lib/binding/tty_wrap.d.ts.map +1 -0
  57. package/dist/node-lib/binding/util.d.ts +126 -0
  58. package/dist/node-lib/binding/util.d.ts.map +1 -0
  59. package/dist/node-lib/binding/uv.d.ts +32 -0
  60. package/dist/node-lib/binding/uv.d.ts.map +1 -0
  61. package/dist/node-lib/binding/zlib-pako.d.ts +22 -0
  62. package/dist/node-lib/binding/zlib-pako.d.ts.map +1 -0
  63. package/dist/node-lib/binding/zlib.d.ts +38 -0
  64. package/dist/node-lib/binding/zlib.d.ts.map +1 -0
  65. package/dist/node-lib/buffer-module.d.ts +51 -0
  66. package/dist/node-lib/buffer-module.d.ts.map +1 -0
  67. package/dist/node-lib/child-process-module.d.ts +26 -0
  68. package/dist/node-lib/child-process-module.d.ts.map +1 -0
  69. package/dist/node-lib/events-module.d.ts +58 -0
  70. package/dist/node-lib/events-module.d.ts.map +1 -0
  71. package/dist/node-lib/fs-module.d.ts +5 -0
  72. package/dist/node-lib/fs-module.d.ts.map +1 -0
  73. package/dist/node-lib/http-bridge.d.ts +43 -0
  74. package/dist/node-lib/http-bridge.d.ts.map +1 -0
  75. package/dist/node-lib/http-module.d.ts +35 -0
  76. package/dist/node-lib/http-module.d.ts.map +1 -0
  77. package/dist/node-lib/internals/addresses.d.ts +40 -0
  78. package/dist/node-lib/internals/addresses.d.ts.map +1 -0
  79. package/dist/node-lib/internals/buffer-and-streams.d.ts +96 -0
  80. package/dist/node-lib/internals/buffer-and-streams.d.ts.map +1 -0
  81. package/dist/node-lib/internals/child-process.d.ts +27 -0
  82. package/dist/node-lib/internals/child-process.d.ts.map +1 -0
  83. package/dist/node-lib/internals/events-util.d.ts +83 -0
  84. package/dist/node-lib/internals/events-util.d.ts.map +1 -0
  85. package/dist/node-lib/internals/index.d.ts +2 -0
  86. package/dist/node-lib/internals/index.d.ts.map +1 -0
  87. package/dist/node-lib/internals/modules.d.ts +67 -0
  88. package/dist/node-lib/internals/modules.d.ts.map +1 -0
  89. package/dist/node-lib/internals/readline-access.d.ts +2 -0
  90. package/dist/node-lib/internals/readline-access.d.ts.map +1 -0
  91. package/dist/node-lib/internals/runtime.d.ts +150 -0
  92. package/dist/node-lib/internals/runtime.d.ts.map +1 -0
  93. package/dist/node-lib/internals/util.d.ts +33 -0
  94. package/dist/node-lib/internals/util.d.ts.map +1 -0
  95. package/dist/node-lib/lazy.d.ts +31 -0
  96. package/dist/node-lib/lazy.d.ts.map +1 -0
  97. package/dist/node-lib/load.d.ts +27 -0
  98. package/dist/node-lib/load.d.ts.map +1 -0
  99. package/dist/node-lib/module-hooks.d.ts +89 -0
  100. package/dist/node-lib/module-hooks.d.ts.map +1 -0
  101. package/dist/node-lib/net-module.d.ts +181 -0
  102. package/dist/node-lib/net-module.d.ts.map +1 -0
  103. package/dist/node-lib/node-versions.d.ts +27 -0
  104. package/dist/node-lib/node-versions.d.ts.map +1 -0
  105. package/dist/node-lib/public-modules.d.ts +2 -0
  106. package/dist/node-lib/public-modules.d.ts.map +1 -0
  107. package/dist/node-lib/require-hook.d.ts +15 -0
  108. package/dist/node-lib/require-hook.d.ts.map +1 -0
  109. package/dist/node-lib/small-modules.d.ts +16 -0
  110. package/dist/node-lib/small-modules.d.ts.map +1 -0
  111. package/dist/node-lib/sources.d.ts +4 -0
  112. package/dist/node-lib/sources.d.ts.map +1 -0
  113. package/dist/node-lib/stream-module.d.ts +77 -0
  114. package/dist/node-lib/stream-module.d.ts.map +1 -0
  115. package/dist/node-lib/timers.d.ts +19 -0
  116. package/dist/node-lib/timers.d.ts.map +1 -0
  117. package/dist/node-lib/util-module.d.ts +76 -0
  118. package/dist/node-lib/util-module.d.ts.map +1 -0
  119. package/dist/node-resolution.d.ts +50 -0
  120. package/dist/node-resolution.d.ts.map +1 -0
  121. package/dist/node-resolver.d.ts +7 -0
  122. package/dist/node-resolver.d.ts.map +1 -0
  123. package/dist/npm/index.d.ts +73 -0
  124. package/dist/npm/index.d.ts.map +1 -0
  125. package/dist/npm/registry.d.ts +111 -0
  126. package/dist/npm/registry.d.ts.map +1 -0
  127. package/dist/npm/resolver.d.ts +79 -0
  128. package/dist/npm/resolver.d.ts.map +1 -0
  129. package/dist/npm/tarball.d.ts +28 -0
  130. package/dist/npm/tarball.d.ts.map +1 -0
  131. package/dist/process-tokens.d.ts +77 -0
  132. package/dist/process-tokens.d.ts.map +1 -0
  133. package/dist/punycode-source.d.ts +3 -0
  134. package/dist/punycode-source.d.ts.map +1 -0
  135. package/dist/runtime-interface.d.ts +74 -0
  136. package/dist/runtime-interface.d.ts.map +1 -0
  137. package/dist/runtime.d.ts +144 -0
  138. package/dist/runtime.d.ts.map +1 -0
  139. package/dist/server-bridge.d.ts +147 -0
  140. package/dist/server-bridge.d.ts.map +1 -0
  141. package/dist/shims/async_hooks.d.ts +50 -0
  142. package/dist/shims/async_hooks.d.ts.map +1 -0
  143. package/dist/shims/child_process.d.ts +104 -0
  144. package/dist/shims/child_process.d.ts.map +1 -0
  145. package/dist/shims/cluster.d.ts +52 -0
  146. package/dist/shims/cluster.d.ts.map +1 -0
  147. package/dist/shims/command-line.d.ts +43 -0
  148. package/dist/shims/command-line.d.ts.map +1 -0
  149. package/dist/shims/constants.d.ts +222 -0
  150. package/dist/shims/constants.d.ts.map +1 -0
  151. package/dist/shims/crypto-constants.d.ts +12 -0
  152. package/dist/shims/crypto-constants.d.ts.map +1 -0
  153. package/dist/shims/crypto.d.ts +144 -0
  154. package/dist/shims/crypto.d.ts.map +1 -0
  155. package/dist/shims/dgram.d.ts +34 -0
  156. package/dist/shims/dgram.d.ts.map +1 -0
  157. package/dist/shims/dns.d.ts +91 -0
  158. package/dist/shims/dns.d.ts.map +1 -0
  159. package/dist/shims/domain.d.ts +25 -0
  160. package/dist/shims/domain.d.ts.map +1 -0
  161. package/dist/shims/esbuild.d.ts +205 -0
  162. package/dist/shims/esbuild.d.ts.map +1 -0
  163. package/dist/shims/fs.d.ts +25 -0
  164. package/dist/shims/fs.d.ts.map +1 -0
  165. package/dist/shims/http2.d.ts +81 -0
  166. package/dist/shims/http2.d.ts.map +1 -0
  167. package/dist/shims/inspector.d.ts +25 -0
  168. package/dist/shims/inspector.d.ts.map +1 -0
  169. package/dist/shims/module.d.ts +22 -0
  170. package/dist/shims/module.d.ts.map +1 -0
  171. package/dist/shims/os.d.ts +159 -0
  172. package/dist/shims/os.d.ts.map +1 -0
  173. package/dist/shims/path.d.ts +52 -0
  174. package/dist/shims/path.d.ts.map +1 -0
  175. package/dist/shims/perf_hooks.d.ts +50 -0
  176. package/dist/shims/perf_hooks.d.ts.map +1 -0
  177. package/dist/shims/process.d.ts +226 -0
  178. package/dist/shims/process.d.ts.map +1 -0
  179. package/dist/shims/rollup.d.ts +71 -0
  180. package/dist/shims/rollup.d.ts.map +1 -0
  181. package/dist/shims/string_decoder.d.ts +41 -0
  182. package/dist/shims/string_decoder.d.ts.map +1 -0
  183. package/dist/shims/sync-child.d.ts +64 -0
  184. package/dist/shims/sync-child.d.ts.map +1 -0
  185. package/dist/shims/tls.d.ts +53 -0
  186. package/dist/shims/tls.d.ts.map +1 -0
  187. package/dist/shims/url.d.ts +84 -0
  188. package/dist/shims/url.d.ts.map +1 -0
  189. package/dist/shims/v8.d.ts +73 -0
  190. package/dist/shims/v8.d.ts.map +1 -0
  191. package/dist/shims/vfs-adapter.d.ts +126 -0
  192. package/dist/shims/vfs-adapter.d.ts.map +1 -0
  193. package/dist/shims/vm.d.ts +52 -0
  194. package/dist/shims/vm.d.ts.map +1 -0
  195. package/dist/shims/wasi.d.ts +78 -0
  196. package/dist/shims/wasi.d.ts.map +1 -0
  197. package/dist/shims/worker_threads.d.ts +66 -0
  198. package/dist/shims/worker_threads.d.ts.map +1 -0
  199. package/dist/shims/zlib-constants.d.ts +66 -0
  200. package/dist/shims/zlib-constants.d.ts.map +1 -0
  201. package/dist/shims/zlib.d.ts +102 -0
  202. package/dist/shims/zlib.d.ts.map +1 -0
  203. package/dist/transform.d.ts +24 -0
  204. package/dist/transform.d.ts.map +1 -0
  205. package/dist/types/package-json.d.ts +18 -0
  206. package/dist/types/package-json.d.ts.map +1 -0
  207. package/dist/utils/binary-encoding.d.ts +13 -0
  208. package/dist/utils/binary-encoding.d.ts.map +1 -0
  209. package/dist/utils/glob.d.ts +19 -0
  210. package/dist/utils/glob.d.ts.map +1 -0
  211. package/dist/utils/hash.d.ts +6 -0
  212. package/dist/utils/hash.d.ts.map +1 -0
  213. package/dist/virtual-fs.d.ts +259 -0
  214. package/dist/virtual-fs.d.ts.map +1 -0
  215. package/dist/vite-plugin.cjs +50 -0
  216. package/dist/vite-plugin.cjs.map +1 -0
  217. package/dist/vite-plugin.d.ts +36 -0
  218. package/dist/vite-plugin.d.ts.map +1 -0
  219. package/dist/vite-plugin.mjs +31 -0
  220. package/dist/vite-plugin.mjs.map +1 -0
  221. package/dist/worker/runtime-worker.d.ts +8 -0
  222. package/dist/worker/runtime-worker.d.ts.map +1 -0
  223. package/dist/worker-runtime.d.ts +50 -0
  224. package/dist/worker-runtime.d.ts.map +1 -0
  225. package/package.json +88 -0
  226. package/src/code-transforms.ts +560 -0
  227. package/src/config/cdn.ts +13 -0
  228. package/src/create-runtime.ts +89 -0
  229. package/src/host-globals.ts +121 -0
  230. package/src/index.ts +235 -0
  231. package/src/node-internals.ts +283 -0
  232. package/src/node-lib/LICENSE +47 -0
  233. package/src/node-lib/_http_agent.js +547 -0
  234. package/src/node-lib/_http_client.js +996 -0
  235. package/src/node-lib/_http_common.js +269 -0
  236. package/src/node-lib/_http_incoming.js +453 -0
  237. package/src/node-lib/_http_outgoing.js +1262 -0
  238. package/src/node-lib/_http_server.js +1230 -0
  239. package/src/node-lib/assert/strict.js +3 -0
  240. package/src/node-lib/assert.js +844 -0
  241. package/src/node-lib/binding/blob.ts +35 -0
  242. package/src/node-lib/binding/buffer.ts +465 -0
  243. package/src/node-lib/binding/cares_wrap.ts +57 -0
  244. package/src/node-lib/binding/fds.ts +80 -0
  245. package/src/node-lib/binding/fs.ts +857 -0
  246. package/src/node-lib/binding/handles.ts +187 -0
  247. package/src/node-lib/binding/http_parser.ts +536 -0
  248. package/src/node-lib/binding/index.ts +77 -0
  249. package/src/node-lib/binding/llhttp-wasm.ts +670 -0
  250. package/src/node-lib/binding/misc.ts +252 -0
  251. package/src/node-lib/binding/pipe_wrap.ts +166 -0
  252. package/src/node-lib/binding/process_wrap.ts +371 -0
  253. package/src/node-lib/binding/spawn_sync.ts +141 -0
  254. package/src/node-lib/binding/stream_wrap.ts +413 -0
  255. package/src/node-lib/binding/stubs.ts +19 -0
  256. package/src/node-lib/binding/tcp_wrap.ts +254 -0
  257. package/src/node-lib/binding/tty_wrap.ts +56 -0
  258. package/src/node-lib/binding/util.ts +274 -0
  259. package/src/node-lib/binding/uv.ts +102 -0
  260. package/src/node-lib/binding/zlib-pako.ts +50 -0
  261. package/src/node-lib/binding/zlib.ts +259 -0
  262. package/src/node-lib/buffer-module.ts +70 -0
  263. package/src/node-lib/buffer.js +1373 -0
  264. package/src/node-lib/child-process-module.ts +58 -0
  265. package/src/node-lib/child_process.js +1046 -0
  266. package/src/node-lib/constants.js +41 -0
  267. package/src/node-lib/diagnostics_channel.js +438 -0
  268. package/src/node-lib/events-module.ts +80 -0
  269. package/src/node-lib/events.js +1223 -0
  270. package/src/node-lib/fs-module.ts +43 -0
  271. package/src/node-lib/fs.js +3377 -0
  272. package/src/node-lib/http-bridge.ts +299 -0
  273. package/src/node-lib/http-module.ts +53 -0
  274. package/src/node-lib/http.js +198 -0
  275. package/src/node-lib/https.js +429 -0
  276. package/src/node-lib/internal/assert/assertion_error.js +417 -0
  277. package/src/node-lib/internal/assert/calltracker.js +153 -0
  278. package/src/node-lib/internal/assert/myers_diff.js +177 -0
  279. package/src/node-lib/internal/assert/utils.js +278 -0
  280. package/src/node-lib/internal/buffer.js +1120 -0
  281. package/src/node-lib/internal/child_process/serialization.js +169 -0
  282. package/src/node-lib/internal/child_process.js +1135 -0
  283. package/src/node-lib/internal/constants.js +56 -0
  284. package/src/node-lib/internal/deps/minimatch/index.js +1628 -0
  285. package/src/node-lib/internal/errors.js +1912 -0
  286. package/src/node-lib/internal/events/abort_listener.js +54 -0
  287. package/src/node-lib/internal/events/symbols.js +11 -0
  288. package/src/node-lib/internal/fixed_queue.js +119 -0
  289. package/src/node-lib/internal/freelist.js +30 -0
  290. package/src/node-lib/internal/fs/cp/cp-sync.js +242 -0
  291. package/src/node-lib/internal/fs/cp/cp.js +382 -0
  292. package/src/node-lib/internal/fs/dir.js +369 -0
  293. package/src/node-lib/internal/fs/glob.js +763 -0
  294. package/src/node-lib/internal/fs/promises.js +1312 -0
  295. package/src/node-lib/internal/fs/read/context.js +129 -0
  296. package/src/node-lib/internal/fs/recursive_watch.js +275 -0
  297. package/src/node-lib/internal/fs/rimraf.js +311 -0
  298. package/src/node-lib/internal/fs/streams.js +571 -0
  299. package/src/node-lib/internal/fs/sync_write_stream.js +49 -0
  300. package/src/node-lib/internal/fs/utils.js +987 -0
  301. package/src/node-lib/internal/fs/watchers.js +418 -0
  302. package/src/node-lib/internal/http.js +62 -0
  303. package/src/node-lib/internal/mime.js +389 -0
  304. package/src/node-lib/internal/modules/customization_hooks.js +406 -0
  305. package/src/node-lib/internal/net.js +95 -0
  306. package/src/node-lib/internal/querystring.js +119 -0
  307. package/src/node-lib/internal/readline/callbacks.js +132 -0
  308. package/src/node-lib/internal/readline/emitKeypressEvents.js +99 -0
  309. package/src/node-lib/internal/readline/interface.js +1418 -0
  310. package/src/node-lib/internal/readline/promises.js +144 -0
  311. package/src/node-lib/internal/readline/utils.js +405 -0
  312. package/src/node-lib/internal/socket_list.js +108 -0
  313. package/src/node-lib/internal/stream_base_commons.js +280 -0
  314. package/src/node-lib/internal/streams/add-abort-signal.js +60 -0
  315. package/src/node-lib/internal/streams/compose.js +257 -0
  316. package/src/node-lib/internal/streams/destroy.js +361 -0
  317. package/src/node-lib/internal/streams/duplex.js +203 -0
  318. package/src/node-lib/internal/streams/duplexify.js +379 -0
  319. package/src/node-lib/internal/streams/duplexpair.js +62 -0
  320. package/src/node-lib/internal/streams/end-of-stream.js +331 -0
  321. package/src/node-lib/internal/streams/from.js +208 -0
  322. package/src/node-lib/internal/streams/lazy_transform.js +57 -0
  323. package/src/node-lib/internal/streams/legacy.js +125 -0
  324. package/src/node-lib/internal/streams/operators.js +436 -0
  325. package/src/node-lib/internal/streams/passthrough.js +47 -0
  326. package/src/node-lib/internal/streams/pipeline.js +465 -0
  327. package/src/node-lib/internal/streams/readable.js +1762 -0
  328. package/src/node-lib/internal/streams/state.js +51 -0
  329. package/src/node-lib/internal/streams/transform.js +204 -0
  330. package/src/node-lib/internal/streams/utils.js +363 -0
  331. package/src/node-lib/internal/streams/writable.js +1163 -0
  332. package/src/node-lib/internal/tty.js +260 -0
  333. package/src/node-lib/internal/util/colors.js +40 -0
  334. package/src/node-lib/internal/util/comparisons.js +1028 -0
  335. package/src/node-lib/internal/util/debuglog.js +524 -0
  336. package/src/node-lib/internal/util/inspect.js +2535 -0
  337. package/src/node-lib/internal/util/parse_args/parse_args.js +412 -0
  338. package/src/node-lib/internal/util/parse_args/utils.js +197 -0
  339. package/src/node-lib/internal/util.js +1003 -0
  340. package/src/node-lib/internal/validators.js +654 -0
  341. package/src/node-lib/internals/addresses.ts +159 -0
  342. package/src/node-lib/internals/buffer-and-streams.ts +224 -0
  343. package/src/node-lib/internals/child-process.ts +64 -0
  344. package/src/node-lib/internals/events-util.ts +105 -0
  345. package/src/node-lib/internals/index.ts +66 -0
  346. package/src/node-lib/internals/modules.ts +117 -0
  347. package/src/node-lib/internals/readline-access.ts +16 -0
  348. package/src/node-lib/internals/runtime.ts +277 -0
  349. package/src/node-lib/internals/util.ts +98 -0
  350. package/src/node-lib/lazy.ts +158 -0
  351. package/src/node-lib/load.ts +390 -0
  352. package/src/node-lib/module-hooks.ts +281 -0
  353. package/src/node-lib/net-module.ts +248 -0
  354. package/src/node-lib/net.js +2540 -0
  355. package/src/node-lib/node-versions.ts +34 -0
  356. package/src/node-lib/os.js +356 -0
  357. package/src/node-lib/path.js +1694 -0
  358. package/src/node-lib/primordials.js +743 -0
  359. package/src/node-lib/public-modules.ts +109 -0
  360. package/src/node-lib/punycode.js +455 -0
  361. package/src/node-lib/querystring.js +484 -0
  362. package/src/node-lib/readline/promises.js +73 -0
  363. package/src/node-lib/readline.js +523 -0
  364. package/src/node-lib/require-hook.ts +27 -0
  365. package/src/node-lib/small-modules.ts +35 -0
  366. package/src/node-lib/sources.ts +200 -0
  367. package/src/node-lib/stream/promises.js +44 -0
  368. package/src/node-lib/stream-module.ts +101 -0
  369. package/src/node-lib/stream.js +152 -0
  370. package/src/node-lib/timers.ts +33 -0
  371. package/src/node-lib/tty.js +168 -0
  372. package/src/node-lib/util-module.ts +104 -0
  373. package/src/node-lib/util.js +689 -0
  374. package/src/node-lib/wasi.js +179 -0
  375. package/src/node-lib/zlib.js +1036 -0
  376. package/src/node-resolution.ts +241 -0
  377. package/src/node-resolver.ts +33 -0
  378. package/src/npm/index.ts +425 -0
  379. package/src/npm/registry.ts +235 -0
  380. package/src/npm/resolver.ts +589 -0
  381. package/src/npm/tarball.ts +246 -0
  382. package/src/process-tokens.ts +156 -0
  383. package/src/punycode-source.ts +6 -0
  384. package/src/runtime-interface.ts +85 -0
  385. package/src/runtime.ts +3073 -0
  386. package/src/server-bridge.ts +971 -0
  387. package/src/shims/async_hooks.ts +181 -0
  388. package/src/shims/child_process.ts +1591 -0
  389. package/src/shims/cluster.ts +79 -0
  390. package/src/shims/command-line.ts +79 -0
  391. package/src/shims/constants.ts +68 -0
  392. package/src/shims/crypto-constants.ts +11 -0
  393. package/src/shims/crypto.ts +1061 -0
  394. package/src/shims/dgram.ts +47 -0
  395. package/src/shims/dns.ts +185 -0
  396. package/src/shims/domain.ts +58 -0
  397. package/src/shims/esbuild.ts +1638 -0
  398. package/src/shims/fs.ts +82 -0
  399. package/src/shims/http2.ts +96 -0
  400. package/src/shims/inspector.ts +30 -0
  401. package/src/shims/module.ts +86 -0
  402. package/src/shims/os.ts +195 -0
  403. package/src/shims/path.ts +246 -0
  404. package/src/shims/perf_hooks.ts +92 -0
  405. package/src/shims/process.ts +804 -0
  406. package/src/shims/rollup.ts +149 -0
  407. package/src/shims/string_decoder.ts +396 -0
  408. package/src/shims/sync-child.ts +469 -0
  409. package/src/shims/tls.ts +94 -0
  410. package/src/shims/url.ts +217 -0
  411. package/src/shims/v8.ts +113 -0
  412. package/src/shims/vfs-adapter.ts +408 -0
  413. package/src/shims/vm.ts +132 -0
  414. package/src/shims/wasi.ts +1440 -0
  415. package/src/shims/worker_threads.ts +114 -0
  416. package/src/shims/zlib-constants.ts +65 -0
  417. package/src/shims/zlib.ts +228 -0
  418. package/src/transform.ts +324 -0
  419. package/src/types/external.d.ts +55 -0
  420. package/src/types/package-json.ts +17 -0
  421. package/src/types/pako-zlib.d.ts +30 -0
  422. package/src/types/sha.js.d.ts +14 -0
  423. package/src/utils/binary-encoding.ts +43 -0
  424. package/src/utils/glob.ts +54 -0
  425. package/src/utils/hash.ts +12 -0
  426. package/src/virtual-fs.ts +1048 -0
  427. package/src/vite-plugin.ts +76 -0
  428. package/src/worker/runtime-worker.ts +128 -0
  429. package/src/worker-runtime.ts +145 -0
@@ -0,0 +1,1440 @@
1
+ /**
2
+ * `wasi`, as Node's own `lib/wasi.js` on a binding over the engine's
3
+ * filesystem and process.
4
+ *
5
+ * What died: the engine had no `node:wasi` at all. `require('node:wasi')` and
6
+ * `require('wasi')` both answered "Cannot find module", and
7
+ * `module.builtinModules` did not list it where Node's does. The first program
8
+ * to hit it in the tab was Tailwind v4's scanner, whose Node loader opens with
9
+ * `const { WASI } = require('node:wasi')`; every route of the app died behind
10
+ * that. Any program that runs a WASI-compiled wasm module hits the same wall.
11
+ *
12
+ * What Node does: `lib/wasi.js` is a thin JS surface — the `WASI` class, its
13
+ * option validation, `start`, `initialize`, `getImportObject` — over
14
+ * `internalBinding('wasi')`, a C++ wrap of uvwasi. The wrap's prototype holds
15
+ * the forty-six `wasi_snapshot_preview1` syscalls, each reading the instance's
16
+ * `WebAssembly.Memory` afresh on every call, checking every pointer against
17
+ * the memory's current size (EOVERFLOW past it), answering an errno rather
18
+ * than throwing, and throwing ERR_WASI_NOT_STARTED only when called before
19
+ * `start`/`initialize` handed over the memory. uvwasi keeps its own descriptor
20
+ * table over the host's fds — stdio at 0, 1, 2; the preopens from 3 upward in
21
+ * the order given; every later open at the lowest free slot — with WASI rights
22
+ * per file type, and resolves every guest path against the descriptor's
23
+ * mapped path, refusing one that normalizes outside it with ENOTCAPABLE and a
24
+ * symlink chain past thirty-two links with ELOOP.
25
+ *
26
+ * What this is: Node's `lib/wasi.js` v22.18.0, vendored unmodified in
27
+ * `../node-lib/wasi.js` and evaluated per guest on a binding that supplies
28
+ * what the Node runtime would have — `primordials`, the three internals it
29
+ * requires, and `internalBinding('wasi')`. The binding is the uvwasi-shaped
30
+ * class below, written from WASI preview1's witx and Node's `src/node_wasi.cc`
31
+ * and uvwasi's sources, over the engine's fs shim (its descriptor table, its
32
+ * tree) and the guest's process (stdio, exit, signals). It carries no
33
+ * implementation from any WASI polyfill package. The rule for this file: the
34
+ * JS surface is Node's file and is never edited here; a surface bug is fixed
35
+ * by moving the vendored file to a newer Node. The binding is corrected only
36
+ * toward what uvwasi answers.
37
+ *
38
+ * Measured by Node's own `test/wasi/*` through `scripts/node-tests.mjs`
39
+ * (`--dir test/wasi`), and by `tests/wasi.test.ts`, a
40
+ * differential against the host's `node:wasi`.
41
+ *
42
+ * What the engine cannot serve, and answers as uvwasi answers a host that
43
+ * cannot: `path_link` is ENOSYS (the tree keeps no hard links); the process and
44
+ * thread CPU-time clocks answer the monotonic clock (a tab has no CPU clock);
45
+ * `fd_read` on descriptor 0 answers what the guest's stdin stream has already
46
+ * buffered, then EOF (a syscall cannot wait); `poll_oneoff` sleeps the calling
47
+ * thread for a clock subscription (Atomics.wait where the thread may, a timed
48
+ * spin where it may not), as uvwasi blocks on its loop.
49
+ */
50
+ import NODE_WASI_SOURCE from '../node-lib/wasi.js?raw';
51
+ import {
52
+ ERR_INVALID_ARG_TYPE,
53
+ ERR_INVALID_ARG_VALUE,
54
+ validateArray,
55
+ validateBoolean,
56
+ validateFunction,
57
+ validateInt32,
58
+ validateObject,
59
+ validateString,
60
+ validateUndefined,
61
+ } from '../node-internals';
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // What the binding needs of the engine: the fs shim's synchronous surface, and
65
+ // the guest's process. Typed by shape so the runtime hands its own in.
66
+ // ---------------------------------------------------------------------------
67
+
68
+ /** The stats the engine's fs answers, read by name. */
69
+ export interface WasiHostStats {
70
+ size: number;
71
+ ino?: number;
72
+ dev?: number;
73
+ nlink?: number;
74
+ atimeMs?: number;
75
+ mtimeMs?: number;
76
+ ctimeMs?: number;
77
+ atime?: Date;
78
+ mtime?: Date;
79
+ ctime?: Date;
80
+ isFile(): boolean;
81
+ isDirectory(): boolean;
82
+ isSymbolicLink(): boolean;
83
+ isCharacterDevice?(): boolean;
84
+ isBlockDevice?(): boolean;
85
+ isSocket?(): boolean;
86
+ isFIFO?(): boolean;
87
+ }
88
+
89
+ export interface WasiHostDirent {
90
+ name: string;
91
+ isFile(): boolean;
92
+ isDirectory(): boolean;
93
+ isSymbolicLink(): boolean;
94
+ }
95
+
96
+ /** The fs members the binding calls; each is the engine's `fs.<name>`. */
97
+ export interface WasiHostFs {
98
+ existsSync(path: string): boolean;
99
+ statSync(path: string): WasiHostStats;
100
+ lstatSync(path: string): WasiHostStats;
101
+ fstatSync(fd: number): WasiHostStats;
102
+ openSync(path: string, flags: string | number, mode?: number): number;
103
+ closeSync(fd: number): void;
104
+ readSync(fd: number, buffer: Uint8Array, offset: number, length: number, position: number | null): number;
105
+ writeSync(fd: number, buffer: Uint8Array, offset?: number, length?: number, position?: number | null): number;
106
+ readdirSync(path: string, options: { withFileTypes: true }): WasiHostDirent[];
107
+ mkdirSync(path: string, options?: { recursive?: boolean }): void;
108
+ rmdirSync(path: string): void;
109
+ unlinkSync(path: string): void;
110
+ renameSync(oldPath: string, newPath: string): void;
111
+ realpathSync(path: string): string;
112
+ ftruncateSync(fd: number, length?: number): void;
113
+ utimesSync?(path: string, atime: number, mtime: number): void;
114
+ readlinkSync?(path: string): string;
115
+ symlinkSync?(target: string, path: string): void;
116
+ }
117
+
118
+ /** The process members the binding reaches: the guest's own. */
119
+ export interface WasiHostProcess {
120
+ pid: number;
121
+ stdout: { write(data: string): unknown };
122
+ stderr: { write(data: string): unknown };
123
+ stdin: { read?: (size?: number) => string | Uint8Array | null };
124
+ exit(code?: number): never;
125
+ kill(pid: number, signal?: string | number): boolean;
126
+ emitWarning(warning: string | Error, ...rest: unknown[]): void;
127
+ }
128
+
129
+ // ---------------------------------------------------------------------------
130
+ // WASI preview1's constants, from its witx (uvwasi's wasi_types.h carries the
131
+ // same numbers).
132
+ // ---------------------------------------------------------------------------
133
+
134
+ const ERRNO = {
135
+ ESUCCESS: 0, E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6,
136
+ EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13,
137
+ ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20,
138
+ EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27,
139
+ EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35,
140
+ EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41,
141
+ ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49,
142
+ ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55,
143
+ ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTSUP: 58, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61,
144
+ EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68,
145
+ EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENOTCAPABLE: 76,
146
+ } as const;
147
+ type Errno = (typeof ERRNO)[keyof typeof ERRNO];
148
+
149
+ /** The errno name for each WASI number, for the error a failed init throws. */
150
+ const ERRNO_NAMES: Record<number, string> = Object.fromEntries(Object.entries(ERRNO).map(([name, value]) => [value, name]));
151
+
152
+ const FILETYPE = { UNKNOWN: 0, BLOCK_DEVICE: 1, CHARACTER_DEVICE: 2, DIRECTORY: 3, REGULAR_FILE: 4, SOCKET_DGRAM: 5, SOCKET_STREAM: 6, SYMBOLIC_LINK: 7 } as const;
153
+
154
+ const CLOCK = { REALTIME: 0, MONOTONIC: 1, PROCESS_CPUTIME_ID: 2, THREAD_CPUTIME_ID: 3 } as const;
155
+
156
+ const EVENTTYPE = { CLOCK: 0, FD_READ: 1, FD_WRITE: 2 } as const;
157
+ const EVENT_FD_READWRITE_HANGUP = 1;
158
+ const SUBSCRIPTION_CLOCK_ABSTIME = 1;
159
+
160
+ const FDFLAG = { APPEND: 1, DSYNC: 2, NONBLOCK: 4, RSYNC: 8, SYNC: 16 } as const;
161
+ const OFLAG = { CREAT: 1, DIRECTORY: 2, EXCL: 4, TRUNC: 8 } as const;
162
+ const LOOKUP_SYMLINK_FOLLOW = 1;
163
+ const FSTFLAG = { ATIM: 1, ATIM_NOW: 2, MTIM: 4, MTIM_NOW: 8 } as const;
164
+ const WHENCE = { SET: 0, CUR: 1, END: 2 } as const;
165
+ const PREOPENTYPE_DIR = 0;
166
+
167
+ const RIGHT = {
168
+ FD_DATASYNC: 1n << 0n, FD_READ: 1n << 1n, FD_SEEK: 1n << 2n, FD_FDSTAT_SET_FLAGS: 1n << 3n,
169
+ FD_SYNC: 1n << 4n, FD_TELL: 1n << 5n, FD_WRITE: 1n << 6n, FD_ADVISE: 1n << 7n, FD_ALLOCATE: 1n << 8n,
170
+ PATH_CREATE_DIRECTORY: 1n << 9n, PATH_CREATE_FILE: 1n << 10n, PATH_LINK_SOURCE: 1n << 11n,
171
+ PATH_LINK_TARGET: 1n << 12n, PATH_OPEN: 1n << 13n, FD_READDIR: 1n << 14n, PATH_READLINK: 1n << 15n,
172
+ PATH_RENAME_SOURCE: 1n << 16n, PATH_RENAME_TARGET: 1n << 17n, PATH_FILESTAT_GET: 1n << 18n,
173
+ PATH_FILESTAT_SET_SIZE: 1n << 19n, PATH_FILESTAT_SET_TIMES: 1n << 20n, FD_FILESTAT_GET: 1n << 21n,
174
+ FD_FILESTAT_SET_SIZE: 1n << 22n, FD_FILESTAT_SET_TIMES: 1n << 23n, PATH_SYMLINK: 1n << 24n,
175
+ PATH_REMOVE_DIRECTORY: 1n << 25n, PATH_UNLINK_FILE: 1n << 26n, POLL_FD_READWRITE: 1n << 27n,
176
+ SOCK_SHUTDOWN: 1n << 28n, SOCK_ACCEPT: 1n << 29n,
177
+ } as const;
178
+
179
+ // uvwasi's rights per file type (wasi_rights.h).
180
+ const RIGHTS_ALL = Object.values(RIGHT).reduce((all, right) => all | right, 0n);
181
+ const RIGHTS_REGULAR_FILE_BASE =
182
+ RIGHT.FD_DATASYNC | RIGHT.FD_READ | RIGHT.FD_SEEK | RIGHT.FD_FDSTAT_SET_FLAGS | RIGHT.FD_SYNC |
183
+ RIGHT.FD_TELL | RIGHT.FD_WRITE | RIGHT.FD_ADVISE | RIGHT.FD_ALLOCATE | RIGHT.FD_FILESTAT_GET |
184
+ RIGHT.FD_FILESTAT_SET_SIZE | RIGHT.FD_FILESTAT_SET_TIMES | RIGHT.POLL_FD_READWRITE;
185
+ const RIGHTS_REGULAR_FILE_INHERITING = 0n;
186
+ const RIGHTS_DIRECTORY_BASE =
187
+ RIGHT.FD_FDSTAT_SET_FLAGS | RIGHT.FD_SYNC | RIGHT.FD_ADVISE | RIGHT.PATH_CREATE_DIRECTORY |
188
+ RIGHT.PATH_CREATE_FILE | RIGHT.PATH_LINK_SOURCE | RIGHT.PATH_LINK_TARGET | RIGHT.PATH_OPEN |
189
+ RIGHT.FD_READDIR | RIGHT.PATH_READLINK | RIGHT.PATH_RENAME_SOURCE | RIGHT.PATH_RENAME_TARGET |
190
+ RIGHT.PATH_FILESTAT_GET | RIGHT.PATH_FILESTAT_SET_SIZE | RIGHT.PATH_FILESTAT_SET_TIMES |
191
+ RIGHT.FD_FILESTAT_GET | RIGHT.FD_FILESTAT_SET_TIMES | RIGHT.PATH_SYMLINK | RIGHT.PATH_UNLINK_FILE |
192
+ RIGHT.PATH_REMOVE_DIRECTORY | RIGHT.POLL_FD_READWRITE;
193
+ const RIGHTS_DIRECTORY_INHERITING = RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE;
194
+ const RIGHTS_SOCKET_BASE =
195
+ RIGHT.FD_READ | RIGHT.FD_FDSTAT_SET_FLAGS | RIGHT.FD_WRITE | RIGHT.FD_FILESTAT_GET |
196
+ RIGHT.POLL_FD_READWRITE | RIGHT.SOCK_SHUTDOWN | RIGHT.SOCK_ACCEPT;
197
+ const RIGHTS_SOCKET_INHERITING = RIGHTS_ALL;
198
+ const RIGHTS_TTY_BASE = RIGHT.FD_READ | RIGHT.FD_FDSTAT_SET_FLAGS | RIGHT.FD_WRITE | RIGHT.FD_FILESTAT_GET | RIGHT.POLL_FD_READWRITE;
199
+ const RIGHTS_TTY_INHERITING = 0n;
200
+
201
+ /** WASI's signal numbers, to the names the guest's `process.kill` takes. */
202
+ const SIGNAL_NAMES: Record<number, string> = {
203
+ 1: 'SIGHUP', 2: 'SIGINT', 3: 'SIGQUIT', 4: 'SIGILL', 5: 'SIGTRAP', 6: 'SIGABRT', 7: 'SIGBUS', 8: 'SIGFPE',
204
+ 9: 'SIGKILL', 10: 'SIGUSR1', 11: 'SIGSEGV', 12: 'SIGUSR2', 13: 'SIGPIPE', 14: 'SIGALRM', 15: 'SIGTERM',
205
+ 16: 'SIGCHLD', 17: 'SIGCONT', 18: 'SIGSTOP', 19: 'SIGTSTP', 20: 'SIGTTIN', 21: 'SIGTTOU', 22: 'SIGURG',
206
+ 23: 'SIGXCPU', 24: 'SIGXFSZ', 25: 'SIGVTALRM', 26: 'SIGPROF', 27: 'SIGWINCH', 28: 'SIGPOLL', 29: 'SIGPWR', 30: 'SIGSYS',
207
+ };
208
+
209
+ // The engine's open flags, the numbers Node's fs.constants carry on Linux.
210
+ const O_RDONLY = 0, O_WRONLY = 1, O_RDWR = 2, O_CREAT = 64, O_EXCL = 128, O_TRUNC = 512, O_APPEND = 1024;
211
+
212
+ /** Structure sizes in guest memory (uvwasi's wasi_serdes.h). */
213
+ const SIZE = { iovec: 8, dirent: 24, fdstat: 24, filestat: 64, prestat: 8, event: 32, subscription: 48 } as const;
214
+
215
+ const MAX_SYMLINK_FOLLOWS = 32;
216
+ const NANOS_PER_SEC = 1_000_000_000n;
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Errors the JS surface reads.
220
+ // ---------------------------------------------------------------------------
221
+
222
+ class ERR_WASI_ALREADY_STARTED extends Error {
223
+ code = 'ERR_WASI_ALREADY_STARTED';
224
+ constructor() {
225
+ super('WASI instance has already started');
226
+ this.name = 'Error';
227
+ }
228
+ }
229
+
230
+ class ERR_WASI_NOT_STARTED extends Error {
231
+ code = 'ERR_WASI_NOT_STARTED';
232
+ constructor() {
233
+ super('wasi.start() has not been called');
234
+ this.name = 'Error';
235
+ }
236
+ }
237
+
238
+ /** node_wasi.cc's WASIException: what a failed uvwasi_init throws. */
239
+ function wasiException(errno: number, syscall: string): Error {
240
+ const code = `UVWASI_${ERRNO_NAMES[errno] ?? 'EIO'}`;
241
+ return Object.assign(new Error(`${code}, ${syscall}`), { errno, code, syscall });
242
+ }
243
+
244
+ /** The engine's fs error, as the errno uvwasi would translate the host's to. */
245
+ function errnoOf(error: unknown): Errno {
246
+ const code = (error as { code?: unknown } | null)?.code;
247
+ if (typeof code === 'string' && Object.prototype.hasOwnProperty.call(ERRNO, code)) return ERRNO[code as keyof typeof ERRNO];
248
+ return ERRNO.EIO;
249
+ }
250
+
251
+ /** Whether a thrown value is a filesystem error (one carrying a code) rather than the guest's exit. */
252
+ function isFsError(error: unknown): boolean {
253
+ return typeof (error as { code?: unknown } | null)?.code === 'string' && !/^ERR_/u.test((error as { code: string }).code);
254
+ }
255
+
256
+ // ---------------------------------------------------------------------------
257
+ // Paths, as uvwasi's path_resolver.c resolves them.
258
+ // ---------------------------------------------------------------------------
259
+
260
+ /** uvwasi__normalize_path: '.' and '..' folded, a leading '..' kept on a relative path. */
261
+ function normalizePath(path: string): string {
262
+ const absolute = path.startsWith('/');
263
+ const out: string[] = [];
264
+ for (const segment of path.split('/')) {
265
+ if (segment === '' || segment === '.') continue;
266
+ if (segment === '..') {
267
+ if (out.length === 0 || out[out.length - 1] === '..') {
268
+ if (!absolute) out.push('..');
269
+ } else {
270
+ out.pop();
271
+ }
272
+ continue;
273
+ }
274
+ out.push(segment);
275
+ }
276
+ if (out.length === 0) return absolute ? '/' : '.';
277
+ return (absolute ? '/' : '') + out.join('/');
278
+ }
279
+
280
+ /** uvwasi__is_path_sandboxed: does a normalized path stay under the descriptor's? */
281
+ function isSandboxed(path: string, fdPath: string): boolean {
282
+ if (fdPath.startsWith('/')) {
283
+ if (fdPath === '/') return path.startsWith('/');
284
+ return path === fdPath || path.startsWith(`${fdPath}/`);
285
+ }
286
+ if (fdPath === '.') return !(path === '..' || path.startsWith('../'));
287
+ if (!(path === fdPath || path.startsWith(`${fdPath}/`))) return false;
288
+ const rest = path.slice(fdPath.length + 1);
289
+ return !(rest === '..' || rest.startsWith('../'));
290
+ }
291
+
292
+ function dirnameOf(path: string): string {
293
+ const at = path.lastIndexOf('/');
294
+ if (at < 0) return '.';
295
+ if (at === 0) return '/';
296
+ return path.slice(0, at);
297
+ }
298
+
299
+ // ---------------------------------------------------------------------------
300
+ // The descriptor table: uvwasi's, over the engine's descriptors.
301
+ // ---------------------------------------------------------------------------
302
+
303
+ /** One WASI descriptor: a guest stream, an open file of the engine, or a directory. */
304
+ interface Descriptor {
305
+ kind: 'stream' | 'file' | 'dir';
306
+ stream?: 'stdin' | 'stdout' | 'stderr';
307
+ /** The engine's descriptor, for a file. */
308
+ fd?: number;
309
+ /** The guest's path, normalized; what path resolution is sandboxed to. */
310
+ path: string;
311
+ /** The engine's path the guest path maps to. */
312
+ hostPath: string;
313
+ type: number;
314
+ rightsBase: bigint;
315
+ rightsInheriting: bigint;
316
+ preopen: boolean;
317
+ fdflags: number;
318
+ /** The file cursor, kept here since the engine's descriptor has no lseek. */
319
+ position: number;
320
+ /** A stream's decoder, so a multi-byte character split across two writes arrives whole. */
321
+ decoder?: TextDecoder;
322
+ }
323
+
324
+ interface Filestat {
325
+ dev: bigint;
326
+ ino: bigint;
327
+ filetype: number;
328
+ nlink: bigint;
329
+ size: bigint;
330
+ atim: bigint;
331
+ mtim: bigint;
332
+ ctim: bigint;
333
+ }
334
+
335
+ /** uvwasi__stat_to_filetype */
336
+ function filetypeOf(stats: WasiHostStats): number {
337
+ if (stats.isSymbolicLink()) return FILETYPE.SYMBOLIC_LINK;
338
+ if (stats.isFile()) return FILETYPE.REGULAR_FILE;
339
+ if (stats.isDirectory()) return FILETYPE.DIRECTORY;
340
+ if (stats.isCharacterDevice?.()) return FILETYPE.CHARACTER_DEVICE;
341
+ if (stats.isBlockDevice?.()) return FILETYPE.BLOCK_DEVICE;
342
+ if (stats.isSocket?.() || stats.isFIFO?.()) return FILETYPE.SOCKET_STREAM;
343
+ return FILETYPE.UNKNOWN;
344
+ }
345
+
346
+ function nanosOf(ms: number | undefined, date: Date | undefined): bigint {
347
+ const value = typeof ms === 'number' ? ms : date instanceof Date ? date.getTime() : 0;
348
+ if (!Number.isFinite(value) || value <= 0) return 0n;
349
+ const whole = Math.floor(value);
350
+ return BigInt(whole) * 1_000_000n + BigInt(Math.round((value - whole) * 1e6));
351
+ }
352
+
353
+ /** uvwasi__stat_to_filestat */
354
+ function filestatOf(stats: WasiHostStats): Filestat {
355
+ return {
356
+ dev: BigInt(stats.dev ?? 0),
357
+ ino: BigInt(stats.ino ?? 0),
358
+ filetype: filetypeOf(stats),
359
+ nlink: BigInt(stats.nlink ?? 1),
360
+ size: BigInt(stats.size ?? 0),
361
+ atim: nanosOf(stats.atimeMs, stats.atime),
362
+ mtim: nanosOf(stats.mtimeMs, stats.mtime),
363
+ ctim: nanosOf(stats.ctimeMs, stats.ctime),
364
+ };
365
+ }
366
+
367
+ // ---------------------------------------------------------------------------
368
+ // Guest memory, read afresh on every call: the buffer changes when the guest
369
+ // grows its memory (Node's test fd_prestat_get_refresh grows between calls).
370
+ // ---------------------------------------------------------------------------
371
+
372
+ class GuestMemory {
373
+ readonly bytes: Uint8Array;
374
+ readonly view: DataView;
375
+ /** A shared memory (a threaded guest's) cannot be handed to TextDecoder or getRandomValues; its bytes are copied through. */
376
+ readonly shared: boolean;
377
+ constructor(buffer: ArrayBufferLike) {
378
+ this.bytes = new Uint8Array(buffer);
379
+ this.view = new DataView(buffer);
380
+ this.shared = typeof SharedArrayBuffer !== 'undefined' && buffer instanceof SharedArrayBuffer;
381
+ }
382
+ /** The bytes at a range, as a view where the memory is unshared and as a copy where it is shared. */
383
+ bytesAt(offset: number, length: number): Uint8Array {
384
+ const range = this.bytes.subarray(offset, offset + length);
385
+ return this.shared ? range.slice() : range;
386
+ }
387
+ /** node_wasi.cc's CHECK_BOUNDS_OR_RETURN. */
388
+ fits(offset: number, size: number): boolean {
389
+ return offset >= 0 && size >= 0 && offset + size <= this.bytes.length;
390
+ }
391
+ u8(offset: number): number { return this.view.getUint8(offset); }
392
+ u16(offset: number): number { return this.view.getUint16(offset, true); }
393
+ u32(offset: number): number { return this.view.getUint32(offset, true); }
394
+ u64(offset: number): bigint { return this.view.getBigUint64(offset, true); }
395
+ setU8(offset: number, value: number): void { this.view.setUint8(offset, value); }
396
+ setU16(offset: number, value: number): void { this.view.setUint16(offset, value, true); }
397
+ setU32(offset: number, value: number): void { this.view.setUint32(offset, value >>> 0, true); }
398
+ setU64(offset: number, value: bigint): void { this.view.setBigUint64(offset, BigInt.asUintN(64, value), true); }
399
+ text(offset: number, length: number): string {
400
+ return new TextDecoder().decode(this.bytesAt(offset, length));
401
+ }
402
+ writeFilestat(offset: number, stat: Filestat): void {
403
+ this.setU64(offset, stat.dev);
404
+ this.setU64(offset + 8, stat.ino);
405
+ this.setU8(offset + 16, stat.filetype);
406
+ this.setU64(offset + 24, stat.nlink);
407
+ this.setU64(offset + 32, stat.size);
408
+ this.setU64(offset + 40, stat.atim);
409
+ this.setU64(offset + 48, stat.mtim);
410
+ this.setU64(offset + 56, stat.ctim);
411
+ }
412
+ }
413
+
414
+ /** The (pointer, length) pairs an iovec array names, bounds-checked as a whole. */
415
+ function iovecsOf(memory: GuestMemory, iovsPtr: number, iovsLen: number): Array<[number, number]> | Errno {
416
+ if (!memory.fits(iovsPtr, iovsLen * SIZE.iovec)) return ERRNO.EOVERFLOW;
417
+ const iovs: Array<[number, number]> = [];
418
+ for (let index = 0; index < iovsLen; index += 1) {
419
+ const ptr = memory.u32(iovsPtr + index * SIZE.iovec);
420
+ const len = memory.u32(iovsPtr + index * SIZE.iovec + 4);
421
+ if (!memory.fits(ptr, len)) return ERRNO.EOVERFLOW;
422
+ iovs.push([ptr, len]);
423
+ }
424
+ return iovs;
425
+ }
426
+
427
+ // ---------------------------------------------------------------------------
428
+ // Clocks and sleeping.
429
+ // ---------------------------------------------------------------------------
430
+
431
+ function nanosSince(ms: number): bigint {
432
+ const whole = Math.floor(ms);
433
+ return BigInt(whole) * 1_000_000n + BigInt(Math.round((ms - whole) * 1e6));
434
+ }
435
+
436
+ function realtimeNanos(): bigint {
437
+ return nanosSince(performance.timeOrigin + performance.now());
438
+ }
439
+
440
+ function monotonicNanos(): bigint {
441
+ return nanosSince(performance.now());
442
+ }
443
+
444
+ /**
445
+ * A synchronous sleep, which is what a clock subscription is to the guest: it
446
+ * called poll_oneoff and expects to resume after the timeout. Atomics.wait
447
+ * parks the thread where the thread may park (a worker, a Node host); on a
448
+ * thread that may not, the time is spent watching the clock.
449
+ */
450
+ function sleepNanos(nanos: bigint): void {
451
+ if (nanos <= 0n) return;
452
+ const ms = Number(nanos) / 1e6;
453
+ const until = performance.now() + ms;
454
+ if (typeof SharedArrayBuffer !== 'undefined' && typeof Atomics !== 'undefined' && typeof Atomics.wait === 'function') {
455
+ try {
456
+ const cell = new Int32Array(new SharedArrayBuffer(4));
457
+ let remaining = until - performance.now();
458
+ while (remaining > 0) {
459
+ Atomics.wait(cell, 0, 0, remaining);
460
+ remaining = until - performance.now();
461
+ }
462
+ return;
463
+ } catch {
464
+ // Not allowed on this thread; fall through to the timed spin.
465
+ }
466
+ }
467
+ while (performance.now() < until) { /* spin */ }
468
+ }
469
+
470
+ // ---------------------------------------------------------------------------
471
+ // The binding: internalBinding('wasi').WASI for one guest.
472
+ // ---------------------------------------------------------------------------
473
+
474
+ /** What a wrap instance holds. Kept off the instance: Node's lib binds every enumerable property as a function. */
475
+ interface WrapState {
476
+ args: string[];
477
+ env: string[];
478
+ table: Map<number, Descriptor>;
479
+ memory: WebAssembly.Memory | undefined;
480
+ /** Bytes the guest's stdin stream has yielded and the guest has not yet read. */
481
+ stdinPending: Uint8Array;
482
+ stdinEnded: boolean;
483
+ }
484
+
485
+ interface BindingClass {
486
+ new (args: string[], env: string[], preopens: string[], stdio: number[]): object;
487
+ }
488
+
489
+ function isWasmMemory(value: unknown): value is WebAssembly.Memory {
490
+ // A memory from another realm (Node's tests instantiate in a vm context) is
491
+ // not an instanceof this realm's constructor; its tag is the same everywhere.
492
+ return value !== null && typeof value === 'object' && Object.prototype.toString.call(value) === '[object WebAssembly.Memory]';
493
+ }
494
+
495
+ function bindingClassFor(fs: WasiHostFs, process: WasiHostProcess): BindingClass {
496
+ const states = new WeakMap<object, WrapState>();
497
+ const encoder = new TextEncoder();
498
+
499
+ const stateOf = (wrap: object): WrapState => {
500
+ const state = states.get(wrap);
501
+ if (state === undefined) throw new TypeError('Illegal invocation');
502
+ return state;
503
+ };
504
+
505
+ // A stream descriptor of the guest's process. The engine's stdio are pipes
506
+ // to the shell (its streams report no TTY), which uvwasi types as a socket
507
+ // stream with socket rights; a TTY gets the narrower TTY rights.
508
+ const streamDescriptor = (stream: 'stdin' | 'stdout' | 'stderr', isTTY: boolean): Descriptor => ({
509
+ kind: 'stream',
510
+ stream,
511
+ path: `<${stream}>`,
512
+ hostPath: '',
513
+ type: isTTY ? FILETYPE.CHARACTER_DEVICE : FILETYPE.SOCKET_STREAM,
514
+ rightsBase: isTTY ? RIGHTS_TTY_BASE : RIGHTS_SOCKET_BASE,
515
+ rightsInheriting: isTTY ? RIGHTS_TTY_INHERITING : RIGHTS_SOCKET_INHERITING,
516
+ preopen: false,
517
+ fdflags: 0,
518
+ position: 0,
519
+ decoder: stream === 'stdin' ? undefined : new TextDecoder(),
520
+ });
521
+
522
+ /** uvwasi__get_rights */
523
+ const rightsFor = (type: number, accessFlags: number): [bigint, bigint] => {
524
+ let base: bigint;
525
+ let inheriting: bigint;
526
+ switch (type) {
527
+ case FILETYPE.REGULAR_FILE: base = RIGHTS_REGULAR_FILE_BASE; inheriting = RIGHTS_REGULAR_FILE_INHERITING; break;
528
+ case FILETYPE.DIRECTORY: base = RIGHTS_DIRECTORY_BASE; inheriting = RIGHTS_DIRECTORY_INHERITING; break;
529
+ case FILETYPE.SOCKET_STREAM: case FILETYPE.SOCKET_DGRAM: base = RIGHTS_SOCKET_BASE; inheriting = RIGHTS_SOCKET_INHERITING; break;
530
+ case FILETYPE.CHARACTER_DEVICE: case FILETYPE.BLOCK_DEVICE: base = RIGHTS_ALL; inheriting = RIGHTS_ALL; break;
531
+ default: base = 0n; inheriting = 0n;
532
+ }
533
+ const access = accessFlags & 3;
534
+ if (access === O_RDONLY) base &= ~RIGHT.FD_WRITE;
535
+ else if (access === O_WRONLY) base &= ~RIGHT.FD_READ;
536
+ return [base, inheriting];
537
+ };
538
+
539
+ /** A descriptor over one of the engine's open files (a stdio option naming an fd, or a path_open). */
540
+ const fileDescriptor = (fd: number, guestPath: string, hostPath: string, accessFlags: number, fdflags: number): Descriptor => {
541
+ const stats = fs.fstatSync(fd);
542
+ const type = filetypeOf(stats);
543
+ const [rightsBase, rightsInheriting] = rightsFor(type, accessFlags);
544
+ return { kind: 'file', fd, path: guestPath, hostPath, type, rightsBase, rightsInheriting, preopen: false, fdflags, position: fdflags & FDFLAG.APPEND ? stats.size : 0 };
545
+ };
546
+
547
+ /** uvwasi_fd_table_insert: the lowest free slot. */
548
+ const insert = (table: Map<number, Descriptor>, descriptor: Descriptor): number => {
549
+ let fd = 0;
550
+ while (table.has(fd)) fd += 1;
551
+ table.set(fd, descriptor);
552
+ return fd;
553
+ };
554
+
555
+ /** uvwasi_fd_table_get: the descriptor, or EBADF, or ENOTCAPABLE when it lacks a right asked for. */
556
+ const lookup = (state: WrapState, fd: number, rightsBase = 0n, rightsInheriting = 0n): Descriptor | Errno => {
557
+ const descriptor = state.table.get(fd >>> 0);
558
+ if (descriptor === undefined) return ERRNO.EBADF;
559
+ if ((~descriptor.rightsBase & rightsBase) !== 0n || (~descriptor.rightsInheriting & rightsInheriting) !== 0n) return ERRNO.ENOTCAPABLE;
560
+ return descriptor;
561
+ };
562
+
563
+ /** The host path a guest path under a descriptor maps to (uvwasi__resolve_path_to_host). */
564
+ const hostPathOf = (descriptor: Descriptor, guestPath: string): string => {
565
+ if (guestPath === descriptor.path) return descriptor.hostPath;
566
+ const rest = descriptor.path === '/' ? guestPath.slice(1) : descriptor.path === '.' ? guestPath : guestPath.slice(descriptor.path.length + 1);
567
+ return descriptor.hostPath === '/' ? `/${rest}` : `${descriptor.hostPath}/${rest}`;
568
+ };
569
+
570
+ /**
571
+ * uvwasi__resolve_path: the guest path normalized against the descriptor's,
572
+ * refused when it leaves the descriptor's tree, and, when asked, followed
573
+ * through symlinks with each target resolved the same way.
574
+ */
575
+ const resolve = (descriptor: Descriptor, path: string, lookupFlags: number): { guest: string; host: string } | Errno => {
576
+ if (descriptor.kind === 'stream') return ERRNO.ENOTDIR;
577
+ // uvwasi refuses these before it resolves anything, and normalizing first
578
+ // folded them away: an empty path opened the preopen itself, a trailing
579
+ // slash opened a file as though it were a directory, an absolute path
580
+ // that happened to land inside the preopen was admitted where uvwasi
581
+ // refuses every absolute path, and a path carrying a NUL reached the
582
+ // filesystem as a name.
583
+ if (path.length === 0) return ERRNO.EINVAL;
584
+ if (path.includes('\0')) return ERRNO.EINVAL;
585
+ if (path.startsWith('/')) return ERRNO.ENOTCAPABLE;
586
+ // A path written with a trailing slash names a directory, whatever it
587
+ // resolves to.
588
+ const mustBeDirectory = path.endsWith('/');
589
+ let input = path;
590
+ for (let follows = 0; ; follows += 1) {
591
+ const normalized = normalizePath(`${descriptor.path}/${input}`);
592
+ if (!isSandboxed(normalized, descriptor.path)) return ERRNO.ENOTCAPABLE;
593
+ const host = hostPathOf(descriptor, normalized);
594
+ if (mustBeDirectory) {
595
+ try { if (!fs.statSync(host).isDirectory()) return ERRNO.ENOTDIR; } catch { /* absent: the caller's own errno */ }
596
+ }
597
+ if ((lookupFlags & LOOKUP_SYMLINK_FOLLOW) === 0) return { guest: normalized, host };
598
+ let target: string;
599
+ try {
600
+ if (!fs.lstatSync(host).isSymbolicLink() || typeof fs.readlinkSync !== 'function') return { guest: normalized, host };
601
+ target = fs.readlinkSync(host);
602
+ } catch {
603
+ // Not there, or not a link: both are fine here, as uvwasi treats them.
604
+ return { guest: normalized, host };
605
+ }
606
+ if (follows + 1 >= MAX_SYMLINK_FOLLOWS) return ERRNO.ELOOP;
607
+ // A link's target is resolved against the descriptor again, and an
608
+ // absolute target leaves the descriptor's tree.
609
+ if (target.startsWith('/')) return ERRNO.ENOTCAPABLE;
610
+ input = `${dirnameOf(normalized)}/${target}`;
611
+ }
612
+ };
613
+
614
+ const filestatOfDescriptor = (descriptor: Descriptor): Filestat => {
615
+ if (descriptor.kind === 'file') return filestatOf(fs.fstatSync(descriptor.fd!));
616
+ if (descriptor.kind === 'dir') return filestatOf(fs.statSync(descriptor.hostPath));
617
+ return { dev: 0n, ino: 0n, filetype: descriptor.type, nlink: 1n, size: 0n, atim: 0n, mtim: 0n, ctim: 0n };
618
+ };
619
+
620
+ const sizeOf = (descriptor: Descriptor): number => (descriptor.kind === 'file' ? fs.fstatSync(descriptor.fd!).size : 0);
621
+
622
+ /** What the guest's stdin has to give: whatever its stream yields now; then EOF. */
623
+ const readStdin = (state: WrapState, into: Uint8Array): number => {
624
+ if (state.stdinPending.length === 0 && !state.stdinEnded) {
625
+ const chunk = typeof process.stdin.read === 'function' ? process.stdin.read() : null;
626
+ if (chunk === null || chunk === undefined) state.stdinEnded = true;
627
+ else state.stdinPending = typeof chunk === 'string' ? encoder.encode(chunk) : new Uint8Array(chunk);
628
+ }
629
+ const count = Math.min(into.length, state.stdinPending.length);
630
+ into.set(state.stdinPending.subarray(0, count));
631
+ state.stdinPending = state.stdinPending.subarray(count);
632
+ return count;
633
+ };
634
+
635
+ const writeStream = (descriptor: Descriptor, bytes: Uint8Array): void => {
636
+ const text = descriptor.decoder!.decode(bytes, { stream: true });
637
+ if (text.length === 0) return;
638
+ (descriptor.stream === 'stderr' ? process.stderr : process.stdout).write(text);
639
+ };
640
+
641
+ /** uvwasi__get_filestat_set_times: the seconds to set, from the flags and the times given. */
642
+ const timesToSet = (descriptor: Descriptor | null, hostPath: string, atim: bigint, mtim: bigint, flags: number): [number, number] => {
643
+ const now = realtimeNanos();
644
+ let current: Filestat | undefined;
645
+ if ((flags & (FSTFLAG.ATIM | FSTFLAG.ATIM_NOW)) === 0 || (flags & (FSTFLAG.MTIM | FSTFLAG.MTIM_NOW)) === 0) {
646
+ current = descriptor ? filestatOfDescriptor(descriptor) : filestatOf(fs.lstatSync(hostPath));
647
+ }
648
+ const atime = flags & FSTFLAG.ATIM_NOW ? now : flags & FSTFLAG.ATIM ? atim : current!.atim;
649
+ const mtime = flags & FSTFLAG.MTIM_NOW ? now : flags & FSTFLAG.MTIM ? mtim : current!.mtim;
650
+ // Seconds, fraction kept: uvwasi sets the times to the nanosecond, and
651
+ // truncating here lost every sub-second a program asked for.
652
+ return [Number(atime) / Number(NANOS_PER_SEC), Number(mtime) / Number(NANOS_PER_SEC)];
653
+ };
654
+
655
+ const setTimes = (hostPath: string, atime: number, mtime: number): Errno => {
656
+ if (typeof fs.utimesSync !== 'function') return ERRNO.ENOSYS;
657
+ fs.utimesSync(hostPath, atime, mtime);
658
+ return ERRNO.ESUCCESS;
659
+ };
660
+
661
+ class WASI {
662
+ constructor(args: string[], env: string[], preopens: string[], stdio: number[]) {
663
+ const table = new Map<number, Descriptor>();
664
+ const state: WrapState = { args: [...args], env: [...env], table, memory: undefined, stdinPending: new Uint8Array(0), stdinEnded: false };
665
+ // uvwasi_init: stdio first, at 0, 1, 2; a stdio option naming another
666
+ // descriptor is one of the engine's open files (the guest opened it).
667
+ const names = ['stdin', 'stdout', 'stderr'] as const;
668
+ for (let index = 0; index < 3; index += 1) {
669
+ const given = stdio[index];
670
+ if (given === index) {
671
+ table.set(index, streamDescriptor(names[index], (process[names[index]] as { isTTY?: boolean }).isTTY === true));
672
+ continue;
673
+ }
674
+ if (given === 0 || given === 1 || given === 2) {
675
+ table.set(index, streamDescriptor(names[given], (process[names[given]] as { isTTY?: boolean }).isTTY === true));
676
+ continue;
677
+ }
678
+ try {
679
+ table.set(index, fileDescriptor(given, `<${names[index]}>`, '', O_RDWR, 0));
680
+ } catch (error) {
681
+ throw wasiException(errnoOf(error), 'uvwasi_init');
682
+ }
683
+ }
684
+ // Then the preopens, in the order given, each a directory that is there.
685
+ for (let index = 0; index + 1 < preopens.length; index += 2) {
686
+ const mapped = preopens[index];
687
+ const real = preopens[index + 1];
688
+ let hostPath: string;
689
+ let stats: WasiHostStats;
690
+ try {
691
+ hostPath = fs.realpathSync(real);
692
+ stats = fs.statSync(hostPath);
693
+ } catch (error) {
694
+ throw wasiException(errnoOf(error), 'uvwasi_init');
695
+ }
696
+ if (!stats.isDirectory()) throw wasiException(ERRNO.ENOTDIR, 'uvwasi_init');
697
+ insert(table, {
698
+ kind: 'dir',
699
+ path: normalizePath(mapped),
700
+ hostPath,
701
+ type: FILETYPE.DIRECTORY,
702
+ rightsBase: RIGHTS_DIRECTORY_BASE,
703
+ rightsInheriting: RIGHTS_DIRECTORY_INHERITING,
704
+ preopen: true,
705
+ fdflags: 0,
706
+ position: 0,
707
+ });
708
+ }
709
+ states.set(this, state);
710
+ // An own, enumerable, deletable property, as node_wasi.cc installs it on
711
+ // the instance: lib/wasi.js takes it and deletes it from the import object.
712
+ Object.defineProperty(this, '_setMemory', {
713
+ value: function _setMemory(this: object, memory: unknown) {
714
+ if (!isWasmMemory(memory)) {
715
+ throw Object.assign(new TypeError('"instance.exports.memory" property must be a WebAssembly.Memory object'), { code: 'ERR_INVALID_ARG_TYPE' });
716
+ }
717
+ stateOf(this).memory = memory;
718
+ },
719
+ enumerable: true,
720
+ writable: true,
721
+ configurable: true,
722
+ });
723
+ }
724
+ }
725
+
726
+ type Syscall = (this: object, state: WrapState, memory: GuestMemory, ...args: Array<number | bigint>) => Errno | void;
727
+
728
+ /**
729
+ * One syscall on the prototype, enumerable so lib/wasi.js's `for...in` binds
730
+ * it. As node_wasi.cc's SlowCallback: the wrong count or type of arguments
731
+ * is EINVAL; a call before the memory is set throws ERR_WASI_NOT_STARTED;
732
+ * the memory is read afresh; a filesystem failure is its errno.
733
+ */
734
+ const define = (name: string, signature: string, syscall: Syscall): void => {
735
+ const method = function (this: object, ...args: Array<number | bigint>): number | void {
736
+ if (args.length !== signature.length) return ERRNO.EINVAL;
737
+ for (let index = 0; index < signature.length; index += 1) {
738
+ const kind = signature[index] === 'L' ? 'bigint' : 'number';
739
+ if (typeof args[index] !== kind) return ERRNO.EINVAL;
740
+ }
741
+ const state = stateOf(this);
742
+ if (state.memory === undefined) throw new ERR_WASI_NOT_STARTED();
743
+ const normalized = args.map((value, index) => (signature[index] === 'L' ? BigInt.asUintN(64, value as bigint) : (value as number) >>> 0));
744
+ try {
745
+ const result = syscall.call(this, state, new GuestMemory(state.memory.buffer), ...normalized);
746
+ return result === undefined ? ERRNO.ESUCCESS : result;
747
+ } catch (error) {
748
+ if (isFsError(error)) return errnoOf(error);
749
+ throw error;
750
+ }
751
+ };
752
+ Object.defineProperty(method, 'name', { value: name, configurable: true });
753
+ Object.defineProperty(WASI.prototype, name, { value: method, enumerable: true, writable: true, configurable: true });
754
+ };
755
+
756
+ // Signatures: 'i' a 32-bit integer, 'L' a 64-bit one (a BigInt at this boundary).
757
+ // The order below is the order node_wasi.cc declares the syscalls, because
758
+ // lib/wasi.js builds `wasiImport` by a `for...in` over this prototype and a
759
+ // guest reads `Object.keys(wasi.wasiImport)`: not alphabetical
760
+ // (fd_prestat_get precedes fd_prestat_dir_name).
761
+
762
+ define('args_get', 'ii', (state, memory, argvPtr, bufPtr) => {
763
+ const bytes = state.args.map((arg) => encoder.encode(`${arg}\0`));
764
+ const size = bytes.reduce((sum, chunk) => sum + chunk.length, 0);
765
+ if (!memory.fits(bufPtr as number, size) || !memory.fits(argvPtr as number, state.args.length * 4)) return ERRNO.EOVERFLOW;
766
+ let at = bufPtr as number;
767
+ bytes.forEach((chunk, index) => {
768
+ memory.setU32((argvPtr as number) + index * 4, at);
769
+ memory.bytes.set(chunk, at);
770
+ at += chunk.length;
771
+ });
772
+ });
773
+
774
+ define('args_sizes_get', 'ii', (state, memory, argcPtr, bufSizePtr) => {
775
+ if (!memory.fits(argcPtr as number, 4) || !memory.fits(bufSizePtr as number, 4)) return ERRNO.EOVERFLOW;
776
+ memory.setU32(argcPtr as number, state.args.length);
777
+ memory.setU32(bufSizePtr as number, state.args.reduce((sum, arg) => sum + encoder.encode(arg).length + 1, 0));
778
+ });
779
+
780
+ define('clock_res_get', 'ii', (_state, memory, clockId, resolutionPtr) => {
781
+ if (!memory.fits(resolutionPtr as number, 8)) return ERRNO.EOVERFLOW;
782
+ switch (clockId) {
783
+ case CLOCK.REALTIME: case CLOCK.MONOTONIC: case CLOCK.PROCESS_CPUTIME_ID: case CLOCK.THREAD_CPUTIME_ID:
784
+ memory.setU64(resolutionPtr as number, 1n);
785
+ return;
786
+ default:
787
+ return ERRNO.EINVAL;
788
+ }
789
+ });
790
+
791
+ define('clock_time_get', 'iLi', (_state, memory, clockId, _precision, timePtr) => {
792
+ if (!memory.fits(timePtr as number, 8)) return ERRNO.EOVERFLOW;
793
+ switch (clockId) {
794
+ case CLOCK.REALTIME:
795
+ memory.setU64(timePtr as number, realtimeNanos());
796
+ return;
797
+ case CLOCK.MONOTONIC:
798
+ // A tab has no CPU clock; the process and thread CPU clocks answer the
799
+ // time the program has run, which is what a guest measuring its own
800
+ // progress against them needs to see advance.
801
+ case CLOCK.PROCESS_CPUTIME_ID:
802
+ case CLOCK.THREAD_CPUTIME_ID:
803
+ memory.setU64(timePtr as number, monotonicNanos());
804
+ return;
805
+ default:
806
+ return ERRNO.EINVAL;
807
+ }
808
+ });
809
+
810
+ define('environ_get', 'ii', (state, memory, environPtr, bufPtr) => {
811
+ const bytes = state.env.map((entry) => encoder.encode(`${entry}\0`));
812
+ const size = bytes.reduce((sum, chunk) => sum + chunk.length, 0);
813
+ if (!memory.fits(bufPtr as number, size) || !memory.fits(environPtr as number, state.env.length * 4)) return ERRNO.EOVERFLOW;
814
+ let at = bufPtr as number;
815
+ bytes.forEach((chunk, index) => {
816
+ memory.setU32((environPtr as number) + index * 4, at);
817
+ memory.bytes.set(chunk, at);
818
+ at += chunk.length;
819
+ });
820
+ });
821
+
822
+ define('environ_sizes_get', 'ii', (state, memory, countPtr, bufSizePtr) => {
823
+ if (!memory.fits(countPtr as number, 4) || !memory.fits(bufSizePtr as number, 4)) return ERRNO.EOVERFLOW;
824
+ memory.setU32(countPtr as number, state.env.length);
825
+ memory.setU32(bufSizePtr as number, state.env.reduce((sum, entry) => sum + encoder.encode(entry).length + 1, 0));
826
+ });
827
+
828
+ define('fd_advise', 'iLLi', (state, _memory, fd, _offset, _len, advice) => {
829
+ const descriptor = lookup(state, fd as number, RIGHT.FD_ADVISE);
830
+ if (typeof descriptor === 'number') return descriptor;
831
+ if ((advice as number) > 5) return ERRNO.EINVAL;
832
+ });
833
+
834
+ define('fd_allocate', 'iLL', (state, _memory, fd, offset, len) => {
835
+ const descriptor = lookup(state, fd as number, RIGHT.FD_ALLOCATE);
836
+ if (typeof descriptor === 'number') return descriptor;
837
+ if (descriptor.kind !== 'file') return ERRNO.EBADF;
838
+ const end = Number((offset as bigint) + (len as bigint));
839
+ if (end > sizeOf(descriptor)) fs.ftruncateSync(descriptor.fd!, end);
840
+ });
841
+
842
+ define('fd_close', 'i', (state, _memory, fd) => {
843
+ const descriptor = lookup(state, fd as number);
844
+ if (typeof descriptor === 'number') return descriptor;
845
+ if (descriptor.kind === 'file') fs.closeSync(descriptor.fd!);
846
+ state.table.delete(fd as number);
847
+ });
848
+
849
+ define('fd_datasync', 'i', (state, _memory, fd) => {
850
+ const descriptor = lookup(state, fd as number, RIGHT.FD_DATASYNC);
851
+ if (typeof descriptor === 'number') return descriptor;
852
+ });
853
+
854
+ define('fd_fdstat_get', 'ii', (state, memory, fd, bufPtr) => {
855
+ if (!memory.fits(bufPtr as number, SIZE.fdstat)) return ERRNO.EOVERFLOW;
856
+ const descriptor = lookup(state, fd as number);
857
+ if (typeof descriptor === 'number') return descriptor;
858
+ memory.setU8(bufPtr as number, descriptor.type);
859
+ memory.setU16((bufPtr as number) + 2, descriptor.fdflags);
860
+ memory.setU64((bufPtr as number) + 8, descriptor.rightsBase);
861
+ memory.setU64((bufPtr as number) + 16, descriptor.rightsInheriting);
862
+ });
863
+
864
+ define('fd_fdstat_set_flags', 'ii', (state, _memory, fd, flags) => {
865
+ const descriptor = lookup(state, fd as number, RIGHT.FD_FDSTAT_SET_FLAGS);
866
+ if (typeof descriptor === 'number') return descriptor;
867
+ descriptor.fdflags = flags as number;
868
+ });
869
+
870
+ define('fd_fdstat_set_rights', 'iLL', (state, _memory, fd, rightsBase, rightsInheriting) => {
871
+ const descriptor = lookup(state, fd as number);
872
+ if (typeof descriptor === 'number') return descriptor;
873
+ // Rights can only be narrowed.
874
+ if ((~descriptor.rightsBase & (rightsBase as bigint)) !== 0n || (~descriptor.rightsInheriting & (rightsInheriting as bigint)) !== 0n) return ERRNO.ENOTCAPABLE;
875
+ descriptor.rightsBase = rightsBase as bigint;
876
+ descriptor.rightsInheriting = rightsInheriting as bigint;
877
+ });
878
+
879
+ define('fd_filestat_get', 'ii', (state, memory, fd, bufPtr) => {
880
+ if (!memory.fits(bufPtr as number, SIZE.filestat)) return ERRNO.EOVERFLOW;
881
+ const descriptor = lookup(state, fd as number, RIGHT.FD_FILESTAT_GET);
882
+ if (typeof descriptor === 'number') return descriptor;
883
+ memory.writeFilestat(bufPtr as number, filestatOfDescriptor(descriptor));
884
+ });
885
+
886
+ define('fd_filestat_set_size', 'iL', (state, _memory, fd, size) => {
887
+ const descriptor = lookup(state, fd as number, RIGHT.FD_FILESTAT_SET_SIZE);
888
+ if (typeof descriptor === 'number') return descriptor;
889
+ if (descriptor.kind !== 'file') return ERRNO.EBADF;
890
+ fs.ftruncateSync(descriptor.fd!, Number(size as bigint));
891
+ });
892
+
893
+ define('fd_filestat_set_times', 'iLLi', (state, _memory, fd, atim, mtim, flags) => {
894
+ const descriptor = lookup(state, fd as number, RIGHT.FD_FILESTAT_SET_TIMES);
895
+ if (typeof descriptor === 'number') return descriptor;
896
+ if (descriptor.kind === 'stream') return ERRNO.EBADF;
897
+ const [atime, mtime] = timesToSet(descriptor, descriptor.hostPath, atim as bigint, mtim as bigint, flags as number);
898
+ return setTimes(descriptor.hostPath, atime, mtime);
899
+ });
900
+
901
+ define('fd_pread', 'iiiLi', (state, memory, fd, iovsPtr, iovsLen, offset, nreadPtr) => {
902
+ if (!memory.fits(nreadPtr as number, 4)) return ERRNO.EOVERFLOW;
903
+ const iovs = iovecsOf(memory, iovsPtr as number, iovsLen as number);
904
+ if (typeof iovs === 'number') return iovs;
905
+ const descriptor = lookup(state, fd as number, RIGHT.FD_READ | RIGHT.FD_SEEK);
906
+ if (typeof descriptor === 'number') return descriptor;
907
+ if (descriptor.kind !== 'file') return ERRNO.EBADF;
908
+ let at = Number(offset as bigint);
909
+ let total = 0;
910
+ for (const [ptr, len] of iovs) {
911
+ const count = fs.readSync(descriptor.fd!, memory.bytes.subarray(ptr, ptr + len), 0, len, at);
912
+ at += count;
913
+ total += count;
914
+ if (count < len) break;
915
+ }
916
+ memory.setU32(nreadPtr as number, total);
917
+ });
918
+
919
+ define('fd_prestat_get', 'ii', (state, memory, fd, bufPtr) => {
920
+ if (!memory.fits(bufPtr as number, SIZE.prestat)) return ERRNO.EOVERFLOW;
921
+ const descriptor = lookup(state, fd as number);
922
+ if (typeof descriptor === 'number') return descriptor;
923
+ if (!descriptor.preopen) return ERRNO.EINVAL;
924
+ memory.setU8(bufPtr as number, PREOPENTYPE_DIR);
925
+ memory.setU32((bufPtr as number) + 4, encoder.encode(descriptor.path).length);
926
+ });
927
+
928
+ define('fd_prestat_dir_name', 'iii', (state, memory, fd, pathPtr, pathLen) => {
929
+ if (!memory.fits(pathPtr as number, pathLen as number)) return ERRNO.EOVERFLOW;
930
+ const descriptor = lookup(state, fd as number);
931
+ if (typeof descriptor === 'number') return descriptor;
932
+ if (!descriptor.preopen) return ERRNO.EBADF;
933
+ const name = encoder.encode(descriptor.path);
934
+ if (name.length > (pathLen as number)) return ERRNO.ENOBUFS;
935
+ memory.bytes.set(name, pathPtr as number);
936
+ });
937
+
938
+ define('fd_pwrite', 'iiiLi', (state, memory, fd, iovsPtr, iovsLen, offset, nwrittenPtr) => {
939
+ if (!memory.fits(nwrittenPtr as number, 4)) return ERRNO.EOVERFLOW;
940
+ const iovs = iovecsOf(memory, iovsPtr as number, iovsLen as number);
941
+ if (typeof iovs === 'number') return iovs;
942
+ const descriptor = lookup(state, fd as number, RIGHT.FD_WRITE | RIGHT.FD_SEEK);
943
+ if (typeof descriptor === 'number') return descriptor;
944
+ if (descriptor.kind !== 'file') return ERRNO.EBADF;
945
+ let at = Number(offset as bigint);
946
+ let total = 0;
947
+ for (const [ptr, len] of iovs) {
948
+ const count = fs.writeSync(descriptor.fd!, memory.bytes.subarray(ptr, ptr + len), 0, len, at);
949
+ at += count;
950
+ total += count;
951
+ }
952
+ memory.setU32(nwrittenPtr as number, total);
953
+ });
954
+
955
+ define('fd_read', 'iiii', (state, memory, fd, iovsPtr, iovsLen, nreadPtr) => {
956
+ if (!memory.fits(nreadPtr as number, 4)) return ERRNO.EOVERFLOW;
957
+ const iovs = iovecsOf(memory, iovsPtr as number, iovsLen as number);
958
+ if (typeof iovs === 'number') return iovs;
959
+ const descriptor = lookup(state, fd as number, RIGHT.FD_READ);
960
+ if (typeof descriptor === 'number') return descriptor;
961
+ let total = 0;
962
+ if (descriptor.kind === 'stream') {
963
+ if (descriptor.stream !== 'stdin') return ERRNO.EBADF;
964
+ for (const [ptr, len] of iovs) {
965
+ const count = readStdin(state, memory.bytes.subarray(ptr, ptr + len));
966
+ total += count;
967
+ if (count < len) break;
968
+ }
969
+ } else if (descriptor.kind === 'file') {
970
+ for (const [ptr, len] of iovs) {
971
+ const count = fs.readSync(descriptor.fd!, memory.bytes.subarray(ptr, ptr + len), 0, len, descriptor.position);
972
+ descriptor.position += count;
973
+ total += count;
974
+ if (count < len) break;
975
+ }
976
+ } else {
977
+ return ERRNO.EISDIR;
978
+ }
979
+ memory.setU32(nreadPtr as number, total);
980
+ });
981
+
982
+ define('fd_readdir', 'iiiLi', (state, memory, fd, bufPtr, bufLen, cookie, bufusedPtr) => {
983
+ if (!memory.fits(bufPtr as number, bufLen as number) || !memory.fits(bufusedPtr as number, 4)) return ERRNO.EOVERFLOW;
984
+ const descriptor = lookup(state, fd as number, RIGHT.FD_READDIR);
985
+ if (typeof descriptor === 'number') return descriptor;
986
+ if (descriptor.kind !== 'dir') return ERRNO.ENOTDIR;
987
+ const entries = fs.readdirSync(descriptor.hostPath, { withFileTypes: true });
988
+ // The cookie is the index of the next entry to hand over, as uvwasi's is
989
+ // the directory stream's telldir position.
990
+ let used = 0;
991
+ for (let index = Number(cookie as bigint); index < entries.length; index += 1) {
992
+ const entry = entries[index];
993
+ const name = encoder.encode(entry.name);
994
+ if (used + SIZE.dirent > (bufLen as number)) {
995
+ used = bufLen as number;
996
+ break;
997
+ }
998
+ let ino = 0n;
999
+ try {
1000
+ ino = BigInt(fs.lstatSync(`${descriptor.hostPath === '/' ? '' : descriptor.hostPath}/${entry.name}`).ino ?? 0);
1001
+ } catch {
1002
+ // An entry that went away between the listing and its stat is listed as it was.
1003
+ }
1004
+ const at = (bufPtr as number) + used;
1005
+ memory.setU64(at, BigInt(index + 1));
1006
+ memory.setU64(at + 8, ino);
1007
+ memory.setU32(at + 16, name.length);
1008
+ memory.setU8(at + 20, entry.isSymbolicLink() ? FILETYPE.SYMBOLIC_LINK : entry.isDirectory() ? FILETYPE.DIRECTORY : entry.isFile() ? FILETYPE.REGULAR_FILE : FILETYPE.UNKNOWN);
1009
+ used += SIZE.dirent;
1010
+ const room = (bufLen as number) - used;
1011
+ const copied = Math.min(room, name.length);
1012
+ memory.bytes.set(name.subarray(0, copied), (bufPtr as number) + used);
1013
+ used += copied;
1014
+ if ((bufLen as number) - used === 0) break;
1015
+ }
1016
+ memory.setU32(bufusedPtr as number, used);
1017
+ });
1018
+
1019
+ define('fd_renumber', 'ii', (state, _memory, from, to) => {
1020
+ if (from === to) return;
1021
+ const source = state.table.get(from as number);
1022
+ const target = state.table.get(to as number);
1023
+ if (source === undefined || target === undefined) return ERRNO.EBADF;
1024
+ if (target.kind === 'file') fs.closeSync(target.fd!);
1025
+ state.table.set(to as number, source);
1026
+ state.table.delete(from as number);
1027
+ });
1028
+
1029
+ define('fd_seek', 'iLii', (state, memory, fd, offset, whence, newOffsetPtr) => {
1030
+ if (!memory.fits(newOffsetPtr as number, 8)) return ERRNO.EOVERFLOW;
1031
+ const descriptor = lookup(state, fd as number, RIGHT.FD_SEEK);
1032
+ if (typeof descriptor === 'number') return descriptor;
1033
+ if (descriptor.kind !== 'file') return ERRNO.ESPIPE;
1034
+ const delta = Number(BigInt.asIntN(64, offset as bigint));
1035
+ let base: number;
1036
+ switch (whence) {
1037
+ case WHENCE.SET: base = 0; break;
1038
+ case WHENCE.CUR: base = descriptor.position; break;
1039
+ case WHENCE.END: base = sizeOf(descriptor); break;
1040
+ default: return ERRNO.EINVAL;
1041
+ }
1042
+ const next = base + delta;
1043
+ if (next < 0) return ERRNO.EINVAL;
1044
+ descriptor.position = next;
1045
+ memory.setU64(newOffsetPtr as number, BigInt(next));
1046
+ });
1047
+
1048
+ define('fd_sync', 'i', (state, _memory, fd) => {
1049
+ const descriptor = lookup(state, fd as number, RIGHT.FD_SYNC);
1050
+ if (typeof descriptor === 'number') return descriptor;
1051
+ });
1052
+
1053
+ define('fd_tell', 'ii', (state, memory, fd, offsetPtr) => {
1054
+ if (!memory.fits(offsetPtr as number, 8)) return ERRNO.EOVERFLOW;
1055
+ const descriptor = lookup(state, fd as number, RIGHT.FD_TELL);
1056
+ if (typeof descriptor === 'number') return descriptor;
1057
+ if (descriptor.kind !== 'file') return ERRNO.ESPIPE;
1058
+ memory.setU64(offsetPtr as number, BigInt(descriptor.position));
1059
+ });
1060
+
1061
+ define('fd_write', 'iiii', (state, memory, fd, iovsPtr, iovsLen, nwrittenPtr) => {
1062
+ if (!memory.fits(nwrittenPtr as number, 4)) return ERRNO.EOVERFLOW;
1063
+ const iovs = iovecsOf(memory, iovsPtr as number, iovsLen as number);
1064
+ if (typeof iovs === 'number') return iovs;
1065
+ const descriptor = lookup(state, fd as number, RIGHT.FD_WRITE);
1066
+ if (typeof descriptor === 'number') return descriptor;
1067
+ let total = 0;
1068
+ if (descriptor.kind === 'stream') {
1069
+ if (descriptor.stream === 'stdin') return ERRNO.EBADF;
1070
+ for (const [ptr, len] of iovs) {
1071
+ writeStream(descriptor, memory.bytesAt(ptr, len));
1072
+ total += len;
1073
+ }
1074
+ } else if (descriptor.kind === 'file') {
1075
+ for (const [ptr, len] of iovs) {
1076
+ // O_APPEND: every write lands at the end, and the cursor follows it.
1077
+ const at = descriptor.fdflags & FDFLAG.APPEND ? sizeOf(descriptor) : descriptor.position;
1078
+ const count = fs.writeSync(descriptor.fd!, memory.bytes.subarray(ptr, ptr + len), 0, len, at);
1079
+ descriptor.position = at + count;
1080
+ total += count;
1081
+ }
1082
+ } else {
1083
+ return ERRNO.EISDIR;
1084
+ }
1085
+ memory.setU32(nwrittenPtr as number, total);
1086
+ });
1087
+
1088
+ define('path_create_directory', 'iii', (state, memory, fd, pathPtr, pathLen) => {
1089
+ if (!memory.fits(pathPtr as number, pathLen as number)) return ERRNO.EOVERFLOW;
1090
+ const descriptor = lookup(state, fd as number, RIGHT.PATH_CREATE_DIRECTORY);
1091
+ if (typeof descriptor === 'number') return descriptor;
1092
+ const resolved = resolve(descriptor, memory.text(pathPtr as number, pathLen as number), 0);
1093
+ if (typeof resolved === 'number') return resolved;
1094
+ if (fs.existsSync(resolved.host)) return ERRNO.EEXIST;
1095
+ const parent = dirnameOf(resolved.host);
1096
+ if (!fs.existsSync(parent)) return ERRNO.ENOENT;
1097
+ if (!fs.statSync(parent).isDirectory()) return ERRNO.ENOTDIR;
1098
+ fs.mkdirSync(resolved.host);
1099
+ });
1100
+
1101
+ define('path_filestat_get', 'iiiii', (state, memory, fd, flags, pathPtr, pathLen, bufPtr) => {
1102
+ if (!memory.fits(pathPtr as number, pathLen as number) || !memory.fits(bufPtr as number, SIZE.filestat)) return ERRNO.EOVERFLOW;
1103
+ const descriptor = lookup(state, fd as number, RIGHT.PATH_FILESTAT_GET);
1104
+ if (typeof descriptor === 'number') return descriptor;
1105
+ const resolved = resolve(descriptor, memory.text(pathPtr as number, pathLen as number), flags as number);
1106
+ if (typeof resolved === 'number') return resolved;
1107
+ memory.writeFilestat(bufPtr as number, filestatOf(fs.lstatSync(resolved.host)));
1108
+ });
1109
+
1110
+ define('path_filestat_set_times', 'iiiiLLi', (state, memory, fd, flags, pathPtr, pathLen, atim, mtim, fstFlags) => {
1111
+ if (!memory.fits(pathPtr as number, pathLen as number)) return ERRNO.EOVERFLOW;
1112
+ const descriptor = lookup(state, fd as number, RIGHT.PATH_FILESTAT_SET_TIMES);
1113
+ if (typeof descriptor === 'number') return descriptor;
1114
+ const resolved = resolve(descriptor, memory.text(pathPtr as number, pathLen as number), flags as number);
1115
+ if (typeof resolved === 'number') return resolved;
1116
+ const [atime, mtime] = timesToSet(null, resolved.host, atim as bigint, mtim as bigint, fstFlags as number);
1117
+ return setTimes(resolved.host, atime, mtime);
1118
+ });
1119
+
1120
+ define('path_link', 'iiiiiii', (state, memory, oldFd, _oldFlags, oldPathPtr, oldPathLen, newFd, newPathPtr, newPathLen) => {
1121
+ if (!memory.fits(oldPathPtr as number, oldPathLen as number) || !memory.fits(newPathPtr as number, newPathLen as number)) return ERRNO.EOVERFLOW;
1122
+ const source = lookup(state, oldFd as number, RIGHT.PATH_LINK_SOURCE);
1123
+ if (typeof source === 'number') return source;
1124
+ const target = lookup(state, newFd as number, RIGHT.PATH_LINK_TARGET);
1125
+ if (typeof target === 'number') return target;
1126
+ // The engine's tree keeps no hard links (its fs.link refuses every call).
1127
+ return ERRNO.ENOSYS;
1128
+ });
1129
+
1130
+ define('path_open', 'iiiiiLLii', (state, memory, dirFd, dirFlags, pathPtr, pathLen, oFlags, rightsBase, rightsInheriting, fdFlags, fdPtr) => {
1131
+ if (!memory.fits(pathPtr as number, pathLen as number) || !memory.fits(fdPtr as number, 4)) return ERRNO.EOVERFLOW;
1132
+ const wanted = rightsBase as bigint;
1133
+ const read = (wanted & (RIGHT.FD_READ | RIGHT.FD_READDIR)) !== 0n;
1134
+ const write = (wanted & (RIGHT.FD_DATASYNC | RIGHT.FD_WRITE | RIGHT.FD_ALLOCATE | RIGHT.FD_FILESTAT_SET_SIZE)) !== 0n;
1135
+ let flags = write ? (read ? O_RDWR : O_WRONLY) : O_RDONLY;
1136
+ let neededBase = RIGHT.PATH_OPEN;
1137
+ let neededInheriting = wanted | (rightsInheriting as bigint);
1138
+ if ((oFlags as number) & OFLAG.CREAT) { flags |= O_CREAT; neededBase |= RIGHT.PATH_CREATE_FILE; }
1139
+ if ((oFlags as number) & OFLAG.EXCL) flags |= O_EXCL;
1140
+ if ((oFlags as number) & OFLAG.TRUNC) { flags |= O_TRUNC; neededBase |= RIGHT.PATH_FILESTAT_SET_SIZE; }
1141
+ if ((fdFlags as number) & FDFLAG.APPEND) flags |= O_APPEND;
1142
+ if ((fdFlags as number) & FDFLAG.DSYNC) neededInheriting |= RIGHT.FD_DATASYNC;
1143
+ if ((fdFlags as number) & (FDFLAG.RSYNC | FDFLAG.SYNC)) neededInheriting |= RIGHT.FD_SYNC;
1144
+ if (write && (flags & (O_APPEND | O_TRUNC)) === 0) neededInheriting |= RIGHT.FD_SEEK;
1145
+ const descriptor = lookup(state, dirFd as number, neededBase, neededInheriting);
1146
+ if (typeof descriptor === 'number') return descriptor;
1147
+ const resolved = resolve(descriptor, memory.text(pathPtr as number, pathLen as number), dirFlags as number);
1148
+ if (typeof resolved === 'number') return resolved;
1149
+ // A directory is opened as one: the engine has no descriptor for a
1150
+ // directory, and a guest asks a directory descriptor only for what its
1151
+ // path serves.
1152
+ const exists = fs.existsSync(resolved.host);
1153
+ if (exists && fs.statSync(resolved.host).isDirectory()) {
1154
+ if (write || flags & O_TRUNC) return ERRNO.EISDIR;
1155
+ if (flags & O_EXCL) return ERRNO.EEXIST;
1156
+ const [maxBase, maxInheriting] = rightsFor(FILETYPE.DIRECTORY, flags);
1157
+ const fd = insert(state.table, {
1158
+ kind: 'dir',
1159
+ path: resolved.guest,
1160
+ hostPath: resolved.host,
1161
+ type: FILETYPE.DIRECTORY,
1162
+ rightsBase: wanted & maxBase,
1163
+ rightsInheriting: (rightsInheriting as bigint) & maxInheriting,
1164
+ preopen: false,
1165
+ fdflags: fdFlags as number,
1166
+ position: 0,
1167
+ });
1168
+ memory.setU32(fdPtr as number, fd);
1169
+ return;
1170
+ }
1171
+ if ((oFlags as number) & OFLAG.DIRECTORY) return exists ? ERRNO.ENOTDIR : ERRNO.ENOENT;
1172
+ const hostFd = fs.openSync(resolved.host, flags, 0o666);
1173
+ let opened: Descriptor;
1174
+ try {
1175
+ opened = fileDescriptor(hostFd, resolved.guest, resolved.host, flags, fdFlags as number);
1176
+ } catch (error) {
1177
+ fs.closeSync(hostFd);
1178
+ throw error;
1179
+ }
1180
+ opened.rightsBase = wanted & opened.rightsBase;
1181
+ opened.rightsInheriting = (rightsInheriting as bigint) & opened.rightsInheriting;
1182
+ memory.setU32(fdPtr as number, insert(state.table, opened));
1183
+ });
1184
+
1185
+ define('path_readlink', 'iiiiii', (state, memory, fd, pathPtr, pathLen, bufPtr, bufLen, bufusedPtr) => {
1186
+ if (!memory.fits(pathPtr as number, pathLen as number) || !memory.fits(bufPtr as number, bufLen as number) || !memory.fits(bufusedPtr as number, 4)) return ERRNO.EOVERFLOW;
1187
+ const descriptor = lookup(state, fd as number, RIGHT.PATH_READLINK);
1188
+ if (typeof descriptor === 'number') return descriptor;
1189
+ const resolved = resolve(descriptor, memory.text(pathPtr as number, pathLen as number), 0);
1190
+ if (typeof resolved === 'number') return resolved;
1191
+ if (typeof fs.readlinkSync !== 'function') return ERRNO.ENOSYS;
1192
+ const target = encoder.encode(fs.readlinkSync(resolved.host));
1193
+ if (target.length >= (bufLen as number)) return ERRNO.ENOBUFS;
1194
+ memory.bytes.set(target, bufPtr as number);
1195
+ memory.setU32(bufusedPtr as number, target.length);
1196
+ });
1197
+
1198
+ define('path_remove_directory', 'iii', (state, memory, fd, pathPtr, pathLen) => {
1199
+ if (!memory.fits(pathPtr as number, pathLen as number)) return ERRNO.EOVERFLOW;
1200
+ const descriptor = lookup(state, fd as number, RIGHT.PATH_REMOVE_DIRECTORY);
1201
+ if (typeof descriptor === 'number') return descriptor;
1202
+ const resolved = resolve(descriptor, memory.text(pathPtr as number, pathLen as number), 0);
1203
+ if (typeof resolved === 'number') return resolved;
1204
+ if (!fs.existsSync(resolved.host)) return ERRNO.ENOENT;
1205
+ if (!fs.statSync(resolved.host).isDirectory()) return ERRNO.ENOTDIR;
1206
+ if (fs.readdirSync(resolved.host, { withFileTypes: true }).length > 0) return ERRNO.ENOTEMPTY;
1207
+ fs.rmdirSync(resolved.host);
1208
+ });
1209
+
1210
+ define('path_rename', 'iiiiii', (state, memory, oldFd, oldPathPtr, oldPathLen, newFd, newPathPtr, newPathLen) => {
1211
+ if (!memory.fits(oldPathPtr as number, oldPathLen as number) || !memory.fits(newPathPtr as number, newPathLen as number)) return ERRNO.EOVERFLOW;
1212
+ const source = lookup(state, oldFd as number, RIGHT.PATH_RENAME_SOURCE);
1213
+ if (typeof source === 'number') return source;
1214
+ const target = lookup(state, newFd as number, RIGHT.PATH_RENAME_TARGET);
1215
+ if (typeof target === 'number') return target;
1216
+ const from = resolve(source, memory.text(oldPathPtr as number, oldPathLen as number), 0);
1217
+ if (typeof from === 'number') return from;
1218
+ const to = resolve(target, memory.text(newPathPtr as number, newPathLen as number), 0);
1219
+ if (typeof to === 'number') return to;
1220
+ fs.renameSync(from.host, to.host);
1221
+ });
1222
+
1223
+ define('path_symlink', 'iiiii', (state, memory, oldPathPtr, oldPathLen, fd, newPathPtr, newPathLen) => {
1224
+ if (!memory.fits(oldPathPtr as number, oldPathLen as number) || !memory.fits(newPathPtr as number, newPathLen as number)) return ERRNO.EOVERFLOW;
1225
+ const descriptor = lookup(state, fd as number, RIGHT.PATH_SYMLINK);
1226
+ if (typeof descriptor === 'number') return descriptor;
1227
+ const resolved = resolve(descriptor, memory.text(newPathPtr as number, newPathLen as number), 0);
1228
+ if (typeof resolved === 'number') return resolved;
1229
+ if (typeof fs.symlinkSync !== 'function') return ERRNO.ENOSYS;
1230
+ fs.symlinkSync(memory.text(oldPathPtr as number, oldPathLen as number), resolved.host);
1231
+ });
1232
+
1233
+ define('path_unlink_file', 'iii', (state, memory, fd, pathPtr, pathLen) => {
1234
+ if (!memory.fits(pathPtr as number, pathLen as number)) return ERRNO.EOVERFLOW;
1235
+ const descriptor = lookup(state, fd as number, RIGHT.PATH_UNLINK_FILE);
1236
+ if (typeof descriptor === 'number') return descriptor;
1237
+ const resolved = resolve(descriptor, memory.text(pathPtr as number, pathLen as number), 0);
1238
+ if (typeof resolved === 'number') return resolved;
1239
+ if (!fs.existsSync(resolved.host)) return ERRNO.ENOENT;
1240
+ if (fs.lstatSync(resolved.host).isDirectory()) return ERRNO.EISDIR;
1241
+ fs.unlinkSync(resolved.host);
1242
+ });
1243
+
1244
+ define('poll_oneoff', 'iiii', (state, memory, inPtr, outPtr, nsubscriptions, neventsPtr) => {
1245
+ if (!memory.fits(inPtr as number, (nsubscriptions as number) * SIZE.subscription) || !memory.fits(outPtr as number, (nsubscriptions as number) * SIZE.event) || !memory.fits(neventsPtr as number, 4)) return ERRNO.EOVERFLOW;
1246
+ if ((nsubscriptions as number) === 0) return ERRNO.EINVAL;
1247
+ let timeout: bigint | undefined;
1248
+ let timerUserdata = 0n;
1249
+ const ready: Array<{ userdata: bigint; error: number; type: number; hangup: boolean }> = [];
1250
+ for (let index = 0; index < (nsubscriptions as number); index += 1) {
1251
+ const at = (inPtr as number) + index * SIZE.subscription;
1252
+ const userdata = memory.u64(at);
1253
+ const type = memory.u8(at + 8);
1254
+ if (type === EVENTTYPE.CLOCK) {
1255
+ const clockTimeout = memory.u64(at + 24);
1256
+ const flags = memory.u16(at + 40);
1257
+ const relative = flags === SUBSCRIPTION_CLOCK_ABSTIME ? clockTimeout - realtimeNanos() : clockTimeout;
1258
+ if (timeout === undefined || relative < timeout) {
1259
+ timeout = relative;
1260
+ timerUserdata = userdata;
1261
+ }
1262
+ } else if (type === EVENTTYPE.FD_READ || type === EVENTTYPE.FD_WRITE) {
1263
+ const descriptor = lookup(state, memory.u32(at + 16), RIGHT.POLL_FD_READWRITE);
1264
+ if (typeof descriptor === 'number') {
1265
+ ready.push({ userdata, error: descriptor, type, hangup: false });
1266
+ continue;
1267
+ }
1268
+ // The engine's files and directories never block; its stdout and
1269
+ // stderr always take a write; its stdin is readable when its stream
1270
+ // has yielded bytes and hung up when it has nothing more to give.
1271
+ if (descriptor.kind !== 'stream') ready.push({ userdata, error: 0, type, hangup: false });
1272
+ else if (descriptor.stream !== 'stdin') { if (type === EVENTTYPE.FD_WRITE) ready.push({ userdata, error: 0, type, hangup: false }); }
1273
+ else if (type === EVENTTYPE.FD_READ) {
1274
+ if (state.stdinPending.length === 0 && !state.stdinEnded) readStdin(state, new Uint8Array(0));
1275
+ ready.push({ userdata, error: 0, type, hangup: state.stdinPending.length === 0 });
1276
+ }
1277
+ } else {
1278
+ return ERRNO.EINVAL;
1279
+ }
1280
+ }
1281
+ let nevents = 0;
1282
+ if (ready.length === 0) {
1283
+ if (timeout !== undefined) sleepNanos(timeout);
1284
+ memory.setU64(outPtr as number, timerUserdata);
1285
+ memory.setU16((outPtr as number) + 8, ERRNO.ESUCCESS);
1286
+ memory.setU8((outPtr as number) + 10, EVENTTYPE.CLOCK);
1287
+ nevents = 1;
1288
+ } else {
1289
+ for (const event of ready) {
1290
+ const at = (outPtr as number) + nevents * SIZE.event;
1291
+ memory.setU64(at, event.userdata);
1292
+ memory.setU16(at + 8, event.error);
1293
+ memory.setU8(at + 10, event.type);
1294
+ memory.setU64(at + 16, 0n);
1295
+ memory.setU16(at + 24, event.hangup ? EVENT_FD_READWRITE_HANGUP : 0);
1296
+ nevents += 1;
1297
+ }
1298
+ }
1299
+ memory.setU32(neventsPtr as number, nevents);
1300
+ });
1301
+
1302
+ define('proc_exit', 'i', (_state, _memory, code) => {
1303
+ // uvwasi's proc_exit ends the process there and then; the guest's
1304
+ // process.exit is that door here. Node's lib replaces this syscall when
1305
+ // the caller asked for the exit code back (returnOnExit).
1306
+ process.exit(code as number);
1307
+ });
1308
+
1309
+ define('proc_raise', 'i', (_state, _memory, signal) => {
1310
+ const name = SIGNAL_NAMES[signal as number];
1311
+ if (name === undefined) return ERRNO.ENOSYS;
1312
+ try {
1313
+ process.kill(process.pid, name);
1314
+ } catch (error) {
1315
+ if ((error as { code?: string } | null)?.code === 'ERR_UNKNOWN_SIGNAL') return ERRNO.ENOSYS;
1316
+ throw error;
1317
+ }
1318
+ });
1319
+
1320
+ define('random_get', 'ii', (_state, memory, bufPtr, bufLen) => {
1321
+ if (!memory.fits(bufPtr as number, bufLen as number)) return ERRNO.EOVERFLOW;
1322
+ const target = memory.bytes.subarray(bufPtr as number, (bufPtr as number) + (bufLen as number));
1323
+ // getRandomValues fills at most 65536 bytes per call, and never a shared buffer.
1324
+ for (let at = 0; at < target.length; at += 65536) {
1325
+ const chunk = target.subarray(at, Math.min(at + 65536, target.length));
1326
+ if (memory.shared) chunk.set(crypto.getRandomValues(new Uint8Array(chunk.length)));
1327
+ else crypto.getRandomValues(chunk);
1328
+ }
1329
+ });
1330
+
1331
+ define('sched_yield', '', () => {});
1332
+
1333
+ // Sockets: the engine's guests have no WASI sockets, so a descriptor that is
1334
+ // there and has the right is not a socket, as uvwasi answers a plain file.
1335
+ define('sock_accept', 'iii', (state, memory, fd, _flags, fdPtr) => {
1336
+ if (!memory.fits(fdPtr as number, 4)) return ERRNO.EOVERFLOW;
1337
+ const descriptor = lookup(state, fd as number, RIGHT.SOCK_ACCEPT);
1338
+ if (typeof descriptor === 'number') return descriptor;
1339
+ return ERRNO.ENOTSOCK;
1340
+ });
1341
+
1342
+ define('sock_recv', 'iiiiii', (state, memory, fd, riDataPtr, riDataLen, _riFlags, roDataLenPtr, roFlagsPtr) => {
1343
+ if (!memory.fits(roDataLenPtr as number, 4) || !memory.fits(roFlagsPtr as number, 4)) return ERRNO.EOVERFLOW;
1344
+ const iovs = iovecsOf(memory, riDataPtr as number, riDataLen as number);
1345
+ if (typeof iovs === 'number') return iovs;
1346
+ const descriptor = lookup(state, fd as number, RIGHT.FD_READ);
1347
+ if (typeof descriptor === 'number') return descriptor;
1348
+ return ERRNO.ENOTSOCK;
1349
+ });
1350
+
1351
+ define('sock_send', 'iiiii', (state, memory, fd, siDataPtr, siDataLen, _siFlags, soDataLenPtr) => {
1352
+ if (!memory.fits(soDataLenPtr as number, 4)) return ERRNO.EOVERFLOW;
1353
+ const iovs = iovecsOf(memory, siDataPtr as number, siDataLen as number);
1354
+ if (typeof iovs === 'number') return iovs;
1355
+ const descriptor = lookup(state, fd as number, RIGHT.FD_WRITE);
1356
+ if (typeof descriptor === 'number') return descriptor;
1357
+ return ERRNO.ENOTSOCK;
1358
+ });
1359
+
1360
+ define('sock_shutdown', 'ii', (state, _memory, fd, _how) => {
1361
+ const descriptor = lookup(state, fd as number, RIGHT.SOCK_SHUTDOWN);
1362
+ if (typeof descriptor === 'number') return descriptor;
1363
+ return ERRNO.ENOTSOCK;
1364
+ });
1365
+
1366
+ return WASI as unknown as BindingClass;
1367
+ }
1368
+
1369
+ // ---------------------------------------------------------------------------
1370
+ // primordials — the seven names wasi.js destructures, written literally over
1371
+ // the built-ins, captured at module load as Node captures its own.
1372
+ // ---------------------------------------------------------------------------
1373
+
1374
+ const uncurry = <A extends unknown[], R>(fn: (...args: A) => R) =>
1375
+ Function.prototype.call.bind(fn) as unknown as (self: unknown, ...args: A) => R;
1376
+
1377
+ const primordials = {
1378
+ ArrayPrototypeForEach: uncurry(Array.prototype.forEach),
1379
+ ArrayPrototypeMap: uncurry(Array.prototype.map),
1380
+ ArrayPrototypePush: uncurry(Array.prototype.push),
1381
+ FunctionPrototypeBind: uncurry(Function.prototype.bind),
1382
+ ObjectEntries: Object.entries,
1383
+ String,
1384
+ Symbol,
1385
+ };
1386
+
1387
+ // ---------------------------------------------------------------------------
1388
+ // The one evaluation of Node's file, per guest: its binding is the guest's fs
1389
+ // and process, so `require('wasi')` in two programs answers two modules.
1390
+ // ---------------------------------------------------------------------------
1391
+
1392
+ /** The `wasi` module as Node's `lib/wasi.js` exports it. */
1393
+ export interface WasiModule {
1394
+ WASI: new (options?: unknown) => {
1395
+ wasiImport: Record<string, unknown>;
1396
+ start(instance: unknown): number;
1397
+ initialize(instance: unknown): void;
1398
+ getImportObject(): Record<string, Record<string, unknown>>;
1399
+ };
1400
+ }
1401
+
1402
+ const experimentalWarnings = new WeakMap<WasiHostProcess, Set<string>>();
1403
+
1404
+ export function createWasiModule(fs: WasiHostFs, process: WasiHostProcess): WasiModule {
1405
+ const binding = { WASI: bindingClassFor(fs, process) };
1406
+ // internal/util's emitExperimentalWarning: once per feature per process.
1407
+ const emitExperimentalWarning = (feature: string): void => {
1408
+ let warned = experimentalWarnings.get(process);
1409
+ if (warned === undefined) experimentalWarnings.set(process, (warned = new Set()));
1410
+ if (warned.has(feature)) return;
1411
+ warned.add(feature);
1412
+ process.emitWarning(`${feature} is an experimental feature and might change at any time`, 'ExperimentalWarning');
1413
+ };
1414
+ const nodeInternalRequire = (specifier: string): unknown => {
1415
+ switch (specifier) {
1416
+ case 'internal/errors':
1417
+ return { codes: { ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_WASI_ALREADY_STARTED } };
1418
+ case 'internal/util':
1419
+ return { emitExperimentalWarning, kEmptyObject: Object.freeze(Object.create(null)) };
1420
+ case 'internal/validators':
1421
+ return { validateArray, validateBoolean, validateFunction, validateInt32, validateObject, validateString, validateUndefined };
1422
+ default:
1423
+ throw new Error(`node-lib/wasi.js asked for an internal the binding does not provide: ${specifier}`);
1424
+ }
1425
+ };
1426
+ const internalBinding = (name: string): unknown => {
1427
+ if (name === 'wasi') return binding;
1428
+ throw new Error(`node-lib/wasi.js asked for a binding the engine does not provide: ${name}`);
1429
+ };
1430
+ const module: { exports: unknown } = { exports: {} };
1431
+ // eslint-disable-next-line no-new-func
1432
+ new Function('primordials', 'require', 'internalBinding', 'module', 'exports', NODE_WASI_SOURCE)(
1433
+ primordials,
1434
+ nodeInternalRequire,
1435
+ internalBinding,
1436
+ module,
1437
+ module.exports,
1438
+ );
1439
+ return module.exports as WasiModule;
1440
+ }