@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 +154 -68
- package/dist/startup.js +154 -68
- package/package.json +1 -1
- package/src/startup.ts +192 -84
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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".
|
|
727
|
-
//
|
|
728
|
-
//
|
|
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
|
|
908
|
+
body?: string
|
|
909
|
+
bodyBase64?: string
|
|
770
910
|
ok?: boolean
|
|
771
911
|
}
|
|
772
|
-
const
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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)
|