funicular 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -0
  3. data/demo/test_chartjs.html +9 -9
  4. data/demo/test_component.html +8 -8
  5. data/demo/test_error_boundary.html +44 -41
  6. data/demo/test_router.html +48 -48
  7. data/demo/tic-tac-toe.html +25 -25
  8. data/docs/architecture.md +46 -9
  9. data/lib/funicular/ssr/runtime.rb +1 -0
  10. data/lib/funicular/vendor/mrbc/VERSION +1 -1
  11. data/lib/funicular/vendor/mrbc/mrbc.js +613 -486
  12. data/lib/funicular/vendor/mrbc/mrbc.wasm +0 -0
  13. data/lib/funicular/vendor/picoruby/VERSION +1 -1
  14. data/lib/funicular/vendor/picoruby/debug/picoruby.js +669 -449
  15. data/lib/funicular/vendor/picoruby/debug/picoruby.wasm +0 -0
  16. data/lib/funicular/vendor/picoruby/dist/picoruby.js +2 -2
  17. data/lib/funicular/vendor/picoruby/dist/picoruby.wasm +0 -0
  18. data/lib/funicular/vendor/picoruby-test-node/VERSION +1 -1
  19. data/lib/funicular/vendor/picoruby-test-node/picoruby.js +921 -629
  20. data/lib/funicular/vendor/picoruby-test-node/picoruby.wasm +0 -0
  21. data/lib/funicular/vendor/picoruby-test-node/picoruby.wasm.map +1 -1
  22. data/lib/funicular/version.rb +1 -1
  23. data/lib/generators/funicular/chat/templates/funicular_chat_component.rb.tt +37 -38
  24. data/minitest/dsl_test.rb +237 -0
  25. data/minitest/fixtures/funicular_app/components/greeting_component.rb +5 -5
  26. data/minitest/form_for_test.rb +2 -2
  27. data/minitest/hydration_test.rb +2 -2
  28. data/minitest/sig_tags_test.rb +30 -0
  29. data/minitest/view_context_test.rb +15 -15
  30. data/mrblib/0_tags.rb +62 -0
  31. data/mrblib/component.rb +113 -23
  32. data/mrblib/error_boundary.rb +25 -19
  33. data/mrblib/form_builder.rb +10 -10
  34. data/mrblib/funicular.rb +1 -1
  35. data/mrblib/styles.rb +102 -12
  36. data/mrblib/view_context.rb +3 -32
  37. data/sig/component.rbs +18 -4
  38. data/sig/error_boundary.rbs +4 -4
  39. data/sig/styles.rbs +18 -5
  40. data/sig/tags.rbs +54 -0
  41. data/sig/view_context.rbs +47 -34
  42. metadata +5 -1
@@ -17,9 +17,14 @@
17
17
 
18
18
  // Note: We use a typeof check here instead of optional chaining using
19
19
  // globalThis because older browsers might not have globalThis defined.
20
- var currentNodeVersion = typeof process !== 'undefined' && process.versions?.node ? humanReadableVersionToPacked(process.versions.node) : TARGET_NOT_SUPPORTED;
21
- if (currentNodeVersion < 160000) {
22
- throw new Error(`This emscripten-generated code requires node v${ packedVersionToHumanReadable(160000) } (detected v${packedVersionToHumanReadable(currentNodeVersion)})`);
20
+
21
+ // We skip the node version checking when running on Bun/Deno since the node
22
+ // version they report doesn't seem to be useful.
23
+ if (typeof process !== 'undefined' && !process.versions?.bun && typeof Deno == "undefined") {
24
+ var currentNodeVersion = process.versions?.node ? humanReadableVersionToPacked(process.versions.node) : TARGET_NOT_SUPPORTED;
25
+ if (currentNodeVersion < 180300) {
26
+ throw new Error(`This emscripten-generated code requires node v${ packedVersionToHumanReadable(180300) } (detected v${packedVersionToHumanReadable(currentNodeVersion)})`);
27
+ }
23
28
  }
24
29
 
25
30
  var userAgent = typeof navigator !== 'undefined' && navigator.userAgent;
@@ -74,7 +79,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR
74
79
  // refer to Module (if they choose; they can also define Module)
75
80
 
76
81
 
77
- var arguments_ = [];
82
+ var programArgs = [];
78
83
  var thisProgram = './this.program';
79
84
  var quit_ = (status, toThrow) => {
80
85
  throw toThrow;
@@ -134,7 +139,7 @@ readAsync = async (filename, binary = true) => {
134
139
  thisProgram = process.argv[1].replace(/\\/g, '/');
135
140
  }
136
141
 
137
- arguments_ = process.argv.slice(2);
142
+ programArgs = process.argv.slice(2);
138
143
 
139
144
  // MODULARIZE will export the module in the proper place outside, we don't need to export here
140
145
  if (typeof module != 'undefined') {
@@ -224,7 +229,7 @@ var OPFS = 'OPFS is no longer included by default; build with -lopfs.js';
224
229
  // perform assertions in shell.js after we set up out() and err(), as otherwise
225
230
  // if an assertion fails it cannot print the message
226
231
 
227
- assert(!ENVIRONMENT_IS_SHELL, 'shell environment detected but not enabled at build time. Add `shell` to `-sENVIRONMENT` to enable.');
232
+ assert(!ENVIRONMENT_IS_SHELL, 'shell environment detected but not enabled at build time (add `shell` to `-sENVIRONMENT` to enable)');
228
233
 
229
234
  // end include: shell.js
230
235
 
@@ -288,45 +293,12 @@ function _free() {
288
293
  var isFileURI = (filename) => filename.startsWith('file://');
289
294
 
290
295
  // include: runtime_common.js
291
- // include: runtime_stack_check.js
292
- // Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode.
293
- function writeStackCookie() {
294
- var max = _emscripten_stack_get_end();
295
- assert((max & 3) == 0);
296
- // If the stack ends at address zero we write our cookies 4 bytes into the
297
- // stack. This prevents interference with SAFE_HEAP and ASAN which also
298
- // monitor writes to address zero.
299
- if (max == 0) {
300
- max += 4;
301
- }
302
- // The stack grow downwards towards _emscripten_stack_get_end.
303
- // We write cookies to the final two words in the stack and detect if they are
304
- // ever overwritten.
305
- HEAPU32[((max)>>2)] = 0x02135467;
306
- HEAPU32[(((max)+(4))>>2)] = 0x89BACDFE;
307
- // Also test the global address 0 for integrity.
308
- HEAPU32[((0)>>2)] = 1668509029;
309
- }
310
-
311
- function checkStackCookie() {
312
- if (ABORT) return;
313
- var max = _emscripten_stack_get_end();
314
- // See writeStackCookie().
315
- if (max == 0) {
316
- max += 4;
317
- }
318
- var cookie1 = HEAPU32[((max)>>2)];
319
- var cookie2 = HEAPU32[(((max)+(4))>>2)];
320
- if (cookie1 != 0x02135467 || cookie2 != 0x89BACDFE) {
321
- abort(`Stack overflow! Stack cookie has been overwritten at ${ptrToString(max)}, expected hex dwords 0x89BACDFE and 0x2135467, but received ${ptrToString(cookie2)} ${ptrToString(cookie1)}`);
322
- }
323
- // Also test the global address 0 for integrity.
324
- if (HEAPU32[((0)>>2)] != 0x63736d65 /* 'emsc' */) {
325
- abort('Runtime error: The application has corrupted its heap memory area (address zero)!');
326
- }
327
- }
328
- // end include: runtime_stack_check.js
329
296
  // include: runtime_exceptions.js
297
+ // Base Emscripten EH error class
298
+ class EmscriptenEH {}
299
+
300
+ class EmscriptenSjLj extends EmscriptenEH {}
301
+
330
302
  // end include: runtime_exceptions.js
331
303
  // include: runtime_debug.js
332
304
  var runtimeDebug = true; // Switch to false at runtime to disable logging at the right times
@@ -348,15 +320,31 @@ function dbg(...args) {
348
320
  })();
349
321
 
350
322
  function consumedModuleProp(prop) {
351
- if (!Object.getOwnPropertyDescriptor(Module, prop)) {
352
- Object.defineProperty(Module, prop, {
353
- configurable: true,
354
- set() {
355
- abort(`Attempt to set \`Module.${prop}\` after it has already been processed. This can happen, for example, when code is injected via '--post-js' rather than '--pre-js'`);
356
-
323
+ var value = Module[prop];
324
+ var msg = `Attempt to modify \`Module.${prop}\` after it has already been processed. This can happen, for example, when code is injected via '--post-js' rather than '--pre-js'`;
325
+ if (Array.isArray(value)) {
326
+ value = new Proxy(value, {
327
+ set(target, key, val) {
328
+ abort(msg);
329
+ return false;
330
+ },
331
+ defineProperty(target, key, descriptor) {
332
+ abort(msg);
333
+ return false;
334
+ },
335
+ deleteProperty(target, key) {
336
+ abort(msg);
337
+ return false;
357
338
  }
358
339
  });
359
340
  }
341
+ Object.defineProperty(Module, prop, {
342
+ configurable: true,
343
+ get() { return value; },
344
+ set() {
345
+ abort(msg);
346
+ }
347
+ });
360
348
  }
361
349
 
362
350
  function makeInvalidEarlyAccess(name) {
@@ -453,32 +441,52 @@ function unexportedRuntimeSymbol(sym) {
453
441
  }
454
442
 
455
443
  // end include: runtime_debug.js
444
+ // include: runtime_stack_check.js
445
+ const stackCookie1 = 0x02135467;
446
+ const stackCookie2 = 0x89BACDFE;
447
+
448
+ // Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode.
449
+ function writeStackCookie() {
450
+ var max = _emscripten_stack_get_end();
451
+ assert((max & 3) == 0);
452
+ // If the stack ends at address zero we write our cookies 4 bytes into the
453
+ // stack. This prevents interference with SAFE_HEAP and ASAN which also
454
+ // monitor writes to address zero.
455
+ if (max == 0) {
456
+ max += 4;
457
+ }
458
+ // The stack grow downwards towards _emscripten_stack_get_end.
459
+ // We write cookies to the final two words in the stack and detect if they are
460
+ // ever overwritten.
461
+ HEAPU32[((max)>>2)] = stackCookie1;
462
+ HEAPU32[(((max)+(4))>>2)] = stackCookie2;
463
+ // Also test the global address 0 for integrity.
464
+ HEAPU32[((0)>>2)] = 1668509029;
465
+ }
466
+
467
+ function u32ToHexString(num) {
468
+ return '0x' + (num >>> 0).toString(16).padStart(8, '0');
469
+ }
470
+
471
+ function checkStackCookie() {
472
+ if (ABORT) return;
473
+ var max = _emscripten_stack_get_end();
474
+ // See writeStackCookie().
475
+ if (max == 0) {
476
+ max += 4;
477
+ }
478
+ var val1 = HEAPU32[((max)>>2)];
479
+ var val2 = HEAPU32[(((max)+(4))>>2)];
480
+ if (val1 != stackCookie1 || val2 != stackCookie2) {
481
+ abort(`Stack overflow! Stack cookie has been overwritten at ${ptrToString(max)}, expected hex dwords ${u32ToHexString(stackCookie2)} and ${u32ToHexString(stackCookie1)}, but received ${u32ToHexString(val2)} ${u32ToHexString(val1)}`);
482
+ }
483
+ // Also test the global address 0 for integrity.
484
+ if (HEAPU32[((0)>>2)] != 0x63736d65 /* 'emsc' */) {
485
+ abort('Runtime error: The application has corrupted its heap memory area (address zero)!');
486
+ }
487
+ }
488
+ // end include: runtime_stack_check.js
456
489
  // Memory management
457
- var
458
- /** @type {!Int8Array} */
459
- HEAP8,
460
- /** @type {!Uint8Array} */
461
- HEAPU8,
462
- /** @type {!Int16Array} */
463
- HEAP16,
464
- /** @type {!Uint16Array} */
465
- HEAPU16,
466
- /** @type {!Int32Array} */
467
- HEAP32,
468
- /** @type {!Uint32Array} */
469
- HEAPU32,
470
- /** @type {!Float32Array} */
471
- HEAPF32,
472
- /** @type {!Float64Array} */
473
- HEAPF64;
474
-
475
- // BigInt64Array type is not correctly defined in closure
476
- var
477
- /** not-@type {!BigInt64Array} */
478
- HEAP64,
479
- /* BigUint64Array type is not correctly defined in closure
480
- /** not-@type {!BigUint64Array} */
481
- HEAPU64;
482
490
 
483
491
  var runtimeInitialized = false;
484
492
 
@@ -486,8 +494,17 @@ var runtimeExited = false;
486
494
 
487
495
 
488
496
 
497
+ // When ALLOW_MEMORY_GROWTH is enabled, the conversion from Wasm
498
+ // memory to ArrayBuffer requires some additional logic.
499
+ function getMemoryBuffer() {
500
+ return wasmMemory.buffer;
501
+ }
502
+
489
503
  function updateMemoryViews() {
490
- var b = wasmMemory.buffer;
504
+ // If we already have a heap that is resizeable/growable buffer we don't
505
+ // need to do anything in updateMemoryViews.
506
+ if (HEAP8?.buffer?.resizable) return;
507
+ var b = getMemoryBuffer();
491
508
  HEAP8 = new Int8Array(b);
492
509
  HEAP16 = new Int16Array(b);
493
510
  HEAPU8 = new Uint8Array(b);
@@ -507,11 +524,10 @@ assert(globalThis.Int32Array && globalThis.Float64Array && Int32Array.prototype.
507
524
  'JS engine does not provide full typed array support');
508
525
 
509
526
  function preRun() {
510
- if (Module['preRun']) {
511
- if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
512
- while (Module['preRun'].length) {
513
- addOnPreRun(Module['preRun'].shift());
514
- }
527
+ var preRun = Module['preRun'];
528
+ if (preRun) {
529
+ if (typeof preRun == 'function') preRun = [preRun];
530
+ onPreRuns.push(...preRun);
515
531
  }
516
532
  consumedModuleProp('preRun');
517
533
  // Begin ATPRERUNS hooks
@@ -535,17 +551,17 @@ TTY.init();
535
551
  // Begin ATPOSTCTORS hooks
536
552
  FS.ignorePermissions = false;
537
553
  // End ATPOSTCTORS hooks
538
- }
539
554
 
540
- function preMain() {
541
555
  checkStackCookie();
542
- // No ATMAINS hooks
543
556
  }
544
557
 
558
+ var runtimeExiting = false;
559
+
545
560
  function exitRuntime() {
546
561
  assert(!runtimeExited);
562
+ assert(!runtimeExiting, 'Re-entrant call to exitRuntime()! This can happen if an atexit() registered callback throws an exception.');
563
+ runtimeExiting = true;
547
564
  checkStackCookie();
548
- // PThreads reuse the runtime from the main thread.
549
565
  ___funcs_on_exit(); // Native atexit() functions
550
566
  // Begin ATEXITS hooks
551
567
  FS.quit();
@@ -556,13 +572,11 @@ TTY.shutdown();
556
572
 
557
573
  function postRun() {
558
574
  checkStackCookie();
559
- // PThreads reuse the runtime from the main thread.
560
575
 
561
- if (Module['postRun']) {
562
- if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
563
- while (Module['postRun'].length) {
564
- addOnPostRun(Module['postRun'].shift());
565
- }
576
+ var postRun = Module['postRun'];
577
+ if (postRun) {
578
+ if (typeof postRun == 'function') postRun = [postRun];
579
+ onPostRuns.push(...postRun);
566
580
  }
567
581
  consumedModuleProp('postRun');
568
582
 
@@ -571,11 +585,13 @@ function postRun() {
571
585
  // End ATPOSTRUNS hooks
572
586
  }
573
587
 
574
- /** @param {string|number=} what */
588
+ /**
589
+ * @param {string|number=} what
590
+ */
575
591
  function abort(what) {
576
592
  Module['onAbort']?.(what);
577
593
 
578
- what = 'Aborted(' + what + ')';
594
+ what = `Aborted(${what})`;
579
595
  // TODO(sbc): Should we remove printing and leave it up to whoever
580
596
  // catches the exception?
581
597
  err(what);
@@ -604,15 +620,14 @@ function abort(what) {
604
620
  throw e;
605
621
  }
606
622
 
607
- function createExportWrapper(name, nargs) {
623
+ function createExportWrapper(name, func, nargs) {
624
+ assert(func);
608
625
  return (...args) => {
609
626
  assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`);
610
627
  assert(!runtimeExited, `native function \`${name}\` called after runtime exit (use NO_EXIT_RUNTIME to keep it alive after main() exits)`);
611
- var f = wasmExports[name];
612
- assert(f, `exported native function \`${name}\` not found`);
613
628
  // Only assert for too many arguments. Too few can be valid since the missing arguments will be zero filled.
614
629
  assert(args.length <= nargs, `native function \`${name}\` called with ${args.length} args but expects ${nargs}`);
615
- return f(...args);
630
+ return func(...args);
616
631
  };
617
632
  }
618
633
 
@@ -623,9 +638,6 @@ function findWasmBinary() {
623
638
  }
624
639
 
625
640
  function getBinarySync(file) {
626
- if (file == wasmBinaryFile && wasmBinary) {
627
- return new Uint8Array(wasmBinary);
628
- }
629
641
  if (readBinary) {
630
642
  return readBinary(file);
631
643
  }
@@ -708,18 +720,15 @@ async function createWasm() {
708
720
  // Load the wasm module and create an instance of using native support in the JS engine.
709
721
  // handle a generated wasm instance, receiving its exports and
710
722
  // performing other necessary setup
711
- /** @param {WebAssembly.Module=} module*/
712
- function receiveInstance(instance, module) {
723
+ function receiveInstance(instance) {
713
724
  wasmExports = instance.exports;
714
725
 
715
726
  assignWasmExports(wasmExports);
716
727
 
717
728
  updateMemoryViews();
718
729
 
719
- removeRunDependency('wasm-instantiate');
720
730
  return wasmExports;
721
731
  }
722
- addRunDependency('wasm-instantiate');
723
732
 
724
733
  // Prefer streaming instantiation if available.
725
734
  // Async compilation can be confusing when an error on the page overwrites Module
@@ -744,15 +753,14 @@ async function createWasm() {
744
753
  // performing.
745
754
  // Also pthreads and wasm workers initialize the wasm instance through this
746
755
  // path.
747
- if (Module['instantiateWasm']) {
748
- return new Promise((resolve, reject) => {
756
+ var instantiateWasm = Module['instantiateWasm'];
757
+ if (instantiateWasm) {
758
+ return new Promise((resolve) => {
749
759
  try {
750
- Module['instantiateWasm'](info, (inst, mod) => {
751
- resolve(receiveInstance(inst, mod));
752
- });
760
+ instantiateWasm(info, (inst) => resolve(receiveInstance(inst)));
753
761
  } catch(e) {
754
762
  err(`Module.instantiateWasm callback failed with error: ${e}`);
755
- reject(e);
763
+ throw e;
756
764
  }
757
765
  });
758
766
  }
@@ -776,6 +784,36 @@ async function createWasm() {
776
784
  }
777
785
  }
778
786
 
787
+ /** @type {!Int16Array} */
788
+ var HEAP16;
789
+
790
+ /** @type {!Int32Array} */
791
+ var HEAP32;
792
+
793
+ /** not-@type {!BigInt64Array} */
794
+ var HEAP64;
795
+
796
+ /** @type {!Int8Array} */
797
+ var HEAP8;
798
+
799
+ /** @type {!Float32Array} */
800
+ var HEAPF32;
801
+
802
+ /** @type {!Float64Array} */
803
+ var HEAPF64;
804
+
805
+ /** @type {!Uint16Array} */
806
+ var HEAPU16;
807
+
808
+ /** @type {!Uint32Array} */
809
+ var HEAPU32;
810
+
811
+ /** not-@type {!BigUint64Array} */
812
+ var HEAPU64;
813
+
814
+ /** @type {!Uint8Array} */
815
+ var HEAPU8;
816
+
779
817
  var callRuntimeCallbacks = (callbacks) => {
780
818
  while (callbacks.length > 0) {
781
819
  // Pass the module as the first argument.
@@ -788,71 +826,6 @@ async function createWasm() {
788
826
  var onPreRuns = [];
789
827
  var addOnPreRun = (cb) => onPreRuns.push(cb);
790
828
 
791
- var runDependencies = 0;
792
-
793
-
794
- var dependenciesFulfilled = null;
795
-
796
- var runDependencyTracking = {
797
- };
798
-
799
- var runDependencyWatcher = null;
800
- var removeRunDependency = (id) => {
801
- runDependencies--;
802
-
803
- Module['monitorRunDependencies']?.(runDependencies);
804
-
805
- assert(id, 'removeRunDependency requires an ID');
806
- assert(runDependencyTracking[id]);
807
- delete runDependencyTracking[id];
808
- if (runDependencies == 0) {
809
- if (runDependencyWatcher !== null) {
810
- clearInterval(runDependencyWatcher);
811
- runDependencyWatcher = null;
812
- }
813
- if (dependenciesFulfilled) {
814
- var callback = dependenciesFulfilled;
815
- dependenciesFulfilled = null;
816
- callback(); // can add another dependenciesFulfilled
817
- }
818
- }
819
- };
820
-
821
-
822
- var addRunDependency = (id) => {
823
- runDependencies++;
824
-
825
- Module['monitorRunDependencies']?.(runDependencies);
826
-
827
- assert(id, 'addRunDependency requires an ID')
828
- assert(!runDependencyTracking[id]);
829
- runDependencyTracking[id] = 1;
830
- if (runDependencyWatcher === null && globalThis.setInterval) {
831
- // Check for missing dependencies every few seconds
832
- runDependencyWatcher = setInterval(() => {
833
- if (ABORT) {
834
- clearInterval(runDependencyWatcher);
835
- runDependencyWatcher = null;
836
- return;
837
- }
838
- var shown = false;
839
- for (var dep in runDependencyTracking) {
840
- if (!shown) {
841
- shown = true;
842
- err('still waiting on run dependencies:');
843
- }
844
- err(`dependency: ${dep}`);
845
- }
846
- if (shown) {
847
- err('(end of list)');
848
- }
849
- }, 10000);
850
- // Prevent this timer from keeping the runtime alive if nothing
851
- // else is.
852
- runDependencyWatcher.unref?.()
853
- }
854
- };
855
-
856
829
 
857
830
 
858
831
  /**
@@ -876,13 +849,12 @@ async function createWasm() {
876
849
 
877
850
  var noExitRuntime = false;
878
851
 
879
- var ptrToString = (ptr) => {
852
+ function ptrToString(ptr) {
880
853
  assert(typeof ptr === 'number', `ptrToString expects a number, got ${typeof ptr}`);
881
854
  // Convert to 32-bit unsigned value
882
855
  ptr >>>= 0;
883
856
  return '0x' + ptr.toString(16).padStart(8, '0');
884
- };
885
-
857
+ }
886
858
 
887
859
 
888
860
  /**
@@ -922,6 +894,14 @@ async function createWasm() {
922
894
 
923
895
  var UTF8Decoder = globalThis.TextDecoder && new TextDecoder();
924
896
 
897
+
898
+ /**
899
+ * heapOrArray is either a regular array, or a JavaScript typed array view.
900
+ * @param {number} idx
901
+ * @param {number=} maxBytesToRead
902
+ * @param {boolean=} ignoreNul
903
+ * @return {number}
904
+ */
925
905
  var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => {
926
906
  var maxIdx = idx + maxBytesToRead;
927
907
  if (ignoreNul) return maxIdx;
@@ -966,7 +946,7 @@ async function createWasm() {
966
946
  if ((u0 & 0xF0) == 0xE0) {
967
947
  u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
968
948
  } else {
969
- if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!');
949
+ if ((u0 & 0xF8) != 0xF0) warnOnce(`Invalid UTF-8 leading byte ${ptrToString(u0)} encountered when deserializing a UTF-8 string in wasm memory to a JS string!`);
970
950
  u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63);
971
951
  }
972
952
 
@@ -1024,15 +1004,12 @@ async function createWasm() {
1024
1004
  // This block is not needed on v19+ since crypto.getRandomValues is builtin
1025
1005
  if (ENVIRONMENT_IS_NODE) {
1026
1006
  var nodeCrypto = require('node:crypto');
1027
- return (view) => nodeCrypto.randomFillSync(view);
1007
+ return (view) => (nodeCrypto.randomFillSync(view), 0);
1028
1008
  }
1029
1009
 
1030
- return (view) => crypto.getRandomValues(view);
1031
- };
1032
- var randomFill = (view) => {
1033
- // Lazily init on the first invocation.
1034
- (randomFill = initRandomFill())(view);
1010
+ return (view) => (crypto.getRandomValues(view), 0);
1035
1011
  };
1012
+ var randomFill = (view) => (randomFill = initRandomFill())(view);
1036
1013
 
1037
1014
 
1038
1015
 
@@ -1098,7 +1075,7 @@ async function createWasm() {
1098
1075
  heap[outIdx++] = 0x80 | (u & 63);
1099
1076
  } else {
1100
1077
  if (outIdx + 3 >= endIdx) break;
1101
- if (u > 0x10FFFF) warnOnce('Invalid Unicode code point ' + ptrToString(u) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF).');
1078
+ if (u > 0x10FFFF) warnOnce(`Invalid Unicode code point ${ptrToString(u)} encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF).`);
1102
1079
  heap[outIdx++] = 0xF0 | (u >> 18);
1103
1080
  heap[outIdx++] = 0x80 | ((u >> 12) & 63);
1104
1081
  heap[outIdx++] = 0x80 | ((u >> 6) & 63);
@@ -1313,7 +1290,7 @@ async function createWasm() {
1313
1290
  var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size);
1314
1291
 
1315
1292
  var alignMemory = (size, alignment) => {
1316
- assert(alignment, "alignment argument is required");
1293
+ assert(alignment, 'alignment argument is required');
1317
1294
  return Math.ceil(size / alignment) * alignment;
1318
1295
  };
1319
1296
  var mmapAlloc = (size) => {
@@ -1386,11 +1363,14 @@ async function createWasm() {
1386
1363
  } else if (FS.isFile(node.mode)) {
1387
1364
  node.node_ops = MEMFS.ops_table.file.node;
1388
1365
  node.stream_ops = MEMFS.ops_table.file.stream;
1389
- node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity.
1390
- // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred
1391
- // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size
1392
- // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme.
1393
- node.contents = null;
1366
+ // The actual number of bytes used in the typed array, as opposed to
1367
+ // contents.length which gives the whole capacity.
1368
+ node.usedBytes = 0;
1369
+ // The byte data of the file is stored in a typed array.
1370
+ // Note: typed arrays are not resizable like normal JS arrays are, so
1371
+ // there is a small penalty involved for appending file writes that
1372
+ // continuously grow a file similar to std::vector capacity vs used.
1373
+ node.contents = MEMFS.emptyFileContents ??= new Uint8Array(0);
1394
1374
  } else if (FS.isLink(node.mode)) {
1395
1375
  node.node_ops = MEMFS.ops_table.link.node;
1396
1376
  node.stream_ops = MEMFS.ops_table.link.stream;
@@ -1407,36 +1387,30 @@ async function createWasm() {
1407
1387
  return node;
1408
1388
  },
1409
1389
  getFileDataAsTypedArray(node) {
1410
- if (!node.contents) return new Uint8Array(0);
1411
- if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes.
1412
- return new Uint8Array(node.contents);
1390
+ assert(FS.isFile(node.mode), 'getFileDataAsTypedArray called on non-file');
1391
+ return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes.
1413
1392
  },
1414
1393
  expandFileStorage(node, newCapacity) {
1415
- var prevCapacity = node.contents ? node.contents.length : 0;
1394
+ var prevCapacity = node.contents.length;
1416
1395
  if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough.
1417
- // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity.
1418
- // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to
1419
- // avoid overshooting the allocation cap by a very large margin.
1396
+ // Don't expand strictly to the given requested limit if it's only a very
1397
+ // small increase, but instead geometrically grow capacity.
1398
+ // For small filesizes (<1MB), perform size*2 geometric increase, but for
1399
+ // large sizes, do a much more conservative size*1.125 increase to avoid
1400
+ // overshooting the allocation cap by a very large margin.
1420
1401
  var CAPACITY_DOUBLING_MAX = 1024 * 1024;
1421
1402
  newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0);
1422
- if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding.
1423
- var oldContents = node.contents;
1403
+ if (prevCapacity) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding.
1404
+ var oldContents = MEMFS.getFileDataAsTypedArray(node);
1424
1405
  node.contents = new Uint8Array(newCapacity); // Allocate new storage.
1425
- if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage.
1406
+ node.contents.set(oldContents);
1426
1407
  },
1427
1408
  resizeFileStorage(node, newSize) {
1428
1409
  if (node.usedBytes == newSize) return;
1429
- if (newSize == 0) {
1430
- node.contents = null; // Fully decommit when requesting a resize to zero.
1431
- node.usedBytes = 0;
1432
- } else {
1433
- var oldContents = node.contents;
1434
- node.contents = new Uint8Array(newSize); // Allocate new storage.
1435
- if (oldContents) {
1436
- node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage.
1437
- }
1438
- node.usedBytes = newSize;
1439
- }
1410
+ var oldContents = node.contents;
1411
+ node.contents = new Uint8Array(newSize); // Allocate new storage.
1412
+ node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage.
1413
+ node.usedBytes = newSize;
1440
1414
  },
1441
1415
  node_ops:{
1442
1416
  getattr(node) {
@@ -1536,16 +1510,11 @@ async function createWasm() {
1536
1510
  if (position >= stream.node.usedBytes) return 0;
1537
1511
  var size = Math.min(stream.node.usedBytes - position, length);
1538
1512
  assert(size >= 0);
1539
- if (size > 8 && contents.subarray) { // non-trivial, and typed array
1540
- buffer.set(contents.subarray(position, position + size), offset);
1541
- } else {
1542
- for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
1543
- }
1513
+ buffer.set(contents.subarray(position, position + size), offset);
1544
1514
  return size;
1545
1515
  },
1546
1516
  write(stream, buffer, offset, length, position, canOwn) {
1547
- // The data buffer should be a typed array view
1548
- assert(!(buffer instanceof ArrayBuffer));
1517
+ assert(buffer.subarray, 'FS.write expects a TypedArray');
1549
1518
  // If the buffer is located in main memory (HEAP), and if
1550
1519
  // memory can grow, we can't hold on to references of the
1551
1520
  // memory buffer, as they may get invalidated. That means we
@@ -1558,33 +1527,19 @@ async function createWasm() {
1558
1527
  var node = stream.node;
1559
1528
  node.mtime = node.ctime = Date.now();
1560
1529
 
1561
- if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array?
1562
- if (canOwn) {
1563
- assert(position === 0, 'canOwn must imply no weird position inside the file');
1564
- node.contents = buffer.subarray(offset, offset + length);
1565
- node.usedBytes = length;
1566
- return length;
1567
- } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data.
1568
- node.contents = buffer.slice(offset, offset + length);
1569
- node.usedBytes = length;
1570
- return length;
1571
- } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file?
1572
- node.contents.set(buffer.subarray(offset, offset + length), position);
1573
- return length;
1574
- }
1575
- }
1576
-
1577
- // Appending to an existing file and we need to reallocate, or source data did not come as a typed array.
1578
- MEMFS.expandFileStorage(node, position+length);
1579
- if (node.contents.subarray && buffer.subarray) {
1530
+ if (canOwn) {
1531
+ assert(position === 0, 'canOwn must imply no weird position inside the file');
1532
+ node.contents = buffer.subarray(offset, offset + length);
1533
+ node.usedBytes = length;
1534
+ } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data.
1535
+ node.contents = buffer.slice(offset, offset + length);
1536
+ node.usedBytes = length;
1537
+ } else {
1538
+ MEMFS.expandFileStorage(node, position+length);
1580
1539
  // Use typed array write which is available.
1581
1540
  node.contents.set(buffer.subarray(offset, offset + length), position);
1582
- } else {
1583
- for (var i = 0; i < length; i++) {
1584
- node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not.
1585
- }
1541
+ node.usedBytes = Math.max(node.usedBytes, position + length);
1586
1542
  }
1587
- node.usedBytes = Math.max(node.usedBytes, position + length);
1588
1543
  return length;
1589
1544
  },
1590
1545
  llseek(stream, offset, whence) {
@@ -1609,7 +1564,7 @@ async function createWasm() {
1609
1564
  var allocated;
1610
1565
  var contents = stream.node.contents;
1611
1566
  // Only make a new copy when MAP_PRIVATE is specified.
1612
- if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) {
1567
+ if (!(flags & 2) && contents.buffer === HEAP8.buffer) {
1613
1568
  // We can't emulate MAP_SHARED when the file is not backed by the
1614
1569
  // buffer we're mapping to (e.g. the HEAP buffer).
1615
1570
  allocated = false;
@@ -1643,6 +1598,7 @@ async function createWasm() {
1643
1598
  };
1644
1599
 
1645
1600
  var FS_modeStringToFlags = (str) => {
1601
+ if (typeof str != 'string') return str;
1646
1602
  var flagModes = {
1647
1603
  'r': 0,
1648
1604
  'r+': 2,
@@ -1658,6 +1614,16 @@ async function createWasm() {
1658
1614
  return flags;
1659
1615
  };
1660
1616
 
1617
+ var FS_fileDataToTypedArray = (data) => {
1618
+ if (typeof data == 'string') {
1619
+ data = intArrayFromString(data, true);
1620
+ }
1621
+ if (!data.subarray) {
1622
+ data = new Uint8Array(data);
1623
+ }
1624
+ return data;
1625
+ };
1626
+
1661
1627
  var FS_getMode = (canRead, canWrite) => {
1662
1628
  var mode = 0;
1663
1629
  if (canRead) mode |= 292 | 73;
@@ -1951,7 +1917,11 @@ async function createWasm() {
1951
1917
  if (attr.mode != null && attr.dontFollow) {
1952
1918
  throw new FS.ErrnoError(52);
1953
1919
  }
1954
- NODEFS.setattr(path, node, attr, fs.chmodSync, fs.utimesSync, fs.truncateSync, fs.lstatSync);
1920
+ // `dontFollow` (AT_SYMLINK_NOFOLLOW): use lutimes so the symlink's own
1921
+ // timestamps are set without the host resolving it, which would
1922
+ // otherwise escape the NODEFS mount root.
1923
+ var utimes = attr.dontFollow ? fs.lutimesSync : fs.utimesSync;
1924
+ NODEFS.setattr(path, node, attr, fs.chmodSync, utimes, fs.truncateSync, fs.lstatSync);
1955
1925
  },
1956
1926
  lookup(parent, name) {
1957
1927
  var path = PATH.join2(NODEFS.realPath(parent), name);
@@ -2083,6 +2053,70 @@ async function createWasm() {
2083
2053
 
2084
2054
 
2085
2055
 
2056
+
2057
+ var NODERAWFS_stream_funcs = {
2058
+ close(stream) {
2059
+ VFS.closeStream(stream.fd);
2060
+ // Don't close stdin/stdout/stderr since they are used by node itself.
2061
+ if (--stream.shared.refcnt <= 0 && stream.nfd > 2) {
2062
+ // This stream is created by our Node.js filesystem, close the
2063
+ // native file descriptor when its reference count drops to 0.
2064
+ fs.closeSync(stream.nfd);
2065
+ }
2066
+ },
2067
+ llseek(stream, offset, whence) {
2068
+ var position = offset;
2069
+ if (whence === 1) {
2070
+ position += stream.position;
2071
+ } else if (whence === 2) {
2072
+ position += fs.fstatSync(stream.nfd).size;
2073
+ } else if (whence !== 0) {
2074
+ throw new FS.ErrnoError(28);
2075
+ }
2076
+
2077
+ if (position < 0) {
2078
+ throw new FS.ErrnoError(28);
2079
+ }
2080
+ stream.position = position;
2081
+ return position;
2082
+ },
2083
+ read(stream, buffer, offset, length, position) {
2084
+ var seeking = typeof position != 'undefined';
2085
+ if (!seeking && stream.seekable) position = stream.position;
2086
+ var bytesRead = fs.readSync(stream.nfd, buffer, offset, length, position);
2087
+ // update position marker when non-seeking
2088
+ if (!seeking) stream.position += bytesRead;
2089
+ return bytesRead;
2090
+ },
2091
+ write(stream, buffer, offset, length, position) {
2092
+ if (stream.flags & 1024) {
2093
+ // seek to the end before writing in append mode
2094
+ FS.llseek(stream, 0, 2);
2095
+ }
2096
+ var seeking = typeof position != 'undefined';
2097
+ if (!seeking && stream.seekable) position = stream.position;
2098
+ var bytesWritten = fs.writeSync(stream.nfd, buffer, offset, length, position);
2099
+ // update position marker when non-seeking
2100
+ if (!seeking) stream.position += bytesWritten;
2101
+ return bytesWritten;
2102
+ },
2103
+ mmap(stream, length, position, prot, flags) {
2104
+ if (!length) {
2105
+ throw new FS.ErrnoError(28);
2106
+ }
2107
+ var ptr = mmapAlloc(length);
2108
+ FS.read(stream, HEAP8, ptr, length, position);
2109
+ return { ptr, allocated: true };
2110
+ },
2111
+ msync(stream, buffer, offset, length, mmapFlags) {
2112
+ FS.write(stream, buffer, 0, length, offset);
2113
+ // should we check if bytesWritten and length are the same?
2114
+ return 0;
2115
+ },
2116
+ ioctl(stream, cmd, arg) {
2117
+ throw new FS.ErrnoError(59);
2118
+ },
2119
+ };
2086
2120
  var NODERAWFS = {
2087
2121
  lookup(parent, name) {
2088
2122
  assert(parent)
@@ -2098,11 +2132,10 @@ async function createWasm() {
2098
2132
  return { path, node: { id: st.ino, mode, node_ops: NODERAWFS, path }};
2099
2133
  },
2100
2134
  createStandardStreams() {
2101
- // FIXME: tty is set to true to appease isatty(), the underlying ioctl syscalls still need to be implemented, see issue #22264.
2102
- FS.createStream({ nfd: 0, position: 0, path: '/dev/stdin', flags: 0, tty: true, seekable: false }, 0);
2135
+ FS.createStream({ nfd: 0, position: 0, path: '/dev/stdin', flags: 0, seekable: false }, 0);
2103
2136
  var paths = [,'/dev/stdout', '/dev/stderr'];
2104
2137
  for (var i = 1; i < 3; i++) {
2105
- FS.createStream({ nfd: i, position: 0, path: paths[i], flags: 577, tty: true, seekable: false }, i);
2138
+ FS.createStream({ nfd: i, position: 0, path: paths[i], flags: 577, seekable: false }, i);
2106
2139
  }
2107
2140
  },
2108
2141
  cwd() { return process.cwd(); },
@@ -2116,6 +2149,14 @@ async function createWasm() {
2116
2149
  },
2117
2150
  mkdir(...args) { fs.mkdirSync(...args); },
2118
2151
  symlink(...args) { fs.symlinkSync(...args); },
2152
+ link(oldpath, newpath, flags) {
2153
+ // AT_SYMLINK_FOLLOW (0x400): dereference oldpath if it is a symlink,
2154
+ // since node's link(2) links to the symlink itself by default.
2155
+ if (flags & 0x400) {
2156
+ oldpath = fs.realpathSync(oldpath);
2157
+ }
2158
+ fs.linkSync(oldpath, newpath);
2159
+ },
2119
2160
  rename(...args) { fs.renameSync(...args); },
2120
2161
  rmdir(...args) { fs.rmdirSync(...args); },
2121
2162
  readdir(...args) { return ['.', '..'].concat(fs.readdirSync(...args)); },
@@ -2132,6 +2173,12 @@ async function createWasm() {
2132
2173
  },
2133
2174
  fstat(fd) {
2134
2175
  var stream = FS.getStreamChecked(fd);
2176
+ // Virtual streams (pipes, sockets) have no backing node fd; defer to their
2177
+ // own getattr rather than node's fs.fstatSync.
2178
+ var getattr = stream.stream_ops?.getattr ?? stream.node.node_ops?.getattr;
2179
+ if (getattr) {
2180
+ return getattr(stream.stream_ops?.getattr ? stream : stream.node);
2181
+ }
2135
2182
  return fs.fstatSync(stream.nfd);
2136
2183
  },
2137
2184
  statfs(path) {
@@ -2185,21 +2232,23 @@ async function createWasm() {
2185
2232
  var stream = FS.getStreamChecked(fd);
2186
2233
  fs.ftruncateSync(stream.nfd, len);
2187
2234
  },
2188
- utime(path, atime, mtime) {
2235
+ utime(path, atime, mtime, dontFollow) {
2189
2236
  // null here for atime or mtime means UTIME_OMIT was passed. Since node
2190
2237
  // doesn't support this concept we need to first find the existing
2191
2238
  // timestamps in order to preserve them.
2192
2239
  if ((atime === null) || (mtime === null)) {
2193
- var st = fs.statSync(path);
2240
+ var st = dontFollow ? fs.lstatSync(path) : fs.statSync(path);
2194
2241
  atime ||= st.atimeMs;
2195
2242
  mtime ||= st.mtimeMs;
2196
2243
  }
2197
- fs.utimesSync(path, atime/1000, mtime/1000);
2244
+ if (dontFollow) {
2245
+ fs.lutimesSync(path, atime/1000, mtime/1000);
2246
+ } else {
2247
+ fs.utimesSync(path, atime/1000, mtime/1000);
2248
+ }
2198
2249
  },
2199
2250
  open(path, flags, mode) {
2200
- if (typeof flags == "string") {
2201
- flags = FS_modeStringToFlags(flags)
2202
- }
2251
+ flags = FS_modeStringToFlags(flags);
2203
2252
  var pathTruncated = path.split('/').map((s) => s.slice(0, 255)).join('/');
2204
2253
  var nfd = fs.openSync(pathTruncated, NODEFS.flagsForNode(flags), mode);
2205
2254
  var st = fs.fstatSync(nfd);
@@ -2214,96 +2263,14 @@ async function createWasm() {
2214
2263
  createStream(stream, fd) {
2215
2264
  // Call the original FS.createStream
2216
2265
  var rtn = VFS.createStream(stream, fd);
2217
- if (typeof rtn.shared.refcnt == 'undefined') {
2218
- rtn.shared.refcnt = 1;
2219
- } else {
2266
+ // Detect PIPEFS streams and skip the refcnt/tty initialization in that case.
2267
+ if (!stream.stream_ops) {
2268
+ rtn.shared.refcnt ??= 0;
2220
2269
  rtn.shared.refcnt++;
2270
+ rtn.tty = nodeTTY.isatty(rtn.nfd);
2221
2271
  }
2222
2272
  return rtn;
2223
2273
  },
2224
- close(stream) {
2225
- VFS.closeStream(stream.fd);
2226
- // Don't close stdin/stdout/stderr since they are used by node itself.
2227
- if (!stream.stream_ops && --stream.shared.refcnt <= 0 && stream.nfd > 2) {
2228
- // This stream is created by our Node.js filesystem, close the
2229
- // native file descriptor when its reference count drops to 0.
2230
- fs.closeSync(stream.nfd);
2231
- }
2232
- },
2233
- llseek(stream, offset, whence) {
2234
- if (stream.stream_ops) {
2235
- // this stream is created by in-memory filesystem
2236
- return VFS.llseek(stream, offset, whence);
2237
- }
2238
- var position = offset;
2239
- if (whence === 1) {
2240
- position += stream.position;
2241
- } else if (whence === 2) {
2242
- position += fs.fstatSync(stream.nfd).size;
2243
- } else if (whence !== 0) {
2244
- throw new FS.ErrnoError(28);
2245
- }
2246
-
2247
- if (position < 0) {
2248
- throw new FS.ErrnoError(28);
2249
- }
2250
- stream.position = position;
2251
- return position;
2252
- },
2253
- read(stream, buffer, offset, length, position) {
2254
- if (stream.stream_ops) {
2255
- // this stream is created by in-memory filesystem
2256
- return VFS.read(stream, buffer, offset, length, position);
2257
- }
2258
- var seeking = typeof position != 'undefined';
2259
- if (!seeking && stream.seekable) position = stream.position;
2260
- var bytesRead = fs.readSync(stream.nfd, buffer, offset, length, position);
2261
- // update position marker when non-seeking
2262
- if (!seeking) stream.position += bytesRead;
2263
- return bytesRead;
2264
- },
2265
- write(stream, buffer, offset, length, position) {
2266
- if (stream.stream_ops) {
2267
- // this stream is created by in-memory filesystem
2268
- return VFS.write(stream, buffer, offset, length, position);
2269
- }
2270
- if (stream.flags & +"1024") {
2271
- // seek to the end before writing in append mode
2272
- FS.llseek(stream, 0, +"2");
2273
- }
2274
- var seeking = typeof position != 'undefined';
2275
- if (!seeking && stream.seekable) position = stream.position;
2276
- var bytesWritten = fs.writeSync(stream.nfd, buffer, offset, length, position);
2277
- // update position marker when non-seeking
2278
- if (!seeking) stream.position += bytesWritten;
2279
- return bytesWritten;
2280
- },
2281
- mmap(stream, length, position, prot, flags) {
2282
- if (!length) {
2283
- throw new FS.ErrnoError(28);
2284
- }
2285
- if (stream.stream_ops) {
2286
- // this stream is created by in-memory filesystem
2287
- return VFS.mmap(stream, length, position, prot, flags);
2288
- }
2289
-
2290
- var ptr = mmapAlloc(length);
2291
- FS.read(stream, HEAP8, ptr, length, position);
2292
- return { ptr, allocated: true };
2293
- },
2294
- msync(stream, buffer, offset, length, mmapFlags) {
2295
- if (stream.stream_ops) {
2296
- // this stream is created by in-memory filesystem
2297
- return VFS.msync(stream, buffer, offset, length, mmapFlags);
2298
- }
2299
-
2300
- FS.write(stream, buffer, 0, length, offset);
2301
- // should we check if bytesWritten and length are the same?
2302
- return 0;
2303
- },
2304
- ioctl() {
2305
- throw new FS.ErrnoError(59);
2306
- },
2307
2274
  };
2308
2275
 
2309
2276
 
@@ -2328,6 +2295,73 @@ async function createWasm() {
2328
2295
  }
2329
2296
  };
2330
2297
 
2298
+ var dependenciesPromise = null;
2299
+ var resolveRunDependencies = async () => dependenciesPromise;
2300
+ var runDependencies = 0;
2301
+
2302
+
2303
+ var dependenciesPromiseResolve = null;
2304
+
2305
+ var runDependencyTracking = {
2306
+ };
2307
+
2308
+ var runDependencyWatcher = null;
2309
+ var removeRunDependency = (id) => {
2310
+ runDependencies--;
2311
+
2312
+ Module['monitorRunDependencies']?.(runDependencies);
2313
+
2314
+ assert(id, 'removeRunDependency requires an ID');
2315
+ assert(runDependencyTracking[id]);
2316
+ delete runDependencyTracking[id];
2317
+ if (!runDependencies) {
2318
+ if (runDependencyWatcher !== null) {
2319
+ clearInterval(runDependencyWatcher);
2320
+ runDependencyWatcher = null;
2321
+ }
2322
+ dependenciesPromiseResolve();
2323
+ }
2324
+ };
2325
+
2326
+
2327
+
2328
+
2329
+ var addRunDependency = (id) => {
2330
+ if (!runDependencies) {
2331
+ dependenciesPromise = new Promise((resolve) => dependenciesPromiseResolve = resolve);
2332
+ }
2333
+ runDependencies++;
2334
+
2335
+ Module['monitorRunDependencies']?.(runDependencies);
2336
+
2337
+ assert(id, 'addRunDependency requires an ID')
2338
+ assert(!runDependencyTracking[id]);
2339
+ runDependencyTracking[id] = 1;
2340
+ if (runDependencyWatcher === null && globalThis.setInterval) {
2341
+ // Check for missing dependencies every few seconds
2342
+ runDependencyWatcher = setInterval(() => {
2343
+ if (ABORT) {
2344
+ clearInterval(runDependencyWatcher);
2345
+ runDependencyWatcher = null;
2346
+ return;
2347
+ }
2348
+ var shown = false;
2349
+ for (var dep in runDependencyTracking) {
2350
+ if (!shown) {
2351
+ shown = true;
2352
+ err('still waiting on run dependencies:');
2353
+ }
2354
+ err(`dependency: ${dep}`);
2355
+ }
2356
+ if (shown) {
2357
+ err('(end of list)');
2358
+ }
2359
+ }, 10000);
2360
+ // Prevent this timer from keeping the runtime alive if nothing
2361
+ // else is.
2362
+ runDependencyWatcher.unref?.()
2363
+ }
2364
+ };
2331
2365
 
2332
2366
 
2333
2367
  var preloadPlugins = [];
@@ -2468,6 +2502,48 @@ async function createWasm() {
2468
2502
  get isDevice() {
2469
2503
  return FS.isChrdev(this.mode);
2470
2504
  }
2505
+ // The per-inode readiness wait-queue. The node carries a Set of listener
2506
+ // entries {cb}; producers (SOCKFS, PIPEFS) call notifyListeners on a
2507
+ // readiness transition, and poll()/epoll consume it. It lives on the node
2508
+ // (not the fd) so dup'd fds share one queue. Only nodes that derive real
2509
+ // readiness (sockets, pipes, and an epoll's own node) ever use this -
2510
+ // always-ready types (regular files, ttys) never register or notify.
2511
+ addListener(cb, exclusive = false) {
2512
+ var entry = {cb, exclusive};
2513
+ var listeners = (this.listeners ??= new Set());
2514
+ listeners.add(entry);
2515
+ return {listeners, entry};
2516
+ }
2517
+ notifyListeners(flags) {
2518
+ // Iterates the set without copying, which is safe ONLY under a
2519
+ // load-bearing contract that every internal listener must honour:
2520
+ // 1. A listener must not run user code synchronously (a poll waiter only
2521
+ // resolves a Promise; an epoll registration only re-lists +
2522
+ // re-notifies; the epoll callback only schedules a tick). User code
2523
+ // runs on a later tick, never inside this loop.
2524
+ // 2. A listener may delete entries only from ITS OWN waiter, never from
2525
+ // a sibling node's set that may be mid-iteration. (Deleting an entry
2526
+ // of the set being iterated here is fine - a Set tolerates removal of
2527
+ // a not-yet-visited entry mid-iteration; mutating a *different* node's
2528
+ // set is fine because that set is not being iterated.)
2529
+ // Violating either gives silently skipped wakeups that are near-impossible
2530
+ // to reproduce. Any new producer/listener must preserve it.
2531
+ if (!this.listeners) return;
2532
+ // Fire every non-exclusive listener. Among EPOLLEXCLUSIVE registrations
2533
+ // (one fd watched by several epolls) wake only one, rotating round-robin
2534
+ // per node, to avoid a thundering herd. (Only epoll registrations are ever
2535
+ // exclusive; poll waiters and a node's own consumers are not.)
2536
+ var excl;
2537
+ for (var entry of this.listeners) {
2538
+ if (entry.exclusive) (excl ||= []).push(entry);
2539
+ else entry.cb(flags);
2540
+ }
2541
+ if (excl) {
2542
+ var i = (this.exclTurn || 0) % excl.length;
2543
+ this.exclTurn = i + 1;
2544
+ excl[i].cb(flags);
2545
+ }
2546
+ }
2471
2547
  },
2472
2548
  lookupPath(path, opts = {}) {
2473
2549
  if (!path) {
@@ -2479,7 +2555,7 @@ async function createWasm() {
2479
2555
  path = FS.cwd() + '/' + path;
2480
2556
  }
2481
2557
 
2482
- // limit max consecutive symlinks to 40 (SYMLOOP_MAX).
2558
+ // limit max consecutive symlinks to SYMLOOP_MAX.
2483
2559
  linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) {
2484
2560
  // split the absolute path
2485
2561
  var parts = path.split('/').filter((p) => !!p);
@@ -2771,7 +2847,14 @@ async function createWasm() {
2771
2847
  var arg = setattr ? stream : node;
2772
2848
  setattr ??= node.node_ops.setattr;
2773
2849
  FS.checkOpExists(setattr, 63)
2774
- setattr(arg, attr);
2850
+ try {
2851
+ setattr(arg, attr);
2852
+ } catch (e) {
2853
+ if (e instanceof RangeError) {
2854
+ throw new FS.ErrnoError(22);
2855
+ }
2856
+ throw e;
2857
+ }
2775
2858
  },
2776
2859
  chrdev_stream_ops:{
2777
2860
  open(stream) {
@@ -3038,6 +3121,25 @@ async function createWasm() {
3038
3121
  }
3039
3122
  return parent.node_ops.symlink(parent, newname, oldpath);
3040
3123
  },
3124
+ link(oldpath, newpath, flags) {
3125
+ var lookup = FS.lookupPath(newpath, { parent: true });
3126
+ var parent = lookup.node;
3127
+ if (!parent) {
3128
+ throw new FS.ErrnoError(44);
3129
+ }
3130
+ var newname = PATH.basename(newpath);
3131
+ var errCode = FS.mayCreate(parent, newname);
3132
+ if (errCode) {
3133
+ throw new FS.ErrnoError(errCode);
3134
+ }
3135
+ // Hardlinks are only supported by filesystem backends that provide a
3136
+ // `link` node op (e.g. NODERAWFS backed by the host). NODEFS omits it:
3137
+ // a host hardlink cannot be confined to the mount root.
3138
+ if (!parent.node_ops.link) {
3139
+ throw new FS.ErrnoError(34);
3140
+ }
3141
+ return parent.node_ops.link(parent, newname, oldpath, flags);
3142
+ },
3041
3143
  rename(old_path, new_path) {
3042
3144
  var old_dirname = PATH.dirname(old_path);
3043
3145
  var new_dirname = PATH.dirname(new_path);
@@ -3284,20 +3386,19 @@ async function createWasm() {
3284
3386
  }
3285
3387
  FS.doTruncate(stream, stream.node, len);
3286
3388
  },
3287
- utime(path, atime, mtime) {
3288
- var lookup = FS.lookupPath(path, { follow: true });
3289
- var node = lookup.node;
3290
- var setattr = FS.checkOpExists(node.node_ops.setattr, 63);
3291
- setattr(node, {
3389
+ utime(path, atime, mtime, dontFollow) {
3390
+ var lookup = FS.lookupPath(path, { follow: !dontFollow });
3391
+ FS.doSetAttr(null, lookup.node, {
3292
3392
  atime: atime,
3293
- mtime: mtime
3393
+ mtime: mtime,
3394
+ dontFollow
3294
3395
  });
3295
3396
  },
3296
3397
  open(path, flags, mode = 0o666) {
3297
3398
  if (path === "") {
3298
3399
  throw new FS.ErrnoError(44);
3299
3400
  }
3300
- flags = typeof flags == 'string' ? FS_modeStringToFlags(flags) : flags;
3401
+ flags = FS_modeStringToFlags(flags);
3301
3402
  if ((flags & 64)) {
3302
3403
  mode = (mode & 4095) | 32768;
3303
3404
  } else {
@@ -3391,6 +3492,11 @@ async function createWasm() {
3391
3492
  throw new FS.ErrnoError(8);
3392
3493
  }
3393
3494
  if (stream.getdents) stream.getdents = null; // free readdir state
3495
+ // The fd is going away: wake anything waiting on it (poll/epoll) with
3496
+ // POLLNVAL so a blocking wait unblocks and an epoll registration is evicted
3497
+ // on its next derive. Only sockets/pipes/epoll ever carry a wait-queue, so
3498
+ // for every other stream (incl. nodeless noderawfs stdio) this is a no-op.
3499
+ stream.node?.notifyListeners(32);
3394
3500
  try {
3395
3501
  if (stream.stream_ops.close) {
3396
3502
  stream.stream_ops.close(stream);
@@ -3448,6 +3554,7 @@ async function createWasm() {
3448
3554
  },
3449
3555
  write(stream, buffer, offset, length, position, canOwn) {
3450
3556
  assert(offset >= 0);
3557
+ assert(buffer.subarray, 'FS.write expects a TypedArray');
3451
3558
  if (length < 0 || position < 0) {
3452
3559
  throw new FS.ErrnoError(28);
3453
3560
  }
@@ -3514,8 +3621,8 @@ async function createWasm() {
3514
3621
  return stream.stream_ops.ioctl(stream, cmd, arg);
3515
3622
  },
3516
3623
  readFile(path, opts = {}) {
3517
- opts.flags = opts.flags || 0;
3518
- opts.encoding = opts.encoding || 'binary';
3624
+ opts.flags = opts.flags ?? 0;
3625
+ opts.encoding = opts.encoding ?? 'binary';
3519
3626
  if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') {
3520
3627
  abort(`Invalid encoding type "${opts.encoding}"`);
3521
3628
  }
@@ -3531,16 +3638,10 @@ async function createWasm() {
3531
3638
  return buf;
3532
3639
  },
3533
3640
  writeFile(path, data, opts = {}) {
3534
- opts.flags = opts.flags || 577;
3641
+ opts.flags = opts.flags ?? 577;
3535
3642
  var stream = FS.open(path, opts.flags, opts.mode);
3536
- if (typeof data == 'string') {
3537
- data = new Uint8Array(intArrayFromString(data, true));
3538
- }
3539
- if (ArrayBuffer.isView(data)) {
3540
- FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn);
3541
- } else {
3542
- abort('Unsupported data type');
3543
- }
3643
+ data = FS_fileDataToTypedArray(data);
3644
+ FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn);
3544
3645
  FS.close(stream);
3545
3646
  },
3546
3647
  cwd:() => FS.currentPath,
@@ -3766,11 +3867,7 @@ async function createWasm() {
3766
3867
  var mode = FS_getMode(canRead, canWrite);
3767
3868
  var node = FS.create(path, mode);
3768
3869
  if (data) {
3769
- if (typeof data == 'string') {
3770
- var arr = new Array(data.length);
3771
- for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
3772
- data = arr;
3773
- }
3870
+ data = FS_fileDataToTypedArray(data);
3774
3871
  // make sure we can write to the file
3775
3872
  FS.chmod(node, mode | 146);
3776
3873
  var stream = FS.open(node, 577);
@@ -3879,8 +3976,8 @@ async function createWasm() {
3879
3976
 
3880
3977
  // Function to get a range from the remote URL.
3881
3978
  var doXHR = (from, to) => {
3882
- if (from > to) abort("invalid range (" + from + ", " + to + ") or no bytes requested!");
3883
- if (to > datalength-1) abort("only " + datalength + " bytes available! programmer error!");
3979
+ if (from > to) abort(`invalid range (${from}, ${to}) or no bytes requested!`);
3980
+ if (to > datalength-1) abort(`only ${datalength} bytes available! programmer error!`);
3884
3981
 
3885
3982
  // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
3886
3983
  var xhr = new XMLHttpRequest();
@@ -3898,7 +3995,7 @@ async function createWasm() {
3898
3995
  if (xhr.response !== undefined) {
3899
3996
  return new Uint8Array(/** @type{Array<number>} */(xhr.response || []));
3900
3997
  }
3901
- return intArrayFromString(xhr.responseText || '', true);
3998
+ return intArrayFromString(xhr.responseText ?? '', true);
3902
3999
  };
3903
4000
  var lazyArray = this;
3904
4001
  lazyArray.setDataGetter((chunkNum) => {
@@ -4005,27 +4102,10 @@ async function createWasm() {
4005
4102
  node.stream_ops = stream_ops;
4006
4103
  return node;
4007
4104
  },
4008
- absolutePath() {
4009
- abort('FS.absolutePath has been removed; use PATH_FS.resolve instead');
4010
- },
4011
- createFolder() {
4012
- abort('FS.createFolder has been removed; use FS.mkdir instead');
4013
- },
4014
- createLink() {
4015
- abort('FS.createLink has been removed; use FS.symlink instead');
4016
- },
4017
- joinPath() {
4018
- abort('FS.joinPath has been removed; use PATH.join instead');
4019
- },
4020
- mmapAlloc() {
4021
- abort('FS.mmapAlloc has been replaced by the top level function mmapAlloc');
4022
- },
4023
- standardizePath() {
4024
- abort('FS.standardizePath has been removed; use PATH.normalize instead');
4025
- },
4026
4105
  };
4027
4106
 
4028
4107
  var SYSCALLS = {
4108
+ currentUmask:18,
4029
4109
  calculateAt(dirfd, path, allowEmpty) {
4030
4110
  if (PATH.isAbs(path)) {
4031
4111
  return path;
@@ -4088,7 +4168,7 @@ async function createWasm() {
4088
4168
  // MAP_PRIVATE calls need not to be synced back to underlying fs
4089
4169
  return 0;
4090
4170
  }
4091
- var buffer = HEAPU8.slice(addr, addr + len);
4171
+ var buffer = HEAPU8.subarray(addr, addr + len);
4092
4172
  FS.msync(stream, buffer, offset, len, flags);
4093
4173
  },
4094
4174
  getStreamFromFD(fd) {
@@ -4126,7 +4206,8 @@ async function createWasm() {
4126
4206
  return stream.flags;
4127
4207
  case 4: {
4128
4208
  var arg = syscallGetVarargI();
4129
- stream.flags |= arg;
4209
+ var mask = 289792;
4210
+ stream.flags = (stream.flags & ~mask) | (arg & mask);
4130
4211
  return 0;
4131
4212
  }
4132
4213
  case 12: {
@@ -4150,6 +4231,7 @@ async function createWasm() {
4150
4231
  return -e.errno;
4151
4232
  }
4152
4233
  }
4234
+
4153
4235
 
4154
4236
 
4155
4237
  function ___syscall_ioctl(fd, op, varargs) {
@@ -4247,6 +4329,7 @@ async function createWasm() {
4247
4329
  return -e.errno;
4248
4330
  }
4249
4331
  }
4332
+
4250
4333
 
4251
4334
 
4252
4335
  function ___syscall_openat(dirfd, path, flags, varargs) {
@@ -4256,18 +4339,22 @@ async function createWasm() {
4256
4339
  path = SYSCALLS.getStr(path);
4257
4340
  path = SYSCALLS.calculateAt(dirfd, path);
4258
4341
  var mode = varargs ? syscallGetVarargI() : 0;
4342
+ if (flags & 64) {
4343
+ mode &= ~SYSCALLS.currentUmask;
4344
+ }
4259
4345
  return FS.open(path, flags, mode).fd;
4260
4346
  } catch (e) {
4261
4347
  if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e;
4262
4348
  return -e.errno;
4263
4349
  }
4264
4350
  }
4351
+
4265
4352
 
4266
4353
  var __abort_js = () =>
4267
4354
  abort('native code called abort()');
4268
4355
 
4269
4356
  var __emscripten_throw_longjmp = () => {
4270
- throw Infinity;
4357
+ throw new EmscriptenSjLj;
4271
4358
  };
4272
4359
 
4273
4360
 
@@ -4409,6 +4496,7 @@ async function createWasm() {
4409
4496
  return e.errno;
4410
4497
  }
4411
4498
  }
4499
+
4412
4500
 
4413
4501
  /** @param {number=} offset */
4414
4502
  var doReadv = (stream, iov, iovcnt, offset) => {
@@ -4417,7 +4505,18 @@ async function createWasm() {
4417
4505
  var ptr = HEAPU32[((iov)>>2)];
4418
4506
  var len = HEAPU32[(((iov)+(4))>>2)];
4419
4507
  iov += 8;
4420
- var curr = FS.read(stream, HEAP8, ptr, len, offset);
4508
+ try {
4509
+ var curr = FS.read(stream, HEAP8, ptr, len, offset);
4510
+ } catch (e) {
4511
+ // On a non-blocking stream a subsequent read may would-block after we
4512
+ // already gathered data. POSIX readv is a single gather-read: return
4513
+ // what we have rather than failing the whole call.
4514
+ if (ret > 0 && e instanceof FS.ErrnoError &&
4515
+ (e.errno == 6 || e.errno == 6)) {
4516
+ break;
4517
+ }
4518
+ throw e;
4519
+ }
4421
4520
  if (curr < 0) return -1;
4422
4521
  ret += curr;
4423
4522
  if (curr < len) break; // nothing more to read
@@ -4440,6 +4539,7 @@ async function createWasm() {
4440
4539
  return e.errno;
4441
4540
  }
4442
4541
  }
4542
+
4443
4543
 
4444
4544
 
4445
4545
  function _fd_seek(fd, offset, whence, newOffset) {
@@ -4448,7 +4548,7 @@ async function createWasm() {
4448
4548
 
4449
4549
  try {
4450
4550
 
4451
- if (isNaN(offset)) return 61;
4551
+ if (isNaN(offset)) return 22;
4452
4552
  var stream = SYSCALLS.getStreamFromFD(fd);
4453
4553
  FS.llseek(stream, offset, whence);
4454
4554
  HEAP64[((newOffset)>>3)] = BigInt(stream.position);
@@ -4463,23 +4563,27 @@ async function createWasm() {
4463
4563
 
4464
4564
  /** @param {number=} offset */
4465
4565
  var doWritev = (stream, iov, iovcnt, offset) => {
4466
- var ret = 0;
4467
- for (var i = 0; i < iovcnt; i++) {
4566
+ // Gather all iovecs into one contiguous buffer and issue a single
4567
+ // FS.write, matching POSIX writev's single gather-write semantics (as
4568
+ // __syscall_sendmsg already does). Per-iovec writes fragment a stream
4569
+ // socket send into multiple segments, breaking stream byte semantics.
4570
+ if (iovcnt == 1) {
4571
+ // Single iovec: write directly from HEAP8, no gather buffer needed.
4572
+ return FS.write(stream, HEAP8, HEAPU32[((iov)>>2)], HEAPU32[(((iov)+(4))>>2)], offset);
4573
+ }
4574
+ var total = 0;
4575
+ for (var i = 0, p = iov; i < iovcnt; i++, p += 8) {
4576
+ total += HEAPU32[(((p)+(4))>>2)];
4577
+ }
4578
+ var view = new Uint8Array(total);
4579
+ var voff = 0;
4580
+ for (var i = 0; i < iovcnt; i++, iov += 8) {
4468
4581
  var ptr = HEAPU32[((iov)>>2)];
4469
4582
  var len = HEAPU32[(((iov)+(4))>>2)];
4470
- iov += 8;
4471
- var curr = FS.write(stream, HEAP8, ptr, len, offset);
4472
- if (curr < 0) return -1;
4473
- ret += curr;
4474
- if (curr < len) {
4475
- // No more space to write.
4476
- break;
4477
- }
4478
- if (typeof offset != 'undefined') {
4479
- offset += curr;
4480
- }
4583
+ view.set(HEAPU8.subarray(ptr, ptr + len), voff);
4584
+ voff += len;
4481
4585
  }
4482
- return ret;
4586
+ return FS.write(stream, view, 0, total, offset);
4483
4587
  };
4484
4588
 
4485
4589
  function _fd_write(fd, iov, iovcnt, pnum) {
@@ -4494,6 +4598,7 @@ async function createWasm() {
4494
4598
  return e.errno;
4495
4599
  }
4496
4600
  }
4601
+
4497
4602
 
4498
4603
 
4499
4604
  var handleException = (e) => {
@@ -4516,7 +4621,7 @@ async function createWasm() {
4516
4621
 
4517
4622
 
4518
4623
  var stringToUTF8 = (str, outPtr, maxBytesToWrite) => {
4519
- assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
4624
+ assert(typeof maxBytesToWrite == 'number', 'stringToUTF8 requires a third parameter that specifies the length of the output buffer');
4520
4625
  return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
4521
4626
  };
4522
4627
 
@@ -4538,7 +4643,7 @@ async function createWasm() {
4538
4643
  wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr);
4539
4644
  }
4540
4645
  /** @suppress {checkTypes} */
4541
- assert(wasmTable.get(funcPtr) == func, 'JavaScript-side Wasm function table mirror is out of date!');
4646
+ assert(wasmTable.get(funcPtr) == func, 'table mirror is out of date');
4542
4647
  return func;
4543
4648
  };
4544
4649
 
@@ -4550,8 +4655,9 @@ if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit(); };
4550
4655
  if (!ENVIRONMENT_IS_NODE) {
4551
4656
  throw new Error("NODERAWFS is currently only supported on Node.js environment.")
4552
4657
  }
4553
- var _wrapNodeError = function(func) {
4554
- return function(...args) {
4658
+ var nodeTTY = require('node:tty');
4659
+ function _wrapNodeError(func) {
4660
+ return (...args) => {
4555
4661
  try {
4556
4662
  return func(...args)
4557
4663
  } catch (e) {
@@ -4566,14 +4672,26 @@ if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit(); };
4566
4672
  throw e;
4567
4673
  }
4568
4674
  }
4569
- };
4675
+ }
4676
+ function _wrapNodeStreamFunc(func, vfs_func) {
4677
+ return _wrapNodeError((stream, ...args) => {
4678
+ if (stream.stream_ops) {
4679
+ // this stream was created by some other FS. e.g: PIPEFS.
4680
+ return vfs_func(stream, ...args);
4681
+ }
4682
+ return func(stream, ...args);
4683
+ });
4684
+ }
4570
4685
  // Use this to reference our in-memory filesystem
4571
4686
  /** @suppress {partialAlias} */
4572
4687
  var VFS = {...FS};
4573
4688
  // Wrap the whole in-memory filesystem API with
4574
4689
  // our Node.js based functions
4575
- for (var _key in NODERAWFS) {
4576
- FS[_key] = _wrapNodeError(NODERAWFS[_key]);
4690
+ for (const [key, value] of Object.entries(NODERAWFS)) {
4691
+ FS[key] = _wrapNodeError(value);
4692
+ }
4693
+ for (const [key, value] of Object.entries(NODERAWFS_stream_funcs)) {
4694
+ FS[key] = _wrapNodeStreamFunc(value, FS[key]);
4577
4695
  };
4578
4696
  // End JS library code
4579
4697
 
@@ -4585,15 +4703,14 @@ if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit(); };
4585
4703
 
4586
4704
  // Begin ATMODULES hooks
4587
4705
  if (Module['noExitRuntime']) noExitRuntime = Module['noExitRuntime'];
4588
- if (Module['preloadPlugins']) preloadPlugins = Module['preloadPlugins'];
4706
+
4589
4707
  if (Module['print']) out = Module['print'];
4590
4708
  if (Module['printErr']) err = Module['printErr'];
4591
- if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];
4592
4709
  // End ATMODULES hooks
4593
4710
 
4594
4711
  checkIncomingModuleAPI();
4595
4712
 
4596
- if (Module['arguments']) arguments_ = Module['arguments'];
4713
+ if (Module['arguments']) programArgs = Module['arguments'];
4597
4714
  if (Module['thisProgram']) thisProgram = Module['thisProgram'];
4598
4715
 
4599
4716
  // Assertions on removed incoming Module JS APIs.
@@ -4612,10 +4729,13 @@ if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];
4612
4729
  assert(typeof Module['wasmMemory'] == 'undefined', 'Use of `wasmMemory` detected. Use -sIMPORTED_MEMORY to define wasmMemory externally');
4613
4730
  assert(typeof Module['INITIAL_MEMORY'] == 'undefined', 'Detected runtime INITIAL_MEMORY setting. Use -sIMPORTED_MEMORY to define wasmMemory dynamically');
4614
4731
 
4615
- if (Module['preInit']) {
4616
- if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']];
4617
- while (Module['preInit'].length > 0) {
4618
- Module['preInit'].shift()();
4732
+ var preInit = Module['preInit'];
4733
+ if (preInit) {
4734
+ if (typeof preInit == 'function') Module['preInit'] = preInit = [preInit];
4735
+ // Written as a loop so that preInit functions that themselves add more
4736
+ // preInit functions. Is this actually needed?
4737
+ while (preInit.length > 0) {
4738
+ preInit.shift()();
4619
4739
  }
4620
4740
  }
4621
4741
  consumedModuleProp('preInit');
@@ -4701,12 +4821,14 @@ if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];
4701
4821
  'registerOrientationChangeEventCallback',
4702
4822
  'fillFullscreenChangeEventData',
4703
4823
  'registerFullscreenChangeEventCallback',
4824
+ 'callCanvasResizedCallback',
4704
4825
  'JSEvents_requestFullscreen',
4705
4826
  'JSEvents_resizeCanvasForFullscreen',
4706
4827
  'registerRestoreOldStyle',
4707
4828
  'hideEverythingExceptGivenElement',
4708
4829
  'restoreHiddenElements',
4709
4830
  'setLetterbox',
4831
+ 'currentFullscreenStrategy',
4710
4832
  'softFullscreenResizeWebGLRenderTarget',
4711
4833
  'doRequestFullscreen',
4712
4834
  'fillPointerlockChangeEventData',
@@ -4738,10 +4860,13 @@ if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];
4738
4860
  'registerPreMainLoop',
4739
4861
  'getPromise',
4740
4862
  'makePromise',
4863
+ 'addPromise',
4741
4864
  'idsToPromises',
4742
4865
  'makePromiseCallback',
4743
4866
  'ExceptionInfo',
4744
4867
  'findMatchingCatch',
4868
+ 'incrementUncaughtExceptionCount',
4869
+ 'decrementUncaughtExceptionCount',
4745
4870
  'Browser_asyncPrepareDataCounter',
4746
4871
  'isLeapYear',
4747
4872
  'ydayFromDate',
@@ -4765,6 +4890,7 @@ if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];
4765
4890
  'colorChannelsInGlTextureFormat',
4766
4891
  'emscriptenWebGLGetTexPixelData',
4767
4892
  'emscriptenWebGLGetUniform',
4893
+ 'webglGetProgramUniformLocation',
4768
4894
  'webglGetUniformLocation',
4769
4895
  'webglPrepareUniformLocationsBeforeFirstUse',
4770
4896
  'webglGetLeftBracePos',
@@ -4792,21 +4918,21 @@ missingLibrarySymbols.forEach(missingLibrarySymbol)
4792
4918
  'err',
4793
4919
  'abort',
4794
4920
  'wasmExports',
4795
- 'HEAPF32',
4796
- 'HEAPF64',
4921
+ 'writeStackCookie',
4922
+ 'checkStackCookie',
4923
+ 'INT53_MAX',
4924
+ 'INT53_MIN',
4925
+ 'bigintToI53Checked',
4797
4926
  'HEAP8',
4798
4927
  'HEAPU8',
4799
4928
  'HEAP16',
4800
4929
  'HEAPU16',
4801
4930
  'HEAP32',
4802
4931
  'HEAPU32',
4932
+ 'HEAPF32',
4933
+ 'HEAPF64',
4803
4934
  'HEAP64',
4804
4935
  'HEAPU64',
4805
- 'writeStackCookie',
4806
- 'checkStackCookie',
4807
- 'INT53_MAX',
4808
- 'INT53_MIN',
4809
- 'bigintToI53Checked',
4810
4936
  'stackSave',
4811
4937
  'stackRestore',
4812
4938
  'stackAlloc',
@@ -4855,7 +4981,6 @@ missingLibrarySymbols.forEach(missingLibrarySymbol)
4855
4981
  'JSEvents',
4856
4982
  'specialHTMLTargets',
4857
4983
  'findCanvasEventTarget',
4858
- 'currentFullscreenStrategy',
4859
4984
  'restoreOldWindowedStyle',
4860
4985
  'UNWIND_CACHE',
4861
4986
  'ExitStatus',
@@ -4868,7 +4993,6 @@ missingLibrarySymbols.forEach(missingLibrarySymbol)
4868
4993
  'emClearImmediate',
4869
4994
  'promiseMap',
4870
4995
  'uncaughtExceptionCount',
4871
- 'exceptionLast',
4872
4996
  'exceptionCaught',
4873
4997
  'Browser',
4874
4998
  'requestFullscreen',
@@ -4888,6 +5012,7 @@ missingLibrarySymbols.forEach(missingLibrarySymbol)
4888
5012
  'FS_preloadFile',
4889
5013
  'FS_modeStringToFlags',
4890
5014
  'FS_getMode',
5015
+ 'FS_fileDataToTypedArray',
4891
5016
  'FS_stdin_getChar_buffer',
4892
5017
  'FS_stdin_getChar',
4893
5018
  'FS_unlink',
@@ -4956,6 +5081,7 @@ missingLibrarySymbols.forEach(missingLibrarySymbol)
4956
5081
  'FS_mkdir',
4957
5082
  'FS_mkdev',
4958
5083
  'FS_symlink',
5084
+ 'FS_link',
4959
5085
  'FS_rename',
4960
5086
  'FS_rmdir',
4961
5087
  'FS_readdir',
@@ -5000,18 +5126,13 @@ missingLibrarySymbols.forEach(missingLibrarySymbol)
5000
5126
  'FS_createDataFile',
5001
5127
  'FS_forceLoadFile',
5002
5128
  'FS_createLazyFile',
5003
- 'FS_absolutePath',
5004
- 'FS_createFolder',
5005
- 'FS_createLink',
5006
- 'FS_joinPath',
5007
- 'FS_mmapAlloc',
5008
- 'FS_standardizePath',
5009
5129
  'MEMFS',
5010
5130
  'TTY',
5011
5131
  'PIPEFS',
5012
5132
  'SOCKFS',
5013
5133
  'NODEFS',
5014
5134
  'NODERAWFS',
5135
+ 'NODERAWFS_stream_funcs',
5015
5136
  'nodePath',
5016
5137
  'tempFixedLengthArray',
5017
5138
  'miniTempWebGLFloatBuffers',
@@ -5040,6 +5161,30 @@ function checkIncomingModuleAPI() {
5040
5161
  ignoredModuleProp('fetchSettings');
5041
5162
  ignoredModuleProp('logReadFiles');
5042
5163
  ignoredModuleProp('loadSplitModule');
5164
+ ignoredModuleProp('onMalloc');
5165
+ ignoredModuleProp('onRealloc');
5166
+ ignoredModuleProp('onFree');
5167
+ ignoredModuleProp('onSbrkGrow');
5168
+ ignoredModuleProp('onCOSCacheHit');
5169
+ ignoredModuleProp('onCOSCacheMiss');
5170
+ ignoredModuleProp('onCOSStore');
5171
+ ignoredModuleProp('GL_MAX_TEXTURE_IMAGE_UNITS');
5172
+ ignoredModuleProp('SDL_canPlayWithWebAudio');
5173
+ ignoredModuleProp('SDL_numSimultaneouslyQueuedBuffers');
5174
+ ignoredModuleProp('freePreloadedMediaOnUse');
5175
+ ignoredModuleProp('preinitializedWebGLContext');
5176
+ ignoredModuleProp('keyboardListeningElement');
5177
+ ignoredModuleProp('doNotCaptureKeyboard');
5178
+ ignoredModuleProp('extraStackTrace');
5179
+ ignoredModuleProp('preloadPlugins');
5180
+ ignoredModuleProp('preMainLoop');
5181
+ ignoredModuleProp('postMainLoop');
5182
+ ignoredModuleProp('forcedAspectRatio');
5183
+ ignoredModuleProp('mainScriptUrlOrBlob');
5184
+ ignoredModuleProp('onFullScreen');
5185
+ ignoredModuleProp('INITIAL_MEMORY');
5186
+ ignoredModuleProp('wasmMemory');
5187
+ ignoredModuleProp('wasmBinary');
5043
5188
  }
5044
5189
 
5045
5190
  // Imports from the Wasm binary.
@@ -5077,14 +5222,14 @@ function assignWasmExports(wasmExports) {
5077
5222
  assert(typeof wasmExports['emscripten_stack_get_current'] != 'undefined', 'missing Wasm export: emscripten_stack_get_current');
5078
5223
  assert(typeof wasmExports['memory'] != 'undefined', 'missing Wasm export: memory');
5079
5224
  assert(typeof wasmExports['__indirect_function_table'] != 'undefined', 'missing Wasm export: __indirect_function_table');
5080
- _main = Module['_main'] = createExportWrapper('__main_argc_argv', 2);
5081
- _fflush = createExportWrapper('fflush', 1);
5082
- ___funcs_on_exit = createExportWrapper('__funcs_on_exit', 0);
5083
- _emscripten_builtin_memalign = createExportWrapper('emscripten_builtin_memalign', 2);
5225
+ _main = Module['_main'] = createExportWrapper('__main_argc_argv', wasmExports['__main_argc_argv'], 2);
5226
+ _fflush = createExportWrapper('fflush', wasmExports['fflush'], 1);
5227
+ ___funcs_on_exit = createExportWrapper('__funcs_on_exit', wasmExports['__funcs_on_exit'], 0);
5228
+ _emscripten_builtin_memalign = createExportWrapper('emscripten_builtin_memalign', wasmExports['emscripten_builtin_memalign'], 2);
5084
5229
  _emscripten_stack_get_end = wasmExports['emscripten_stack_get_end'];
5085
5230
  _emscripten_stack_get_base = wasmExports['emscripten_stack_get_base'];
5086
- _strerror = createExportWrapper('strerror', 1);
5087
- _setThrew = createExportWrapper('setThrew', 2);
5231
+ _strerror = createExportWrapper('strerror', wasmExports['strerror'], 1);
5232
+ _setThrew = createExportWrapper('setThrew', wasmExports['setThrew'], 2);
5088
5233
  _emscripten_stack_init = wasmExports['emscripten_stack_init'];
5089
5234
  _emscripten_stack_get_free = wasmExports['emscripten_stack_get_free'];
5090
5235
  __emscripten_stack_restore = wasmExports['_emscripten_stack_restore'];
@@ -5139,7 +5284,7 @@ function invoke_ii(index,a1) {
5139
5284
  return getWasmTableEntry(index)(a1);
5140
5285
  } catch(e) {
5141
5286
  stackRestore(sp);
5142
- if (e !== e+0) throw e;
5287
+ if (!(e instanceof EmscriptenEH)) throw e;
5143
5288
  _setThrew(1, 0);
5144
5289
  }
5145
5290
  }
@@ -5150,7 +5295,7 @@ function invoke_iii(index,a1,a2) {
5150
5295
  return getWasmTableEntry(index)(a1,a2);
5151
5296
  } catch(e) {
5152
5297
  stackRestore(sp);
5153
- if (e !== e+0) throw e;
5298
+ if (!(e instanceof EmscriptenEH)) throw e;
5154
5299
  _setThrew(1, 0);
5155
5300
  }
5156
5301
  }
@@ -5161,7 +5306,7 @@ function invoke_viii(index,a1,a2,a3) {
5161
5306
  getWasmTableEntry(index)(a1,a2,a3);
5162
5307
  } catch(e) {
5163
5308
  stackRestore(sp);
5164
- if (e !== e+0) throw e;
5309
+ if (!(e instanceof EmscriptenEH)) throw e;
5165
5310
  _setThrew(1, 0);
5166
5311
  }
5167
5312
  }
@@ -5172,7 +5317,7 @@ function invoke_vii(index,a1,a2) {
5172
5317
  getWasmTableEntry(index)(a1,a2);
5173
5318
  } catch(e) {
5174
5319
  stackRestore(sp);
5175
- if (e !== e+0) throw e;
5320
+ if (!(e instanceof EmscriptenEH)) throw e;
5176
5321
  _setThrew(1, 0);
5177
5322
  }
5178
5323
  }
@@ -5183,7 +5328,7 @@ function invoke_vi(index,a1) {
5183
5328
  getWasmTableEntry(index)(a1);
5184
5329
  } catch(e) {
5185
5330
  stackRestore(sp);
5186
- if (e !== e+0) throw e;
5331
+ if (!(e instanceof EmscriptenEH)) throw e;
5187
5332
  _setThrew(1, 0);
5188
5333
  }
5189
5334
  }
@@ -5232,65 +5377,47 @@ function stackCheckInit() {
5232
5377
  writeStackCookie();
5233
5378
  }
5234
5379
 
5235
- function run(args = arguments_) {
5236
-
5237
- if (runDependencies > 0) {
5238
- dependenciesFulfilled = run;
5239
- return;
5240
- }
5380
+ async function run(args = programArgs) {
5381
+ assert(!calledRun);
5382
+ calledRun = true;
5241
5383
 
5242
5384
  stackCheckInit();
5243
5385
 
5244
5386
  preRun();
5245
5387
 
5246
- // a preRun added a dependency, run will be called later
5247
- if (runDependencies > 0) {
5248
- dependenciesFulfilled = run;
5249
- return;
5388
+ if (runDependencies) {
5389
+ await resolveRunDependencies();
5250
5390
  }
5251
5391
 
5252
- function doRun() {
5253
- // run may have just been called through dependencies being fulfilled just in this very frame,
5254
- // or while the async setStatus time below was happening
5255
- assert(!calledRun);
5256
- calledRun = true;
5257
- Module['calledRun'] = true;
5258
-
5259
- if (ABORT) return;
5392
+ var setStatus = Module['setStatus'];
5393
+ if (setStatus) {
5394
+ setStatus('Running...');
5395
+ // Yield to the event loop to allow the browser to paint "Running..."
5396
+ await new Promise((resolve) => setTimeout(resolve, 1));
5397
+ // Then we want to clear the status text, but only after the rest of this function runs.
5398
+ setTimeout(setStatus, 1, '');
5399
+ }
5260
5400
 
5261
- initRuntime();
5401
+ if (ABORT) return;
5262
5402
 
5263
- preMain();
5403
+ initRuntime();
5264
5404
 
5265
- Module['onRuntimeInitialized']?.();
5266
- consumedModuleProp('onRuntimeInitialized');
5405
+ // No ATMAINS hooks
5267
5406
 
5268
- var noInitialRun = Module['noInitialRun'] || false;
5269
- if (!noInitialRun) callMain(args);
5407
+ Module['onRuntimeInitialized']?.();
5408
+ consumedModuleProp('onRuntimeInitialized');
5270
5409
 
5271
- postRun();
5272
- }
5410
+ var noInitialRun = Module['noInitialRun'] || false;
5411
+ if (!noInitialRun) callMain(args);
5273
5412
 
5274
- if (Module['setStatus']) {
5275
- Module['setStatus']('Running...');
5276
- setTimeout(() => {
5277
- setTimeout(() => Module['setStatus'](''), 1);
5278
- doRun();
5279
- }, 1);
5280
- } else
5281
- {
5282
- doRun();
5283
- }
5284
- checkStackCookie();
5413
+ postRun();
5285
5414
  }
5286
5415
 
5287
5416
  var wasmExports;
5288
5417
 
5289
5418
  // With async instantation wasmExports is assigned asynchronously when the
5290
5419
  // instance is received.
5291
- createWasm();
5292
-
5293
- run();
5420
+ createWasm().then(() => run());
5294
5421
 
5295
5422
  // end include: postamble.js
5296
5423