@statewalker/webrun-http-browser 0.12.1 → 0.12.2

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/dist/index.js CHANGED
@@ -1747,7 +1747,7 @@ const LAST_CHUNK = /* @__PURE__ */ new Uint8Array([
1747
1747
  13,
1748
1748
  10
1749
1749
  ]);
1750
- const encoder = /* @__PURE__ */ new TextEncoder();
1750
+ const encoder$2 = /* @__PURE__ */ new TextEncoder();
1751
1751
  /**
1752
1752
  * Chunk sizes are written in HEXADECIMAL — a 21-byte chunk is `15`. Writing
1753
1753
  * them in decimal is the defect note 15 found in @libp2p/http, and it is the
@@ -1760,7 +1760,7 @@ const encoder = /* @__PURE__ */ new TextEncoder();
1760
1760
  async function* encodeChunked(body) {
1761
1761
  for await (const chunk of body) {
1762
1762
  if (chunk.byteLength === 0) continue;
1763
- yield encoder.encode(`${chunk.byteLength.toString(16)}\r\n`);
1763
+ yield encoder$2.encode(`${chunk.byteLength.toString(16)}\r\n`);
1764
1764
  yield chunk;
1765
1765
  yield CRLF;
1766
1766
  }
@@ -3331,7 +3331,7 @@ const STREAM_ABORT = "webrun-rpc:stream-abort";
3331
3331
  * request, sent only on a port where both ends agreed `pipeline`. The window's
3332
3332
  * credit is its confirmation.
3333
3333
  */
3334
- const CHUNK = "webrun-rpc:chunk";
3334
+ const CHUNK$1 = "webrun-rpc:chunk";
3335
3335
  /**
3336
3336
  * What a pipelined chunk counts beyond its bytes, against the window, on the
3337
3337
  * receiving side. Both codecs' windows count more for the chunk's envelope
@@ -3662,7 +3662,7 @@ function receiveChunks(port, channelName, controller, touch, notice) {
3662
3662
  const watcher = (event) => {
3663
3663
  const data = event.data;
3664
3664
  if (data?.channelName !== channelName) return;
3665
- const chunk = data.type === CHUNK ? data.chunk : data.type === "request" ? data.params : void 0;
3665
+ const chunk = data.type === CHUNK$1 ? data.chunk : data.type === "request" ? data.params : void 0;
3666
3666
  if (!chunk) return;
3667
3667
  if (!watchPeerEnd()) {
3668
3668
  port.removeEventListener("message", watcher);
@@ -3697,7 +3697,7 @@ function receiveChunks(port, channelName, controller, touch, notice) {
3697
3697
  }, { channelName });
3698
3698
  const onChunk = (event) => {
3699
3699
  const data = event.data;
3700
- if (data?.type !== CHUNK || data.channelName !== channelName || !data.chunk) return;
3700
+ if (data?.type !== CHUNK$1 || data.channelName !== channelName || !data.chunk) return;
3701
3701
  if (poison) return;
3702
3702
  const agreement = portAgreement(port);
3703
3703
  if (!agreement?.caps.has("pipeline")) {
@@ -3810,7 +3810,7 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
3810
3810
  });
3811
3811
  if (mayPipeline(port)) {
3812
3812
  port.postMessage({
3813
- type: CHUNK,
3813
+ type: CHUNK$1,
3814
3814
  channelName,
3815
3815
  chunk
3816
3816
  });
@@ -5104,20 +5104,24 @@ function postFrame(port, frame) {
5104
5104
  function messageSize(data) {
5105
5105
  return typeof data === "string" ? utf8Length(data) : data.byteLength;
5106
5106
  }
5107
- /** The UTF-8 length of `text`, without encoding it. */
5107
+ const encoder = new TextEncoder();
5108
+ /** Characters encoded per `encodeInto` call: at most 3 bytes each, so the scratch stays at 96 KiB. */
5109
+ const CHUNK = 32768;
5110
+ const scratch = new Uint8Array(CHUNK * 3);
5111
+ /**
5112
+ * The UTF-8 length of `text`, as `TextEncoder` would encode it (a lone surrogate counts as the 3
5113
+ * bytes of U+FFFD). Every message is measured on both ends (`bufferedAmount` and the byte acks), so
5114
+ * this runs natively: `encodeInto` a reused buffer, a chunk at a time, never splitting a surrogate
5115
+ * pair across two chunks. A loop over the characters in JavaScript took about 30 times as long.
5116
+ */
5108
5117
  function utf8Length(text) {
5109
5118
  let length = 0;
5110
- for (let i = 0; i < text.length; i++) {
5111
- const code = text.charCodeAt(i);
5112
- if (code < 128) length += 1;
5113
- else if (code < 2048) length += 2;
5114
- else if (code >= 55296 && code <= 56319 && i + 1 < text.length) {
5115
- const next = text.charCodeAt(i + 1);
5116
- if (next >= 56320 && next <= 57343) {
5117
- length += 4;
5118
- i++;
5119
- } else length += 3;
5120
- } else length += 3;
5119
+ for (let start = 0; start < text.length;) {
5120
+ let end = Math.min(start + CHUNK, text.length);
5121
+ const last = text.charCodeAt(end - 1);
5122
+ if (end < text.length && last >= 55296 && last <= 56319) end--;
5123
+ length += encoder.encodeInto(text.slice(start, end), scratch).written;
5124
+ start = end;
5121
5125
  }
5122
5126
  return length;
5123
5127
  }
package/dist/sw.js CHANGED
@@ -706,7 +706,7 @@ const LAST_CHUNK = /* @__PURE__ */ new Uint8Array([
706
706
  13,
707
707
  10
708
708
  ]);
709
- const encoder = /* @__PURE__ */ new TextEncoder();
709
+ const encoder$2 = /* @__PURE__ */ new TextEncoder();
710
710
  /**
711
711
  * Chunk sizes are written in HEXADECIMAL — a 21-byte chunk is `15`. Writing
712
712
  * them in decimal is the defect note 15 found in @libp2p/http, and it is the
@@ -719,7 +719,7 @@ const encoder = /* @__PURE__ */ new TextEncoder();
719
719
  async function* encodeChunked(body) {
720
720
  for await (const chunk of body) {
721
721
  if (chunk.byteLength === 0) continue;
722
- yield encoder.encode(`${chunk.byteLength.toString(16)}\r\n`);
722
+ yield encoder$2.encode(`${chunk.byteLength.toString(16)}\r\n`);
723
723
  yield chunk;
724
724
  yield CRLF;
725
725
  }
@@ -2089,7 +2089,7 @@ const STREAM_ABORT = "webrun-rpc:stream-abort";
2089
2089
  * request, sent only on a port where both ends agreed `pipeline`. The window's
2090
2090
  * credit is its confirmation.
2091
2091
  */
2092
- const CHUNK = "webrun-rpc:chunk";
2092
+ const CHUNK$1 = "webrun-rpc:chunk";
2093
2093
  /**
2094
2094
  * What a pipelined chunk counts beyond its bytes, against the window, on the
2095
2095
  * receiving side. Both codecs' windows count more for the chunk's envelope
@@ -2420,7 +2420,7 @@ function receiveChunks(port, channelName, controller, touch, notice) {
2420
2420
  const watcher = (event) => {
2421
2421
  const data = event.data;
2422
2422
  if (data?.channelName !== channelName) return;
2423
- const chunk = data.type === CHUNK ? data.chunk : data.type === "request" ? data.params : void 0;
2423
+ const chunk = data.type === CHUNK$1 ? data.chunk : data.type === "request" ? data.params : void 0;
2424
2424
  if (!chunk) return;
2425
2425
  if (!watchPeerEnd()) {
2426
2426
  port.removeEventListener("message", watcher);
@@ -2455,7 +2455,7 @@ function receiveChunks(port, channelName, controller, touch, notice) {
2455
2455
  }, { channelName });
2456
2456
  const onChunk = (event) => {
2457
2457
  const data = event.data;
2458
- if (data?.type !== CHUNK || data.channelName !== channelName || !data.chunk) return;
2458
+ if (data?.type !== CHUNK$1 || data.channelName !== channelName || !data.chunk) return;
2459
2459
  if (poison) return;
2460
2460
  const agreement = portAgreement(port);
2461
2461
  if (!agreement?.caps.has("pipeline")) {
@@ -2568,7 +2568,7 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
2568
2568
  });
2569
2569
  if (mayPipeline(port)) {
2570
2570
  port.postMessage({
2571
- type: CHUNK,
2571
+ type: CHUNK$1,
2572
2572
  channelName,
2573
2573
  chunk
2574
2574
  });
@@ -3867,20 +3867,24 @@ function postFrame(port, frame) {
3867
3867
  function messageSize(data) {
3868
3868
  return typeof data === "string" ? utf8Length(data) : data.byteLength;
3869
3869
  }
3870
- /** The UTF-8 length of `text`, without encoding it. */
3870
+ const encoder = new TextEncoder();
3871
+ /** Characters encoded per `encodeInto` call: at most 3 bytes each, so the scratch stays at 96 KiB. */
3872
+ const CHUNK = 32768;
3873
+ const scratch = new Uint8Array(CHUNK * 3);
3874
+ /**
3875
+ * The UTF-8 length of `text`, as `TextEncoder` would encode it (a lone surrogate counts as the 3
3876
+ * bytes of U+FFFD). Every message is measured on both ends (`bufferedAmount` and the byte acks), so
3877
+ * this runs natively: `encodeInto` a reused buffer, a chunk at a time, never splitting a surrogate
3878
+ * pair across two chunks. A loop over the characters in JavaScript took about 30 times as long.
3879
+ */
3871
3880
  function utf8Length(text) {
3872
3881
  let length = 0;
3873
- for (let i = 0; i < text.length; i++) {
3874
- const code = text.charCodeAt(i);
3875
- if (code < 128) length += 1;
3876
- else if (code < 2048) length += 2;
3877
- else if (code >= 55296 && code <= 56319 && i + 1 < text.length) {
3878
- const next = text.charCodeAt(i + 1);
3879
- if (next >= 56320 && next <= 57343) {
3880
- length += 4;
3881
- i++;
3882
- } else length += 3;
3883
- } else length += 3;
3882
+ for (let start = 0; start < text.length;) {
3883
+ let end = Math.min(start + CHUNK, text.length);
3884
+ const last = text.charCodeAt(end - 1);
3885
+ if (end < text.length && last >= 55296 && last <= 56319) end--;
3886
+ length += encoder.encodeInto(text.slice(start, end), scratch).written;
3887
+ start = end;
3884
3888
  }
3885
3889
  return length;
3886
3890
  }
package/dist/worker.js CHANGED
@@ -647,7 +647,7 @@ const LAST_CHUNK = /* @__PURE__ */ new Uint8Array([
647
647
  13,
648
648
  10
649
649
  ]);
650
- const encoder = /* @__PURE__ */ new TextEncoder();
650
+ const encoder$2 = /* @__PURE__ */ new TextEncoder();
651
651
  /**
652
652
  * Chunk sizes are written in HEXADECIMAL — a 21-byte chunk is `15`. Writing
653
653
  * them in decimal is the defect note 15 found in @libp2p/http, and it is the
@@ -660,7 +660,7 @@ const encoder = /* @__PURE__ */ new TextEncoder();
660
660
  async function* encodeChunked(body) {
661
661
  for await (const chunk of body) {
662
662
  if (chunk.byteLength === 0) continue;
663
- yield encoder.encode(`${chunk.byteLength.toString(16)}\r\n`);
663
+ yield encoder$2.encode(`${chunk.byteLength.toString(16)}\r\n`);
664
664
  yield chunk;
665
665
  yield CRLF;
666
666
  }
@@ -1975,7 +1975,7 @@ const STREAM_ABORT = "webrun-rpc:stream-abort";
1975
1975
  * request, sent only on a port where both ends agreed `pipeline`. The window's
1976
1976
  * credit is its confirmation.
1977
1977
  */
1978
- const CHUNK = "webrun-rpc:chunk";
1978
+ const CHUNK$1 = "webrun-rpc:chunk";
1979
1979
  /**
1980
1980
  * What a pipelined chunk counts beyond its bytes, against the window, on the
1981
1981
  * receiving side. Both codecs' windows count more for the chunk's envelope
@@ -2306,7 +2306,7 @@ function receiveChunks(port, channelName, controller, touch, notice) {
2306
2306
  const watcher = (event) => {
2307
2307
  const data = event.data;
2308
2308
  if (data?.channelName !== channelName) return;
2309
- const chunk = data.type === CHUNK ? data.chunk : data.type === "request" ? data.params : void 0;
2309
+ const chunk = data.type === CHUNK$1 ? data.chunk : data.type === "request" ? data.params : void 0;
2310
2310
  if (!chunk) return;
2311
2311
  if (!watchPeerEnd()) {
2312
2312
  port.removeEventListener("message", watcher);
@@ -2341,7 +2341,7 @@ function receiveChunks(port, channelName, controller, touch, notice) {
2341
2341
  }, { channelName });
2342
2342
  const onChunk = (event) => {
2343
2343
  const data = event.data;
2344
- if (data?.type !== CHUNK || data.channelName !== channelName || !data.chunk) return;
2344
+ if (data?.type !== CHUNK$1 || data.channelName !== channelName || !data.chunk) return;
2345
2345
  if (poison) return;
2346
2346
  const agreement = portAgreement(port);
2347
2347
  if (!agreement?.caps.has("pipeline")) {
@@ -2454,7 +2454,7 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
2454
2454
  });
2455
2455
  if (mayPipeline(port)) {
2456
2456
  port.postMessage({
2457
- type: CHUNK,
2457
+ type: CHUNK$1,
2458
2458
  channelName,
2459
2459
  chunk
2460
2460
  });
@@ -3678,20 +3678,24 @@ function postFrame(port, frame) {
3678
3678
  function messageSize(data) {
3679
3679
  return typeof data === "string" ? utf8Length(data) : data.byteLength;
3680
3680
  }
3681
- /** The UTF-8 length of `text`, without encoding it. */
3681
+ const encoder = new TextEncoder();
3682
+ /** Characters encoded per `encodeInto` call: at most 3 bytes each, so the scratch stays at 96 KiB. */
3683
+ const CHUNK = 32768;
3684
+ const scratch = new Uint8Array(CHUNK * 3);
3685
+ /**
3686
+ * The UTF-8 length of `text`, as `TextEncoder` would encode it (a lone surrogate counts as the 3
3687
+ * bytes of U+FFFD). Every message is measured on both ends (`bufferedAmount` and the byte acks), so
3688
+ * this runs natively: `encodeInto` a reused buffer, a chunk at a time, never splitting a surrogate
3689
+ * pair across two chunks. A loop over the characters in JavaScript took about 30 times as long.
3690
+ */
3682
3691
  function utf8Length(text) {
3683
3692
  let length = 0;
3684
- for (let i = 0; i < text.length; i++) {
3685
- const code = text.charCodeAt(i);
3686
- if (code < 128) length += 1;
3687
- else if (code < 2048) length += 2;
3688
- else if (code >= 55296 && code <= 56319 && i + 1 < text.length) {
3689
- const next = text.charCodeAt(i + 1);
3690
- if (next >= 56320 && next <= 57343) {
3691
- length += 4;
3692
- i++;
3693
- } else length += 3;
3694
- } else length += 3;
3693
+ for (let start = 0; start < text.length;) {
3694
+ let end = Math.min(start + CHUNK, text.length);
3695
+ const last = text.charCodeAt(end - 1);
3696
+ if (end < text.length && last >= 55296 && last <= 56319) end--;
3697
+ length += encoder.encodeInto(text.slice(start, end), scratch).written;
3698
+ start = end;
3695
3699
  }
3696
3700
  return length;
3697
3701
  }
@@ -63,6 +63,11 @@ export declare function readFrame(data: unknown): WsFrame | undefined;
63
63
  export declare function postFrame(port: MessagePort, frame: WsFrame): void;
64
64
  /** The size a message counts for in `bufferedAmount`: UTF-8 bytes, or the buffer's length. */
65
65
  export declare function messageSize(data: string | ArrayBuffer): number;
66
- /** The UTF-8 length of `text`, without encoding it. */
66
+ /**
67
+ * The UTF-8 length of `text`, as `TextEncoder` would encode it (a lone surrogate counts as the 3
68
+ * bytes of U+FFFD). Every message is measured on both ends (`bufferedAmount` and the byte acks), so
69
+ * this runs natively: `encodeInto` a reused buffer, a chunk at a time, never splitting a surrogate
70
+ * pair across two chunks. A loop over the characters in JavaScript took about 30 times as long.
71
+ */
67
72
  export declare function utf8Length(text: string): number;
68
73
  //# sourceMappingURL=frames.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/ws/frames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C,oEAAoE;AACpE,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,MAAM,MAAM,OAAO,GACf;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,GACnC;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACtB;IAAE,CAAC,EAAE,GAAG,CAAA;CAAE,GACV;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACjD,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,aAAa,EAAE,QAK3B,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CA2B5D;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGjE;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAE9D;AAED,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAe/C"}
1
+ {"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/ws/frames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C,oEAAoE;AACpE,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,MAAM,MAAM,OAAO,GACf;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,GACnC;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACtB;IAAE,CAAC,EAAE,GAAG,CAAA;CAAE,GACV;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACjD,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,aAAa,EAAE,QAK3B,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CA2B5D;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGjE;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAE9D;AAOD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/webrun-http-browser",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "ServiceWorker-based HTTP and WebSocket server for browsers: handlers in pages and Dedicated Workers, same-origin and relay modes, streams with backpressure",
@@ -58,9 +58,9 @@
58
58
  ],
59
59
  "dependencies": {
60
60
  "idb-keyval": "^6.3.0",
61
- "@statewalker/webrun-streams": "^0.2.3",
61
+ "@statewalker/webrun-http-streams": "^0.4.0",
62
62
  "@statewalker/webrun-rpc": "^0.10.0",
63
- "@statewalker/webrun-http-streams": "^0.4.0"
63
+ "@statewalker/webrun-streams": "^0.2.3"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@biomejs/biome": "^2.5.15",
package/src/ws/frames.ts CHANGED
@@ -100,20 +100,26 @@ export function messageSize(data: string | ArrayBuffer): number {
100
100
  return typeof data === "string" ? utf8Length(data) : data.byteLength;
101
101
  }
102
102
 
103
- /** The UTF-8 length of `text`, without encoding it. */
103
+ const encoder = new TextEncoder();
104
+ /** Characters encoded per `encodeInto` call: at most 3 bytes each, so the scratch stays at 96 KiB. */
105
+ const CHUNK = 32_768;
106
+ const scratch = new Uint8Array(CHUNK * 3);
107
+
108
+ /**
109
+ * The UTF-8 length of `text`, as `TextEncoder` would encode it (a lone surrogate counts as the 3
110
+ * bytes of U+FFFD). Every message is measured on both ends (`bufferedAmount` and the byte acks), so
111
+ * this runs natively: `encodeInto` a reused buffer, a chunk at a time, never splitting a surrogate
112
+ * pair across two chunks. A loop over the characters in JavaScript took about 30 times as long.
113
+ */
104
114
  export function utf8Length(text: string): number {
105
115
  let length = 0;
106
- for (let i = 0; i < text.length; i++) {
107
- const code = text.charCodeAt(i);
108
- if (code < 0x80) length += 1;
109
- else if (code < 0x800) length += 2;
110
- else if (code >= 0xd800 && code <= 0xdbff && i + 1 < text.length) {
111
- const next = text.charCodeAt(i + 1);
112
- if (next >= 0xdc00 && next <= 0xdfff) {
113
- length += 4;
114
- i++;
115
- } else length += 3;
116
- } else length += 3;
116
+ for (let start = 0; start < text.length; ) {
117
+ let end = Math.min(start + CHUNK, text.length);
118
+ // A high surrogate ending the chunk goes with its low surrogate into the next one.
119
+ const last = text.charCodeAt(end - 1);
120
+ if (end < text.length && last >= 0xd800 && last <= 0xdbff) end--;
121
+ length += encoder.encodeInto(text.slice(start, end), scratch).written;
122
+ start = end;
117
123
  }
118
124
  return length;
119
125
  }