@mentra/jspolyfill 3.4.0-dev.960 → 3.4.0-dev.965

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/assets/startup.js CHANGED
@@ -436,6 +436,140 @@
436
436
  }
437
437
  ;
438
438
  g.crypto = cryptoNs;
439
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
440
+ const BASE64_VALUES = new Uint8Array(128).fill(255);
441
+ for (let i = 0; i < BASE64_ALPHABET.length; i++) BASE64_VALUES[BASE64_ALPHABET.charCodeAt(i)] = i;
442
+ const CHARS_PER_CHUNK = 8192;
443
+ function bytesOf(data) {
444
+ return data instanceof ArrayBuffer ? new Uint8Array(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
445
+ }
446
+ function bytesToBase64(bytes) {
447
+ const parts = [];
448
+ const chars = [];
449
+ for (let i = 0; i < bytes.length; i += 3) {
450
+ const b1 = bytes[i];
451
+ const b2 = i + 1 < bytes.length ? bytes[i + 1] : 0;
452
+ const b3 = i + 2 < bytes.length ? bytes[i + 2] : 0;
453
+ chars.push(
454
+ BASE64_ALPHABET.charCodeAt(b1 >> 2),
455
+ BASE64_ALPHABET.charCodeAt((b1 & 3) << 4 | b2 >> 4),
456
+ i + 1 < bytes.length ? BASE64_ALPHABET.charCodeAt((b2 & 15) << 2 | b3 >> 6) : 61,
457
+ i + 2 < bytes.length ? BASE64_ALPHABET.charCodeAt(b3 & 63) : 61
458
+ );
459
+ if (chars.length >= CHARS_PER_CHUNK) {
460
+ parts.push(String.fromCharCode.apply(null, chars));
461
+ chars.length = 0;
462
+ }
463
+ }
464
+ parts.push(String.fromCharCode.apply(null, chars));
465
+ return parts.join("");
466
+ }
467
+ function base64ToBytes(base64) {
468
+ const length = base64.length;
469
+ if (length % 4 !== 0) throw new TypeError("Invalid base64 from the native bridge");
470
+ const padding = length === 0 || base64.charCodeAt(length - 1) !== 61 ? 0 : base64.charCodeAt(length - 2) === 61 ? 2 : 1;
471
+ const out = new Uint8Array(length / 4 * 3 - padding);
472
+ const unpadded = padding === 0 ? length : length - 4;
473
+ let codes = 0;
474
+ let values = 0;
475
+ let o = 0;
476
+ for (let i = 0; i < unpadded; i += 4) {
477
+ const c1 = base64.charCodeAt(i);
478
+ const c2 = base64.charCodeAt(i + 1);
479
+ const c3 = base64.charCodeAt(i + 2);
480
+ const c4 = base64.charCodeAt(i + 3);
481
+ const v1 = BASE64_VALUES[c1 & 127];
482
+ const v2 = BASE64_VALUES[c2 & 127];
483
+ const v3 = BASE64_VALUES[c3 & 127];
484
+ const v4 = BASE64_VALUES[c4 & 127];
485
+ codes |= c1 | c2 | c3 | c4;
486
+ values |= v1 | v2 | v3 | v4;
487
+ out[o++] = v1 << 2 | v2 >> 4;
488
+ out[o++] = (v2 & 15) << 4 | v3 >> 2;
489
+ out[o++] = (v3 & 3) << 6 | v4;
490
+ }
491
+ if (padding > 0) {
492
+ const c1 = base64.charCodeAt(unpadded);
493
+ const c2 = base64.charCodeAt(unpadded + 1);
494
+ const c3 = padding === 1 ? base64.charCodeAt(unpadded + 2) : 65;
495
+ const v1 = BASE64_VALUES[c1 & 127];
496
+ const v2 = BASE64_VALUES[c2 & 127];
497
+ const v3 = BASE64_VALUES[c3 & 127];
498
+ codes |= c1 | c2 | c3;
499
+ values |= v1 | v2 | v3;
500
+ out[o++] = v1 << 2 | v2 >> 4;
501
+ if (padding === 1) out[o++] = (v2 & 15) << 4 | v3 >> 2;
502
+ }
503
+ if (codes > 127 || values > 63) throw new TypeError("Invalid base64 from the native bridge");
504
+ return out;
505
+ }
506
+ function decodeUtf8(bytes, fatal, ignoreBOM) {
507
+ const length = bytes.length;
508
+ const parts = [];
509
+ const units = [];
510
+ let i = !ignoreBOM && length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191 ? 3 : 0;
511
+ while (i < length) {
512
+ const asciiStart = i;
513
+ while (i < length && bytes[i] < 128) i++;
514
+ if (i > asciiStart) {
515
+ if (units.length > 0) {
516
+ parts.push(String.fromCharCode.apply(null, units));
517
+ units.length = 0;
518
+ }
519
+ for (let start = asciiStart; start < i; start += CHARS_PER_CHUNK) {
520
+ const chunk = bytes.subarray(start, Math.min(i, start + CHARS_PER_CHUNK));
521
+ parts.push(String.fromCharCode.apply(null, chunk));
522
+ }
523
+ if (i >= length) break;
524
+ }
525
+ const lead = bytes[i++];
526
+ let codePoint;
527
+ let needed;
528
+ let lower = 128;
529
+ let upper = 191;
530
+ if (lead >= 194 && lead <= 223) {
531
+ needed = 1;
532
+ codePoint = lead & 31;
533
+ } else if (lead >= 224 && lead <= 239) {
534
+ if (lead === 224) lower = 160;
535
+ if (lead === 237) upper = 159;
536
+ needed = 2;
537
+ codePoint = lead & 15;
538
+ } else if (lead >= 240 && lead <= 244) {
539
+ if (lead === 240) lower = 144;
540
+ if (lead === 244) upper = 143;
541
+ needed = 3;
542
+ codePoint = lead & 7;
543
+ } else {
544
+ needed = -1;
545
+ codePoint = 0;
546
+ }
547
+ while (needed > 0) {
548
+ const next = i < length ? bytes[i] : -1;
549
+ if (next < lower || next > upper) break;
550
+ codePoint = codePoint << 6 | next & 63;
551
+ lower = 128;
552
+ upper = 191;
553
+ needed--;
554
+ i++;
555
+ }
556
+ if (needed !== 0) {
557
+ if (fatal) throw new TypeError("The encoded data was not valid utf-8");
558
+ units.push(65533);
559
+ } else if (codePoint > 65535) {
560
+ codePoint -= 65536;
561
+ units.push(55296 + (codePoint >> 10), 56320 + (codePoint & 1023));
562
+ } else {
563
+ units.push(codePoint);
564
+ }
565
+ if (units.length >= CHARS_PER_CHUNK) {
566
+ parts.push(String.fromCharCode.apply(null, units));
567
+ units.length = 0;
568
+ }
569
+ }
570
+ if (units.length > 0) parts.push(String.fromCharCode.apply(null, units));
571
+ return parts.join("");
572
+ }
439
573
  if (typeof g.TextEncoder !== "function") {
440
574
  class TextEncoderPolyfill {
441
575
  constructor() {
@@ -476,36 +610,14 @@
476
610
  }
477
611
  if (typeof g.TextDecoder !== "function") {
478
612
  class TextDecoderPolyfill {
479
- constructor(encoding = "utf-8") {
613
+ constructor(encoding = "utf-8", options) {
480
614
  this.encoding = encoding;
615
+ this.fatal = options?.fatal === true;
616
+ this.ignoreBOM = options?.ignoreBOM === true;
481
617
  }
482
618
  decode(buffer) {
483
619
  if (!buffer) return "";
484
- const bytes = buffer instanceof Uint8Array ? buffer : buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
485
- let out = "";
486
- let i = 0;
487
- while (i < bytes.length) {
488
- const b1 = bytes[i++];
489
- let code;
490
- if (b1 < 128) {
491
- code = b1;
492
- } else if (b1 < 192) {
493
- code = 65533;
494
- } else if (b1 < 224) {
495
- code = (b1 & 31) << 6 | bytes[i++] & 63;
496
- } else if (b1 < 240) {
497
- code = (b1 & 15) << 12 | (bytes[i++] & 63) << 6 | bytes[i++] & 63;
498
- } else {
499
- code = (b1 & 7) << 18 | (bytes[i++] & 63) << 12 | (bytes[i++] & 63) << 6 | bytes[i++] & 63;
500
- }
501
- if (code > 65535) {
502
- code -= 65536;
503
- out += String.fromCharCode(55296 + (code >> 10), 56320 + (code & 1023));
504
- } else {
505
- out += String.fromCharCode(code);
506
- }
507
- }
508
- return out;
620
+ return decodeUtf8(bytesOf(buffer), this.fatal, this.ignoreBOM);
509
621
  }
510
622
  }
511
623
  ;
@@ -552,9 +664,12 @@
552
664
  const url = typeof input === "string" ? input : input.toString();
553
665
  const method = (init?.method ?? "GET").toUpperCase();
554
666
  let bodyString = null;
667
+ let bodyBase64;
555
668
  const reqBody = init?.body;
556
669
  if (typeof reqBody === "string") {
557
670
  bodyString = reqBody;
671
+ } else if (reqBody instanceof ArrayBuffer || ArrayBuffer.isView(reqBody)) {
672
+ bodyBase64 = bytesToBase64(bytesOf(reqBody));
558
673
  } else if (reqBody && typeof reqBody.toString === "function") {
559
674
  bodyString = reqBody.toString();
560
675
  }
@@ -578,9 +693,10 @@
578
693
  }
579
694
  const sendRequest = g.__mentraSendRequest;
580
695
  const result = await sendRequest("fetch", "request", [
581
- { url, method, headers, body: bodyString }
696
+ bodyBase64 === void 0 ? { url, method, headers, body: bodyString } : { url, method, headers, bodyBase64 }
582
697
  ]);
583
- const bodyText = typeof result.body === "string" ? result.body : "";
698
+ const responseText = typeof result.body === "string" ? result.body : null;
699
+ const responseBase64 = typeof result.bodyBase64 === "string" ? result.bodyBase64 : "";
584
700
  const responseHeaders = new Map(Object.entries(result.headers ?? {}));
585
701
  class ResponseLike {
586
702
  constructor() {
@@ -597,11 +713,13 @@
597
713
  };
598
714
  }
599
715
  async text() {
600
- return bodyText;
716
+ if (responseText === null) return decodeUtf8(base64ToBytes(responseBase64), false, false);
717
+ return responseText.charCodeAt(0) === 65279 ? responseText.slice(1) : responseText;
601
718
  }
602
719
  async json() {
720
+ const text = await this.text();
603
721
  try {
604
- return JSON.parse(bodyText);
722
+ return JSON.parse(text);
605
723
  } catch (e) {
606
724
  const err = new Error(
607
725
  `Failed to parse JSON response from ${url}: ${e instanceof Error ? e.message : String(e)}`
@@ -611,52 +729,21 @@
611
729
  }
612
730
  }
613
731
  async arrayBuffer() {
732
+ if (responseText === null) return base64ToBytes(responseBase64).buffer;
614
733
  const enc = new g.TextEncoder();
615
- return enc.encode(bodyText).buffer;
734
+ return enc.encode(responseText).buffer;
616
735
  }
617
736
  }
618
737
  return new ResponseLike();
619
738
  };
620
739
  }
621
740
  if (typeof g.WebSocket !== "function") {
622
- let bytesToBase642 = function(bytes) {
623
- const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
624
- let out = "";
625
- let i = 0;
626
- while (i < bytes.length) {
627
- const c1 = bytes[i++];
628
- const c2 = i < bytes.length ? bytes[i++] : NaN;
629
- const c3 = i < bytes.length ? bytes[i++] : NaN;
630
- const e1 = c1 >> 2;
631
- const e2 = (c1 & 3) << 4 | (Number.isNaN(c2) ? 0 : c2 >> 4);
632
- const e3 = Number.isNaN(c2) ? 64 : (c2 & 15) << 2 | (Number.isNaN(c3) ? 0 : c3 >> 6);
633
- const e4 = Number.isNaN(c3) ? 64 : c3 & 63;
634
- out += b64[e1] + b64[e2] + (e3 === 64 ? "=" : b64[e3]) + (e4 === 64 ? "=" : b64[e4]);
635
- }
636
- return out;
637
- }, base64ToBytes2 = function(s) {
638
- const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
639
- const clean = s.replace(/=+$/, "");
640
- const out = [];
641
- let buf = 0;
642
- let bits = 0;
643
- for (let i = 0; i < clean.length; i++) {
644
- const idx = b64.indexOf(clean[i]);
645
- if (idx < 0) continue;
646
- buf = buf << 6 | idx;
647
- bits += 6;
648
- if (bits >= 8) {
649
- bits -= 8;
650
- out.push(buf >> bits & 255);
651
- }
652
- }
653
- return new Uint8Array(out);
654
- }, queueMicrotaskSafe2 = function(fn) {
741
+ let queueMicrotaskSafe2 = function(fn) {
655
742
  const q = g.queueMicrotask;
656
743
  if (typeof q === "function") q(fn);
657
744
  else g.Promise.resolve().then(fn);
658
745
  };
659
- var bytesToBase64 = bytesToBase642, base64ToBytes = base64ToBytes2, queueMicrotaskSafe = queueMicrotaskSafe2;
746
+ var queueMicrotaskSafe = queueMicrotaskSafe2;
660
747
  const sockets = /* @__PURE__ */ new Map();
661
748
  g.__mentraDeliverWebSocketEvent = (sid, type, payload) => {
662
749
  const sock = sockets.get(sid);
@@ -718,8 +805,7 @@
718
805
  payload = data;
719
806
  } else {
720
807
  kind = "binary";
721
- const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
722
- payload = bytesToBase642(bytes);
808
+ payload = bytesToBase64(bytesOf(data));
723
809
  }
724
810
  try {
725
811
  __dispatch("ws", "send", JSON.stringify([{ sid: this.sid, kind, payload }]));
@@ -752,7 +838,7 @@
752
838
  }
753
839
  const synth = { type, target: this, ...ev };
754
840
  if (type === "message" && ev.kind === "binary" && typeof ev.data === "string") {
755
- synth.data = base64ToBytes2(ev.data).buffer;
841
+ synth.data = base64ToBytes(ev.data).buffer;
756
842
  } else if (type === "message" && ev.kind === "text") {
757
843
  synth.data = ev.data;
758
844
  }
package/dist/startup.js CHANGED
@@ -436,6 +436,140 @@
436
436
  }
437
437
  ;
438
438
  g.crypto = cryptoNs;
439
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
440
+ const BASE64_VALUES = new Uint8Array(128).fill(255);
441
+ for (let i = 0; i < BASE64_ALPHABET.length; i++) BASE64_VALUES[BASE64_ALPHABET.charCodeAt(i)] = i;
442
+ const CHARS_PER_CHUNK = 8192;
443
+ function bytesOf(data) {
444
+ return data instanceof ArrayBuffer ? new Uint8Array(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
445
+ }
446
+ function bytesToBase64(bytes) {
447
+ const parts = [];
448
+ const chars = [];
449
+ for (let i = 0; i < bytes.length; i += 3) {
450
+ const b1 = bytes[i];
451
+ const b2 = i + 1 < bytes.length ? bytes[i + 1] : 0;
452
+ const b3 = i + 2 < bytes.length ? bytes[i + 2] : 0;
453
+ chars.push(
454
+ BASE64_ALPHABET.charCodeAt(b1 >> 2),
455
+ BASE64_ALPHABET.charCodeAt((b1 & 3) << 4 | b2 >> 4),
456
+ i + 1 < bytes.length ? BASE64_ALPHABET.charCodeAt((b2 & 15) << 2 | b3 >> 6) : 61,
457
+ i + 2 < bytes.length ? BASE64_ALPHABET.charCodeAt(b3 & 63) : 61
458
+ );
459
+ if (chars.length >= CHARS_PER_CHUNK) {
460
+ parts.push(String.fromCharCode.apply(null, chars));
461
+ chars.length = 0;
462
+ }
463
+ }
464
+ parts.push(String.fromCharCode.apply(null, chars));
465
+ return parts.join("");
466
+ }
467
+ function base64ToBytes(base64) {
468
+ const length = base64.length;
469
+ if (length % 4 !== 0) throw new TypeError("Invalid base64 from the native bridge");
470
+ const padding = length === 0 || base64.charCodeAt(length - 1) !== 61 ? 0 : base64.charCodeAt(length - 2) === 61 ? 2 : 1;
471
+ const out = new Uint8Array(length / 4 * 3 - padding);
472
+ const unpadded = padding === 0 ? length : length - 4;
473
+ let codes = 0;
474
+ let values = 0;
475
+ let o = 0;
476
+ for (let i = 0; i < unpadded; i += 4) {
477
+ const c1 = base64.charCodeAt(i);
478
+ const c2 = base64.charCodeAt(i + 1);
479
+ const c3 = base64.charCodeAt(i + 2);
480
+ const c4 = base64.charCodeAt(i + 3);
481
+ const v1 = BASE64_VALUES[c1 & 127];
482
+ const v2 = BASE64_VALUES[c2 & 127];
483
+ const v3 = BASE64_VALUES[c3 & 127];
484
+ const v4 = BASE64_VALUES[c4 & 127];
485
+ codes |= c1 | c2 | c3 | c4;
486
+ values |= v1 | v2 | v3 | v4;
487
+ out[o++] = v1 << 2 | v2 >> 4;
488
+ out[o++] = (v2 & 15) << 4 | v3 >> 2;
489
+ out[o++] = (v3 & 3) << 6 | v4;
490
+ }
491
+ if (padding > 0) {
492
+ const c1 = base64.charCodeAt(unpadded);
493
+ const c2 = base64.charCodeAt(unpadded + 1);
494
+ const c3 = padding === 1 ? base64.charCodeAt(unpadded + 2) : 65;
495
+ const v1 = BASE64_VALUES[c1 & 127];
496
+ const v2 = BASE64_VALUES[c2 & 127];
497
+ const v3 = BASE64_VALUES[c3 & 127];
498
+ codes |= c1 | c2 | c3;
499
+ values |= v1 | v2 | v3;
500
+ out[o++] = v1 << 2 | v2 >> 4;
501
+ if (padding === 1) out[o++] = (v2 & 15) << 4 | v3 >> 2;
502
+ }
503
+ if (codes > 127 || values > 63) throw new TypeError("Invalid base64 from the native bridge");
504
+ return out;
505
+ }
506
+ function decodeUtf8(bytes, fatal, ignoreBOM) {
507
+ const length = bytes.length;
508
+ const parts = [];
509
+ const units = [];
510
+ let i = !ignoreBOM && length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191 ? 3 : 0;
511
+ while (i < length) {
512
+ const asciiStart = i;
513
+ while (i < length && bytes[i] < 128) i++;
514
+ if (i > asciiStart) {
515
+ if (units.length > 0) {
516
+ parts.push(String.fromCharCode.apply(null, units));
517
+ units.length = 0;
518
+ }
519
+ for (let start = asciiStart; start < i; start += CHARS_PER_CHUNK) {
520
+ const chunk = bytes.subarray(start, Math.min(i, start + CHARS_PER_CHUNK));
521
+ parts.push(String.fromCharCode.apply(null, chunk));
522
+ }
523
+ if (i >= length) break;
524
+ }
525
+ const lead = bytes[i++];
526
+ let codePoint;
527
+ let needed;
528
+ let lower = 128;
529
+ let upper = 191;
530
+ if (lead >= 194 && lead <= 223) {
531
+ needed = 1;
532
+ codePoint = lead & 31;
533
+ } else if (lead >= 224 && lead <= 239) {
534
+ if (lead === 224) lower = 160;
535
+ if (lead === 237) upper = 159;
536
+ needed = 2;
537
+ codePoint = lead & 15;
538
+ } else if (lead >= 240 && lead <= 244) {
539
+ if (lead === 240) lower = 144;
540
+ if (lead === 244) upper = 143;
541
+ needed = 3;
542
+ codePoint = lead & 7;
543
+ } else {
544
+ needed = -1;
545
+ codePoint = 0;
546
+ }
547
+ while (needed > 0) {
548
+ const next = i < length ? bytes[i] : -1;
549
+ if (next < lower || next > upper) break;
550
+ codePoint = codePoint << 6 | next & 63;
551
+ lower = 128;
552
+ upper = 191;
553
+ needed--;
554
+ i++;
555
+ }
556
+ if (needed !== 0) {
557
+ if (fatal) throw new TypeError("The encoded data was not valid utf-8");
558
+ units.push(65533);
559
+ } else if (codePoint > 65535) {
560
+ codePoint -= 65536;
561
+ units.push(55296 + (codePoint >> 10), 56320 + (codePoint & 1023));
562
+ } else {
563
+ units.push(codePoint);
564
+ }
565
+ if (units.length >= CHARS_PER_CHUNK) {
566
+ parts.push(String.fromCharCode.apply(null, units));
567
+ units.length = 0;
568
+ }
569
+ }
570
+ if (units.length > 0) parts.push(String.fromCharCode.apply(null, units));
571
+ return parts.join("");
572
+ }
439
573
  if (typeof g.TextEncoder !== "function") {
440
574
  class TextEncoderPolyfill {
441
575
  constructor() {
@@ -476,36 +610,14 @@
476
610
  }
477
611
  if (typeof g.TextDecoder !== "function") {
478
612
  class TextDecoderPolyfill {
479
- constructor(encoding = "utf-8") {
613
+ constructor(encoding = "utf-8", options) {
480
614
  this.encoding = encoding;
615
+ this.fatal = options?.fatal === true;
616
+ this.ignoreBOM = options?.ignoreBOM === true;
481
617
  }
482
618
  decode(buffer) {
483
619
  if (!buffer) return "";
484
- const bytes = buffer instanceof Uint8Array ? buffer : buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
485
- let out = "";
486
- let i = 0;
487
- while (i < bytes.length) {
488
- const b1 = bytes[i++];
489
- let code;
490
- if (b1 < 128) {
491
- code = b1;
492
- } else if (b1 < 192) {
493
- code = 65533;
494
- } else if (b1 < 224) {
495
- code = (b1 & 31) << 6 | bytes[i++] & 63;
496
- } else if (b1 < 240) {
497
- code = (b1 & 15) << 12 | (bytes[i++] & 63) << 6 | bytes[i++] & 63;
498
- } else {
499
- code = (b1 & 7) << 18 | (bytes[i++] & 63) << 12 | (bytes[i++] & 63) << 6 | bytes[i++] & 63;
500
- }
501
- if (code > 65535) {
502
- code -= 65536;
503
- out += String.fromCharCode(55296 + (code >> 10), 56320 + (code & 1023));
504
- } else {
505
- out += String.fromCharCode(code);
506
- }
507
- }
508
- return out;
620
+ return decodeUtf8(bytesOf(buffer), this.fatal, this.ignoreBOM);
509
621
  }
510
622
  }
511
623
  ;
@@ -552,9 +664,12 @@
552
664
  const url = typeof input === "string" ? input : input.toString();
553
665
  const method = (init?.method ?? "GET").toUpperCase();
554
666
  let bodyString = null;
667
+ let bodyBase64;
555
668
  const reqBody = init?.body;
556
669
  if (typeof reqBody === "string") {
557
670
  bodyString = reqBody;
671
+ } else if (reqBody instanceof ArrayBuffer || ArrayBuffer.isView(reqBody)) {
672
+ bodyBase64 = bytesToBase64(bytesOf(reqBody));
558
673
  } else if (reqBody && typeof reqBody.toString === "function") {
559
674
  bodyString = reqBody.toString();
560
675
  }
@@ -578,9 +693,10 @@
578
693
  }
579
694
  const sendRequest = g.__mentraSendRequest;
580
695
  const result = await sendRequest("fetch", "request", [
581
- { url, method, headers, body: bodyString }
696
+ bodyBase64 === void 0 ? { url, method, headers, body: bodyString } : { url, method, headers, bodyBase64 }
582
697
  ]);
583
- const bodyText = typeof result.body === "string" ? result.body : "";
698
+ const responseText = typeof result.body === "string" ? result.body : null;
699
+ const responseBase64 = typeof result.bodyBase64 === "string" ? result.bodyBase64 : "";
584
700
  const responseHeaders = new Map(Object.entries(result.headers ?? {}));
585
701
  class ResponseLike {
586
702
  constructor() {
@@ -597,11 +713,13 @@
597
713
  };
598
714
  }
599
715
  async text() {
600
- return bodyText;
716
+ if (responseText === null) return decodeUtf8(base64ToBytes(responseBase64), false, false);
717
+ return responseText.charCodeAt(0) === 65279 ? responseText.slice(1) : responseText;
601
718
  }
602
719
  async json() {
720
+ const text = await this.text();
603
721
  try {
604
- return JSON.parse(bodyText);
722
+ return JSON.parse(text);
605
723
  } catch (e) {
606
724
  const err = new Error(
607
725
  `Failed to parse JSON response from ${url}: ${e instanceof Error ? e.message : String(e)}`
@@ -611,52 +729,21 @@
611
729
  }
612
730
  }
613
731
  async arrayBuffer() {
732
+ if (responseText === null) return base64ToBytes(responseBase64).buffer;
614
733
  const enc = new g.TextEncoder();
615
- return enc.encode(bodyText).buffer;
734
+ return enc.encode(responseText).buffer;
616
735
  }
617
736
  }
618
737
  return new ResponseLike();
619
738
  };
620
739
  }
621
740
  if (typeof g.WebSocket !== "function") {
622
- let bytesToBase642 = function(bytes) {
623
- const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
624
- let out = "";
625
- let i = 0;
626
- while (i < bytes.length) {
627
- const c1 = bytes[i++];
628
- const c2 = i < bytes.length ? bytes[i++] : NaN;
629
- const c3 = i < bytes.length ? bytes[i++] : NaN;
630
- const e1 = c1 >> 2;
631
- const e2 = (c1 & 3) << 4 | (Number.isNaN(c2) ? 0 : c2 >> 4);
632
- const e3 = Number.isNaN(c2) ? 64 : (c2 & 15) << 2 | (Number.isNaN(c3) ? 0 : c3 >> 6);
633
- const e4 = Number.isNaN(c3) ? 64 : c3 & 63;
634
- out += b64[e1] + b64[e2] + (e3 === 64 ? "=" : b64[e3]) + (e4 === 64 ? "=" : b64[e4]);
635
- }
636
- return out;
637
- }, base64ToBytes2 = function(s) {
638
- const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
639
- const clean = s.replace(/=+$/, "");
640
- const out = [];
641
- let buf = 0;
642
- let bits = 0;
643
- for (let i = 0; i < clean.length; i++) {
644
- const idx = b64.indexOf(clean[i]);
645
- if (idx < 0) continue;
646
- buf = buf << 6 | idx;
647
- bits += 6;
648
- if (bits >= 8) {
649
- bits -= 8;
650
- out.push(buf >> bits & 255);
651
- }
652
- }
653
- return new Uint8Array(out);
654
- }, queueMicrotaskSafe2 = function(fn) {
741
+ let queueMicrotaskSafe2 = function(fn) {
655
742
  const q = g.queueMicrotask;
656
743
  if (typeof q === "function") q(fn);
657
744
  else g.Promise.resolve().then(fn);
658
745
  };
659
- var bytesToBase64 = bytesToBase642, base64ToBytes = base64ToBytes2, queueMicrotaskSafe = queueMicrotaskSafe2;
746
+ var queueMicrotaskSafe = queueMicrotaskSafe2;
660
747
  const sockets = /* @__PURE__ */ new Map();
661
748
  g.__mentraDeliverWebSocketEvent = (sid, type, payload) => {
662
749
  const sock = sockets.get(sid);
@@ -718,8 +805,7 @@
718
805
  payload = data;
719
806
  } else {
720
807
  kind = "binary";
721
- const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
722
- payload = bytesToBase642(bytes);
808
+ payload = bytesToBase64(bytesOf(data));
723
809
  }
724
810
  try {
725
811
  __dispatch("ws", "send", JSON.stringify([{ sid: this.sid, kind, payload }]));
@@ -752,7 +838,7 @@
752
838
  }
753
839
  const synth = { type, target: this, ...ev };
754
840
  if (type === "message" && ev.kind === "binary" && typeof ev.data === "string") {
755
- synth.data = base64ToBytes2(ev.data).buffer;
841
+ synth.data = base64ToBytes(ev.data).buffer;
756
842
  } else if (type === "message" && ev.kind === "text") {
757
843
  synth.data = ev.data;
758
844
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/jspolyfill",
3
- "version": "3.4.0-dev.960",
3
+ "version": "3.4.0-dev.965",
4
4
  "description": "MentraJS polyfill bundle — installed into each per-miniapp JSContext (iOS-JSC, Android-QuickJS) at spawn time. Provides browser-like globals (console, timers, fetch, WebSocket, localStorage, crypto.subtle) over a single __dispatch native bridge, plus the __deliver / signalReady plumbing the host runtime relies on.",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
package/src/startup.ts CHANGED
@@ -598,6 +598,166 @@ declare const __nativeClearTimer: (token: number) => void
598
598
  }
599
599
  ;(g as Record<string, unknown>).crypto = cryptoNs
600
600
 
601
+ // ---------- bytes over the JSON bridge -----------------------------------
602
+ // __dispatch and __deliver carry JSON text only, so binary data (fetch
603
+ // bodies that are not UTF-8 text, WebSocket binary frames) crosses as
604
+ // base64 and is decoded here.
605
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
606
+ // Values 0-63; 0xff marks a character outside the alphabet.
607
+ const BASE64_VALUES = new Uint8Array(128).fill(0xff)
608
+ for (let i = 0; i < BASE64_ALPHABET.length; i++) BASE64_VALUES[BASE64_ALPHABET.charCodeAt(i)] = i
609
+ // String.fromCharCode.apply takes one argument per code unit and engines
610
+ // cap argument counts near 64K, so strings are built from bounded chunks.
611
+ const CHARS_PER_CHUNK = 8192
612
+
613
+ function bytesOf(data: ArrayBuffer | ArrayBufferView): Uint8Array {
614
+ return data instanceof ArrayBuffer
615
+ ? new Uint8Array(data)
616
+ : new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
617
+ }
618
+
619
+ function bytesToBase64(bytes: Uint8Array): string {
620
+ const parts: string[] = []
621
+ const chars: number[] = []
622
+ for (let i = 0; i < bytes.length; i += 3) {
623
+ const b1 = bytes[i]!
624
+ const b2 = i + 1 < bytes.length ? bytes[i + 1]! : 0
625
+ const b3 = i + 2 < bytes.length ? bytes[i + 2]! : 0
626
+ chars.push(
627
+ BASE64_ALPHABET.charCodeAt(b1 >> 2),
628
+ BASE64_ALPHABET.charCodeAt(((b1 & 3) << 4) | (b2 >> 4)),
629
+ i + 1 < bytes.length ? BASE64_ALPHABET.charCodeAt(((b2 & 15) << 2) | (b3 >> 6)) : 0x3d,
630
+ i + 2 < bytes.length ? BASE64_ALPHABET.charCodeAt(b3 & 63) : 0x3d,
631
+ )
632
+ if (chars.length >= CHARS_PER_CHUNK) {
633
+ parts.push(String.fromCharCode.apply(null, chars))
634
+ chars.length = 0
635
+ }
636
+ }
637
+ parts.push(String.fromCharCode.apply(null, chars))
638
+ return parts.join("")
639
+ }
640
+
641
+ /** Strict: the native bridges always emit padded RFC 4648 base64. */
642
+ function base64ToBytes(base64: string): Uint8Array {
643
+ const length = base64.length
644
+ if (length % 4 !== 0) throw new TypeError("Invalid base64 from the native bridge")
645
+ const padding =
646
+ length === 0 || base64.charCodeAt(length - 1) !== 0x3d ? 0 : base64.charCodeAt(length - 2) === 0x3d ? 2 : 1
647
+ const out = new Uint8Array((length / 4) * 3 - padding)
648
+ // Groups before the padded one decode without per-character branches;
649
+ // the accumulated codes and values are checked once at the end.
650
+ const unpadded = padding === 0 ? length : length - 4
651
+ let codes = 0
652
+ let values = 0
653
+ let o = 0
654
+ for (let i = 0; i < unpadded; i += 4) {
655
+ const c1 = base64.charCodeAt(i)
656
+ const c2 = base64.charCodeAt(i + 1)
657
+ const c3 = base64.charCodeAt(i + 2)
658
+ const c4 = base64.charCodeAt(i + 3)
659
+ const v1 = BASE64_VALUES[c1 & 0x7f]!
660
+ const v2 = BASE64_VALUES[c2 & 0x7f]!
661
+ const v3 = BASE64_VALUES[c3 & 0x7f]!
662
+ const v4 = BASE64_VALUES[c4 & 0x7f]!
663
+ codes |= c1 | c2 | c3 | c4
664
+ values |= v1 | v2 | v3 | v4
665
+ out[o++] = (v1 << 2) | (v2 >> 4)
666
+ out[o++] = ((v2 & 15) << 4) | (v3 >> 2)
667
+ out[o++] = ((v3 & 3) << 6) | v4
668
+ }
669
+ if (padding > 0) {
670
+ const c1 = base64.charCodeAt(unpadded)
671
+ const c2 = base64.charCodeAt(unpadded + 1)
672
+ const c3 = padding === 1 ? base64.charCodeAt(unpadded + 2) : 0x41
673
+ const v1 = BASE64_VALUES[c1 & 0x7f]!
674
+ const v2 = BASE64_VALUES[c2 & 0x7f]!
675
+ const v3 = BASE64_VALUES[c3 & 0x7f]!
676
+ codes |= c1 | c2 | c3
677
+ values |= v1 | v2 | v3
678
+ out[o++] = (v1 << 2) | (v2 >> 4)
679
+ if (padding === 1) out[o++] = ((v2 & 15) << 4) | (v3 >> 2)
680
+ }
681
+ // A non-ASCII code, or a character outside the alphabet (value 0xff).
682
+ if (codes > 0x7f || values > 0x3f) throw new TypeError("Invalid base64 from the native bridge")
683
+ return out
684
+ }
685
+
686
+ /**
687
+ * The WHATWG Encoding Standard's UTF-8 decoder: each maximal malformed
688
+ * subsequence becomes one U+FFFD, or throws when `fatal` is set. Runs of
689
+ * ASCII, most of any JSON body, are copied straight from the bytes.
690
+ */
691
+ function decodeUtf8(bytes: Uint8Array, fatal: boolean, ignoreBOM: boolean): string {
692
+ const length = bytes.length
693
+ const parts: string[] = []
694
+ const units: number[] = []
695
+ let i = !ignoreBOM && length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf ? 3 : 0
696
+ while (i < length) {
697
+ const asciiStart = i
698
+ while (i < length && bytes[i]! < 0x80) i++
699
+ if (i > asciiStart) {
700
+ if (units.length > 0) {
701
+ parts.push(String.fromCharCode.apply(null, units))
702
+ units.length = 0
703
+ }
704
+ for (let start = asciiStart; start < i; start += CHARS_PER_CHUNK) {
705
+ const chunk = bytes.subarray(start, Math.min(i, start + CHARS_PER_CHUNK))
706
+ parts.push(String.fromCharCode.apply(null, chunk as unknown as number[]))
707
+ }
708
+ if (i >= length) break
709
+ }
710
+ const lead = bytes[i++]!
711
+ let codePoint: number
712
+ let needed: number
713
+ let lower = 0x80
714
+ let upper = 0xbf
715
+ if (lead >= 0xc2 && lead <= 0xdf) {
716
+ needed = 1
717
+ codePoint = lead & 0x1f
718
+ } else if (lead >= 0xe0 && lead <= 0xef) {
719
+ if (lead === 0xe0) lower = 0xa0
720
+ if (lead === 0xed) upper = 0x9f
721
+ needed = 2
722
+ codePoint = lead & 0x0f
723
+ } else if (lead >= 0xf0 && lead <= 0xf4) {
724
+ if (lead === 0xf0) lower = 0x90
725
+ if (lead === 0xf4) upper = 0x8f
726
+ needed = 3
727
+ codePoint = lead & 0x07
728
+ } else {
729
+ needed = -1
730
+ codePoint = 0
731
+ }
732
+ while (needed > 0) {
733
+ const next = i < length ? bytes[i]! : -1
734
+ // A byte outside the allowed range ends the sequence unconsumed, so
735
+ // it is decoded again as the start of the next one.
736
+ if (next < lower || next > upper) break
737
+ codePoint = (codePoint << 6) | (next & 0x3f)
738
+ lower = 0x80
739
+ upper = 0xbf
740
+ needed--
741
+ i++
742
+ }
743
+ if (needed !== 0) {
744
+ if (fatal) throw new TypeError("The encoded data was not valid utf-8")
745
+ units.push(0xfffd)
746
+ } else if (codePoint > 0xffff) {
747
+ codePoint -= 0x10000
748
+ units.push(0xd800 + (codePoint >> 10), 0xdc00 + (codePoint & 0x3ff))
749
+ } else {
750
+ units.push(codePoint)
751
+ }
752
+ if (units.length >= CHARS_PER_CHUNK) {
753
+ parts.push(String.fromCharCode.apply(null, units))
754
+ units.length = 0
755
+ }
756
+ }
757
+ if (units.length > 0) parts.push(String.fromCharCode.apply(null, units))
758
+ return parts.join("")
759
+ }
760
+
601
761
  // ---------- TextEncoder / TextDecoder ------------------------------------
602
762
  // Both engines lack these. Tiny implementations sufficient for the SDK's
603
763
  // existing usage (UTF-8 only; we don't expose stream encoding because the
@@ -640,46 +800,16 @@ declare const __nativeClearTimer: (token: number) => void
640
800
  if (typeof (g as Record<string, unknown>).TextDecoder !== "function") {
641
801
  class TextDecoderPolyfill {
642
802
  readonly encoding: string
643
- constructor(encoding = "utf-8") {
803
+ readonly fatal: boolean
804
+ readonly ignoreBOM: boolean
805
+ constructor(encoding = "utf-8", options?: {fatal?: boolean; ignoreBOM?: boolean}) {
644
806
  this.encoding = encoding
807
+ this.fatal = options?.fatal === true
808
+ this.ignoreBOM = options?.ignoreBOM === true
645
809
  }
646
810
  decode(buffer?: ArrayBuffer | ArrayBufferView | null): string {
647
811
  if (!buffer) return ""
648
- const bytes =
649
- buffer instanceof Uint8Array
650
- ? buffer
651
- : buffer instanceof ArrayBuffer
652
- ? new Uint8Array(buffer)
653
- : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)
654
- let out = ""
655
- let i = 0
656
- while (i < bytes.length) {
657
- const b1 = bytes[i++]!
658
- let code: number
659
- if (b1 < 0x80) {
660
- code = b1
661
- } else if (b1 < 0xc0) {
662
- // Invalid start byte; emit replacement character.
663
- code = 0xfffd
664
- } else if (b1 < 0xe0) {
665
- code = ((b1 & 0x1f) << 6) | (bytes[i++]! & 0x3f)
666
- } else if (b1 < 0xf0) {
667
- code = ((b1 & 0x0f) << 12) | ((bytes[i++]! & 0x3f) << 6) | (bytes[i++]! & 0x3f)
668
- } else {
669
- code =
670
- ((b1 & 0x07) << 18) |
671
- ((bytes[i++]! & 0x3f) << 12) |
672
- ((bytes[i++]! & 0x3f) << 6) |
673
- (bytes[i++]! & 0x3f)
674
- }
675
- if (code > 0xffff) {
676
- code -= 0x10000
677
- out += String.fromCharCode(0xd800 + (code >> 10), 0xdc00 + (code & 0x3ff))
678
- } else {
679
- out += String.fromCharCode(code)
680
- }
681
- }
682
- return out
812
+ return decodeUtf8(bytesOf(buffer), this.fatal, this.ignoreBOM)
683
813
  }
684
814
  }
685
815
  ;(g as Record<string, unknown>).TextDecoder = TextDecoderPolyfill
@@ -723,17 +853,26 @@ declare const __nativeClearTimer: (token: number) => void
723
853
  }
724
854
 
725
855
  // ---------- fetch (thin native bridge) -----------------------------------
726
- // Async — uses __mentraSendRequest under the hood with iface "fetch". The
727
- // native handler is expected to return {status, statusText, headers, body}
728
- // where body is either a JSON-encodable value or a base64 string.
856
+ // Async — uses __mentraSendRequest under the hood with iface "fetch".
857
+ // Bodies cross in either direction as `body`, a string whose UTF-8
858
+ // encoding is exactly the body, or `bodyBase64`, the bytes in base64:
859
+ // request {url, method, headers, body | bodyBase64}
860
+ // response {status, statusText, headers, ok, body | bodyBase64}
861
+ // Requests use bodyBase64 for ArrayBuffer and typed-array bodies; native
862
+ // responds with bodyBase64 whenever the bytes are not exactly UTF-8 text.
863
+ // Either way arrayBuffer() is exact and text() follows the Fetch
864
+ // Standard's UTF-8 decode.
729
865
  if (typeof (g as Record<string, unknown>).fetch !== "function") {
730
866
  ;(g as Record<string, unknown>).fetch = async (input: string | URL, init?: RequestInit) => {
731
867
  const url = typeof input === "string" ? input : input.toString()
732
868
  const method = (init?.method ?? "GET").toUpperCase()
733
869
  let bodyString: string | null = null
870
+ let bodyBase64: string | undefined
734
871
  const reqBody = init?.body as unknown
735
872
  if (typeof reqBody === "string") {
736
873
  bodyString = reqBody
874
+ } else if (reqBody instanceof ArrayBuffer || ArrayBuffer.isView(reqBody)) {
875
+ bodyBase64 = bytesToBase64(bytesOf(reqBody))
737
876
  } else if (reqBody && typeof (reqBody as {toString?: () => string}).toString === "function") {
738
877
  bodyString = (reqBody as {toString: () => string}).toString()
739
878
  }
@@ -761,15 +900,17 @@ declare const __nativeClearTimer: (token: number) => void
761
900
  args: unknown[],
762
901
  ) => Promise<unknown>
763
902
  const result = (await sendRequest("fetch", "request", [
764
- {url, method, headers, body: bodyString},
903
+ bodyBase64 === undefined ? {url, method, headers, body: bodyString} : {url, method, headers, bodyBase64},
765
904
  ])) as {
766
905
  status: number
767
906
  statusText?: string
768
907
  headers?: Record<string, string>
769
- body?: string | null
908
+ body?: string
909
+ bodyBase64?: string
770
910
  ok?: boolean
771
911
  }
772
- const bodyText = typeof result.body === "string" ? result.body : ""
912
+ const responseText = typeof result.body === "string" ? result.body : null
913
+ const responseBase64 = typeof result.bodyBase64 === "string" ? result.bodyBase64 : ""
773
914
  const responseHeaders = new Map(Object.entries(result.headers ?? {}))
774
915
  // Minimal Response polyfill — enough for SDK usage.
775
916
  class ResponseLike {
@@ -785,11 +926,14 @@ declare const __nativeClearTimer: (token: number) => void
785
926
  },
786
927
  }
787
928
  async text(): Promise<string> {
788
- return bodyText
929
+ if (responseText === null) return decodeUtf8(base64ToBytes(responseBase64), false, false)
930
+ // UTF-8 decode drops a leading byte order mark.
931
+ return responseText.charCodeAt(0) === 0xfeff ? responseText.slice(1) : responseText
789
932
  }
790
933
  async json(): Promise<unknown> {
934
+ const text = await this.text()
791
935
  try {
792
- return JSON.parse(bodyText)
936
+ return JSON.parse(text)
793
937
  } catch (e) {
794
938
  // Match the browser fetch().json() behaviour — a SyntaxError
795
939
  // is thrown that names the response in its message so the
@@ -804,8 +948,10 @@ declare const __nativeClearTimer: (token: number) => void
804
948
  }
805
949
  }
806
950
  async arrayBuffer(): Promise<ArrayBuffer> {
951
+ if (responseText === null) return base64ToBytes(responseBase64).buffer as ArrayBuffer
952
+ // Exact: native sends `body` only when this re-encoding is the body.
807
953
  const enc = new (g as unknown as {TextEncoder: new () => TextEncoder}).TextEncoder()
808
- return enc.encode(bodyText).buffer as ArrayBuffer
954
+ return enc.encode(responseText).buffer as ArrayBuffer
809
955
  }
810
956
  }
811
957
  return new ResponseLike()
@@ -910,11 +1056,7 @@ declare const __nativeClearTimer: (token: number) => void
910
1056
  payload = data
911
1057
  } else {
912
1058
  kind = "binary"
913
- const bytes =
914
- data instanceof ArrayBuffer
915
- ? new Uint8Array(data)
916
- : new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
917
- payload = bytesToBase64(bytes)
1059
+ payload = bytesToBase64(bytesOf(data))
918
1060
  }
919
1061
  // bufferedAmount is intentionally a static 0 — native owns the
920
1062
  // real queue (URLSessionWebSocketTask / OkHttp); reading it
@@ -1006,40 +1148,6 @@ declare const __nativeClearTimer: (token: number) => void
1006
1148
  }
1007
1149
  }
1008
1150
 
1009
- function bytesToBase64(bytes: Uint8Array): string {
1010
- const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
1011
- let out = ""
1012
- let i = 0
1013
- while (i < bytes.length) {
1014
- const c1 = bytes[i++]!
1015
- const c2 = i < bytes.length ? bytes[i++]! : NaN
1016
- const c3 = i < bytes.length ? bytes[i++]! : NaN
1017
- const e1 = c1 >> 2
1018
- const e2 = ((c1 & 3) << 4) | (Number.isNaN(c2) ? 0 : c2 >> 4)
1019
- const e3 = Number.isNaN(c2) ? 64 : ((c2 & 15) << 2) | (Number.isNaN(c3) ? 0 : c3 >> 6)
1020
- const e4 = Number.isNaN(c3) ? 64 : c3 & 63
1021
- out += b64[e1]! + b64[e2]! + (e3 === 64 ? "=" : b64[e3]!) + (e4 === 64 ? "=" : b64[e4]!)
1022
- }
1023
- return out
1024
- }
1025
- function base64ToBytes(s: string): Uint8Array {
1026
- const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
1027
- const clean = s.replace(/=+$/, "")
1028
- const out: number[] = []
1029
- let buf = 0
1030
- let bits = 0
1031
- for (let i = 0; i < clean.length; i++) {
1032
- const idx = b64.indexOf(clean[i]!)
1033
- if (idx < 0) continue
1034
- buf = (buf << 6) | idx
1035
- bits += 6
1036
- if (bits >= 8) {
1037
- bits -= 8
1038
- out.push((buf >> bits) & 0xff)
1039
- }
1040
- }
1041
- return new Uint8Array(out)
1042
- }
1043
1151
  function queueMicrotaskSafe(fn: () => void): void {
1044
1152
  const q = (g as Record<string, unknown>).queueMicrotask as ((cb: () => void) => void) | undefined
1045
1153
  if (typeof q === "function") q(fn)