@marimo-team/islands 0.19.6-dev0 → 0.19.6-dev2

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/main.js CHANGED
@@ -56758,31 +56758,29 @@ Database schema: ${c}`), (_a3 = r2.aiFix) == null ? void 0 : _a3.setAiCompletion
56758
56758
  function base64ToDataURL(e, r) {
56759
56759
  return `data:${r};base64,${e}`;
56760
56760
  }
56761
- function typedAtob(e) {
56762
- return window.atob(e);
56763
- }
56764
- function typedBtoa(e) {
56765
- return window.btoa(e);
56766
- }
56767
56761
  function isDataURLString(e) {
56768
56762
  return e.startsWith("data:") && e.includes(";base64,");
56769
56763
  }
56770
56764
  function extractBase64FromDataURL(e) {
56771
56765
  return e.split(",")[1];
56772
56766
  }
56773
- function byteStringToBinary(e) {
56774
- return Uint8Array.from(e, (e2) => e2.charCodeAt(0));
56767
+ function base64ToUint8Array(e) {
56768
+ let r = window.atob(e);
56769
+ return Uint8Array.from(r, (e2) => e2.charCodeAt(0));
56775
56770
  }
56776
- function binaryToByteString(e) {
56777
- let r = "";
56778
- for (let c of e) r += String.fromCharCode(c);
56779
- return r;
56771
+ function base64ToDataView(e) {
56772
+ let r = base64ToUint8Array(e);
56773
+ return new DataView(r.buffer);
56774
+ }
56775
+ function uint8ArrayToBase64(e) {
56776
+ let r = Array.from(e, (e2) => String.fromCharCode(e2));
56777
+ return window.btoa(r.join(""));
56778
+ }
56779
+ function dataViewToBase64(e) {
56780
+ return uint8ArrayToBase64(new Uint8Array(e.buffer, e.byteOffset, e.byteLength));
56780
56781
  }
56781
56782
  function safeExtractSetUIElementMessageBuffers(e) {
56782
- return (e.buffers ?? []).map((e2) => {
56783
- let r = byteStringToBinary(typedAtob(e2));
56784
- return new DataView(r.buffer);
56785
- });
56783
+ return (e.buffers ?? []).map(base64ToDataView);
56786
56784
  }
56787
56785
  function getLegacyNumericSpec(e, r, c) {
56788
56786
  return {
@@ -57011,14 +57009,14 @@ Database schema: ${c}`), (_a3 = r2.aiFix) == null ? void 0 : _a3.setAiCompletion
57011
57009
  }, c = "source_0";
57012
57010
  else if (isDataURLString(e)) {
57013
57011
  c = "data_0";
57014
- let d = typedAtob(extractBase64FromDataURL(e));
57015
- r = d.startsWith(ARROW_MAGIC_NUMBER) ? {
57016
- values: byteStringToBinary(d),
57012
+ let d = extractBase64FromDataURL(e), f = window.atob(d);
57013
+ r = f.startsWith(ARROW_MAGIC_NUMBER) ? {
57014
+ values: base64ToUint8Array(d),
57017
57015
  format: {
57018
57016
  type: "arrow"
57019
57017
  }
57020
57018
  } : {
57021
- values: parseCsvData(d)
57019
+ values: parseCsvData(f)
57022
57020
  };
57023
57021
  } else r = {
57024
57022
  values: parseCsvData(e)
@@ -63239,9 +63237,6 @@ ${E}`,
63239
63237
  };
63240
63238
  }
63241
63239
  };
63242
- function dataViewToBase64(e) {
63243
- return typedBtoa(binaryToByteString(new Uint8Array(e.buffer, e.byteOffset, e.byteLength)));
63244
- }
63245
63240
  function findDataViewPaths(e, r = []) {
63246
63241
  let c = [];
63247
63242
  if (e instanceof DataView) c.push(r);
@@ -63290,10 +63285,7 @@ ${E}`,
63290
63285
  Logger.warn("[anywidget] Could not find buffer at path", r2);
63291
63286
  continue;
63292
63287
  }
63293
- if (typeof c2 == "string") {
63294
- let e3 = byteStringToBinary(typedAtob(c2));
63295
- set_default(f, r2, new DataView(e3.buffer));
63296
- } else set_default(f, r2, c2);
63288
+ typeof c2 == "string" ? set_default(f, r2, base64ToDataView(c2)) : set_default(f, r2, c2);
63297
63289
  }
63298
63290
  return f;
63299
63291
  }
@@ -101103,7 +101095,7 @@ Defaulting to \`null\`.`;
101103
101095
  return Logger.warn("Failed to get version from mount config"), null;
101104
101096
  }
101105
101097
  }
101106
- const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.6-dev0"), showCodeInRunModeAtom = atom(true);
101098
+ const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.6-dev2"), showCodeInRunModeAtom = atom(true);
101107
101099
  atom(null);
101108
101100
  var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
101109
101101
  constructor() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.19.6-dev0",
3
+ "version": "0.19.6-dev2",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -258,7 +258,7 @@
258
258
  "stylelint-config-standard": "^36.0.1",
259
259
  "tailwindcss": "^4.1.18",
260
260
  "vega-typings": "^2.1.0",
261
- "vite": "npm:rolldown-vite@7.3.0",
261
+ "vite": "npm:rolldown-vite@7.3.1",
262
262
  "vite-plugin-top-level-await": "^1.6.0",
263
263
  "vite-plugin-wasm": "^3.5.0",
264
264
  "vitest": "^3.2.4"
@@ -12,10 +12,9 @@ import type { TopLevelFacetedUnitSpec } from "@/plugins/impl/data-explorer/queri
12
12
  import { arrow } from "@/plugins/impl/vega/formats";
13
13
  import { parseCsvData } from "@/plugins/impl/vega/loader";
14
14
  import {
15
- byteStringToBinary,
15
+ base64ToUint8Array,
16
16
  extractBase64FromDataURL,
17
17
  isDataURLString,
18
- typedAtob,
19
18
  } from "@/utils/json/base64";
20
19
 
21
20
  export function getLegacyNumericSpec(
@@ -281,12 +280,12 @@ export function getDataSpecAndSourceName<T>(data: string | T[]): {
281
280
  } else if (isDataURLString(data)) {
282
281
  sourceName = "data_0";
283
282
  const base64 = extractBase64FromDataURL(data);
284
- const decoded = typedAtob(base64);
283
+ const decoded = window.atob(base64);
285
284
 
286
285
  // eslint-disable-next-line unicorn/prefer-ternary
287
286
  if (decoded.startsWith(ARROW_MAGIC_NUMBER)) {
288
287
  dataSpec = {
289
- values: byteStringToBinary(decoded),
288
+ values: base64ToUint8Array(base64),
290
289
  // @ts-expect-error vega-typings does not include arrow format
291
290
  format: { type: "arrow" },
292
291
  };
@@ -98,10 +98,8 @@ export const NameCellContentEditable: React.FC<{
98
98
  e.stopPropagation();
99
99
 
100
100
  // On Enter, blur the input to commit the change
101
- if (e.key === "Enter") {
102
- if (e.target instanceof HTMLElement) {
103
- e.target.blur();
104
- }
101
+ if (e.key === "Enter" && e.target instanceof HTMLElement) {
102
+ e.target.blur();
105
103
  }
106
104
  }}
107
105
  >
@@ -1,7 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { describe, expect, it } from "vitest";
3
3
  import {
4
- dataViewToBase64,
5
4
  decodeFromWire,
6
5
  isWireFormat,
7
6
  serializeBuffersToBase64,
@@ -260,50 +259,6 @@ describe("Immutability Tests", () => {
260
259
  });
261
260
  });
262
261
 
263
- describe("dataViewToBase64", () => {
264
- it("should convert a DataView to a base64 string", () => {
265
- const encoder = new TextEncoder();
266
- const bytes = encoder.encode("Hello, World!");
267
- const dataView = new DataView(bytes.buffer);
268
- const base64 = dataViewToBase64(dataView);
269
-
270
- // Decode and verify
271
- const decoded = atob(base64);
272
- expect(decoded).toBe("Hello, World!");
273
- });
274
-
275
- it("should handle empty DataView", () => {
276
- const dataView = new DataView(new ArrayBuffer(0));
277
- const base64 = dataViewToBase64(dataView);
278
- expect(base64).toBe("");
279
- });
280
-
281
- it("should handle DataView with offset and length", () => {
282
- const encoder = new TextEncoder();
283
- const bytes = encoder.encode("Hello, World!");
284
- // Create a DataView that only looks at "World!"
285
- const dataView = new DataView(bytes.buffer, 7, 6);
286
- const base64 = dataViewToBase64(dataView);
287
-
288
- const decoded = atob(base64);
289
- expect(decoded).toBe("World!");
290
- });
291
-
292
- it("should handle binary data", () => {
293
- const bytes = new Uint8Array([0, 1, 2, 255, 254, 253]);
294
- const dataView = new DataView(bytes.buffer);
295
- const base64 = dataViewToBase64(dataView);
296
-
297
- // Verify round-trip
298
- const decoded = atob(base64);
299
- const decodedBytes = new Uint8Array(decoded.length);
300
- for (let i = 0; i < decoded.length; i++) {
301
- decodedBytes[i] = decoded.charCodeAt(i);
302
- }
303
- expect([...decodedBytes]).toEqual([0, 1, 2, 255, 254, 253]);
304
- });
305
- });
306
-
307
262
  describe("serializeBuffersToBase64", () => {
308
263
  it("should return empty arrays when no DataViews present", () => {
309
264
  const input = { a: 1, b: "text", c: { d: true } };
@@ -3,26 +3,11 @@ import { get, set } from "lodash-es";
3
3
  import { invariant } from "./invariant";
4
4
  import {
5
5
  type Base64String,
6
- binaryToByteString,
7
- byteStringToBinary,
8
- typedAtob,
9
- typedBtoa,
6
+ base64ToDataView,
7
+ dataViewToBase64,
10
8
  } from "./json/base64";
11
9
  import { Logger } from "./Logger";
12
10
 
13
- /**
14
- * Convert a DataView to a base64 string.
15
- */
16
- export function dataViewToBase64(dataView: DataView): Base64String {
17
- const bytes = new Uint8Array(
18
- dataView.buffer,
19
- dataView.byteOffset,
20
- dataView.byteLength,
21
- );
22
- const byteString = binaryToByteString(bytes);
23
- return typedBtoa(byteString);
24
- }
25
-
26
11
  /**
27
12
  * Recursively find all DataViews in an object and return their paths.
28
13
  *
@@ -148,8 +133,7 @@ export function decodeFromWire<T extends Record<string, unknown>>(input: {
148
133
 
149
134
  // Handle both base64 strings (from wire format) and DataViews (direct usage)
150
135
  if (typeof buffer === "string") {
151
- const bytes = byteStringToBinary(typedAtob(buffer));
152
- set(out, bufferPath, new DataView(bytes.buffer));
136
+ set(out, bufferPath, base64ToDataView(buffer));
153
137
  } else {
154
138
  set(out, bufferPath, buffer);
155
139
  }
@@ -2,99 +2,175 @@
2
2
  import { describe, expect, test } from "vitest";
3
3
  import {
4
4
  type Base64String,
5
- type ByteString,
6
5
  base64ToDataURL,
7
- byteStringToBinary,
6
+ base64ToDataView,
7
+ base64ToUint8Array,
8
8
  type DataURLString,
9
- deserializeBase64,
9
+ dataViewToBase64,
10
+ deserializeJson,
10
11
  extractBase64FromDataURL,
11
12
  isDataURLString,
12
13
  type JsonString,
13
- typedAtob,
14
- typedBtoa,
14
+ uint8ArrayToBase64,
15
15
  } from "../base64";
16
16
 
17
- describe("base64Utils", () => {
18
- const testData = {
19
- name: "marimo inc",
20
- type: "company",
21
- };
17
+ describe("base64", () => {
18
+ describe("deserializeJson", () => {
19
+ test("deserializes JSON string to object", () => {
20
+ const json = '{"name":"marimo","type":"notebook"}' as JsonString<{
21
+ name: string;
22
+ type: string;
23
+ }>;
24
+ const result = deserializeJson(json);
25
+ expect(result).toEqual({ name: "marimo", type: "notebook" });
26
+ });
22
27
 
23
- const expectedBase64 =
24
- "JTdCJTIybmFtZSUyMiUzQSUyMm1hcmltbyUyMGluYyUyMiUyQyUyMnR5cGUlMjIlM0ElMjJjb21wYW55JTIyJTdE" as Base64String<
25
- JsonString<typeof testData>
26
- >;
27
-
28
- test("serializeJsonToBase64 should correctly encode JSON to Base64", () => {
29
- const base64Encoded = serializeJsonToBase64(testData);
30
- expect(base64Encoded).toBe(expectedBase64);
28
+ test("deserializes JSON array", () => {
29
+ const json = "[1,2,3]" as JsonString<number[]>;
30
+ const result = deserializeJson(json);
31
+ expect(result).toEqual([1, 2, 3]);
32
+ });
31
33
  });
32
34
 
33
- test("deserializeBase64ToJson should correctly decode Base64 to JSON", () => {
34
- const jsonDecoded = deserializeBase64(expectedBase64);
35
- expect(JSON.parse(jsonDecoded)).toEqual(testData);
36
- });
35
+ describe("base64ToDataURL", () => {
36
+ test("creates proper data URL with mime type", () => {
37
+ const base64 = "SGVsbG8=" as Base64String;
38
+ const result = base64ToDataURL(base64, "text/plain");
39
+ expect(result).toBe("data:text/plain;base64,SGVsbG8=");
40
+ });
37
41
 
38
- test("serializeJsonToBase64 and deserializeBase64ToJson should be reversible", () => {
39
- const base64Encoded = serializeJsonToBase64(testData);
40
- const jsonDecoded = deserializeBase64(base64Encoded);
41
- expect(JSON.parse(jsonDecoded)).toEqual(testData);
42
+ test("handles image mime types", () => {
43
+ const base64 = "iVBORw0KGgo=" as Base64String;
44
+ const result = base64ToDataURL(base64, "image/png");
45
+ expect(result).toBe("data:image/png;base64,iVBORw0KGgo=");
46
+ });
42
47
  });
43
48
 
44
- test("base64ToDataURL should create proper data URL", () => {
45
- const base64 = "SGVsbG8=" as Base64String;
46
- const result = base64ToDataURL(base64, "text/plain");
47
- expect(result).toBe("data:text/plain;base64,SGVsbG8=");
49
+ describe("isDataURLString", () => {
50
+ test.each([
51
+ ["data:text/plain;base64,SGVsbG8=", true],
52
+ ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA", true],
53
+ ["not-a-data-url", false],
54
+ ["base64,SGVsbG8=", false],
55
+ ["data:text/plain,hello", false],
56
+ ["data:text/plain;charset=utf-8,hello", false],
57
+ ])("isDataURLString(%s) returns %s", (input, expected) => {
58
+ expect(isDataURLString(input)).toBe(expected);
59
+ });
48
60
  });
49
61
 
50
- test("typedAtob and typedBtoa should be reversible", () => {
51
- const original = "hello world" as ByteString;
52
- const encoded = typedBtoa(original);
53
- const decoded = typedAtob(encoded);
54
- expect(decoded).toBe(original);
62
+ describe("extractBase64FromDataURL", () => {
63
+ test.each([
64
+ ["data:text/plain;base64,SGVsbG8=", "SGVsbG8="],
65
+ [
66
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA",
67
+ "iVBORw0KGgoAAAANSUhEUgA",
68
+ ],
69
+ ["data:application/json;base64,", ""],
70
+ ["data:text/html;charset=utf-8;base64,PGh0bWw+", "PGh0bWw+"],
71
+ ])("extracts base64 from %s", (dataUrl, expected) => {
72
+ expect(extractBase64FromDataURL(dataUrl as DataURLString)).toBe(expected);
73
+ });
55
74
  });
56
75
 
57
- test.each([
58
- ["data:text/plain;base64,SGVsbG8=", true],
59
- ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA", true],
60
- ["not-a-data-url", false],
61
- ["base64,SGVsbG8=", false],
62
- ])("isDataURLString(%s) should return %s", (input, expected) => {
63
- expect(isDataURLString(input)).toBe(expected);
76
+ describe("base64ToUint8Array", () => {
77
+ test("converts base64 to Uint8Array", () => {
78
+ const base64 = "QUJD" as Base64String; // "ABC"
79
+ const result = base64ToUint8Array(base64);
80
+ expect(result).toEqual(new Uint8Array([65, 66, 67]));
81
+ });
82
+
83
+ test("handles empty base64 string", () => {
84
+ const base64 = "" as Base64String;
85
+ const result = base64ToUint8Array(base64);
86
+ expect(result).toEqual(new Uint8Array([]));
87
+ });
64
88
  });
65
89
 
66
- test("isDataURLString should reject data URLs without base64", () => {
67
- expect(isDataURLString("data:text/plain,hello")).toBe(false);
68
- expect(isDataURLString("data:text/plain;charset=utf-8,hello")).toBe(false);
90
+ describe("base64ToDataView", () => {
91
+ test("converts base64 to DataView", () => {
92
+ const base64 = "QUJD" as Base64String; // "ABC"
93
+ const result = base64ToDataView(base64);
94
+ expect(result.byteLength).toBe(3);
95
+ expect(result.getUint8(0)).toBe(65);
96
+ expect(result.getUint8(1)).toBe(66);
97
+ expect(result.getUint8(2)).toBe(67);
98
+ });
69
99
  });
70
100
 
71
- test.each([
72
- ["data:text/plain;base64,SGVsbG8=", "SGVsbG8="],
73
- [
74
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA",
75
- "iVBORw0KGgoAAAANSUhEUgA",
76
- ],
77
- ["data:application/json;base64,", ""],
78
- ["data:text/html;charset=utf-8;base64,PGh0bWw+", "PGh0bWw+"],
79
- ])("extractBase64FromDataURL(%s) should return %s", (dataUrl, expected) => {
80
- expect(extractBase64FromDataURL(dataUrl as DataURLString)).toBe(expected);
101
+ describe("uint8ArrayToBase64", () => {
102
+ test("converts Uint8Array to base64", () => {
103
+ const array = new Uint8Array([65, 66, 67]);
104
+ const result = uint8ArrayToBase64(array);
105
+ expect(result).toBe("QUJD");
106
+ });
107
+
108
+ test("handles empty Uint8Array", () => {
109
+ const array = new Uint8Array([]);
110
+ const result = uint8ArrayToBase64(array);
111
+ expect(result).toBe("");
112
+ });
81
113
  });
82
114
 
83
- test("byteStringToBinary should convert to Uint8Array", () => {
84
- const bytes = "ABC" as ByteString;
85
- const result = byteStringToBinary(bytes);
86
- expect(result).toEqual(new Uint8Array([65, 66, 67]));
115
+ describe("dataViewToBase64", () => {
116
+ test("should convert a DataView to a base64 string", () => {
117
+ const encoder = new TextEncoder();
118
+ const bytes = encoder.encode("Hello, World!");
119
+ const dataView = new DataView(bytes.buffer);
120
+ const base64 = dataViewToBase64(dataView);
121
+
122
+ // Decode and verify
123
+ const decoded = atob(base64);
124
+ expect(decoded).toBe("Hello, World!");
125
+ });
126
+
127
+ test("should handle empty DataView", () => {
128
+ const dataView = new DataView(new ArrayBuffer(0));
129
+ const base64 = dataViewToBase64(dataView);
130
+ expect(base64).toBe("");
131
+ });
132
+
133
+ test("should handle DataView with offset and length", () => {
134
+ const encoder = new TextEncoder();
135
+ const bytes = encoder.encode("Hello, World!");
136
+ // Create a DataView that only looks at "World!"
137
+ const dataView = new DataView(bytes.buffer, 7, 6);
138
+ const base64 = dataViewToBase64(dataView);
139
+
140
+ const decoded = atob(base64);
141
+ expect(decoded).toBe("World!");
142
+ });
143
+
144
+ test("should handle binary data", () => {
145
+ const bytes = new Uint8Array([0, 1, 2, 255, 254, 253]);
146
+ const dataView = new DataView(bytes.buffer);
147
+ const base64 = dataViewToBase64(dataView);
148
+
149
+ // Verify round-trip
150
+ const decoded = atob(base64);
151
+ const decodedBytes = new Uint8Array(decoded.length);
152
+ for (let i = 0; i < decoded.length; i++) {
153
+ decodedBytes[i] = decoded.charCodeAt(i);
154
+ }
155
+ expect([...decodedBytes]).toEqual([0, 1, 2, 255, 254, 253]);
156
+ });
87
157
  });
88
158
 
89
- test("byteStringToBinary should handle empty string", () => {
90
- const bytes = "" as ByteString;
91
- const result = byteStringToBinary(bytes);
92
- expect(result).toEqual(new Uint8Array([]));
159
+ describe("round-trip conversions", () => {
160
+ test("Uint8Array to base64 and back", () => {
161
+ const original = new Uint8Array([1, 2, 3, 255, 128, 0]);
162
+ const base64 = uint8ArrayToBase64(original);
163
+ const result = base64ToUint8Array(base64);
164
+ expect(result).toEqual(original);
165
+ });
166
+
167
+ test("DataView to base64 and back", () => {
168
+ const buffer = new ArrayBuffer(4);
169
+ const original = new DataView(buffer);
170
+ original.setUint32(0, 0x12_34_56_78);
171
+ const base64 = dataViewToBase64(original);
172
+ const result = base64ToDataView(base64);
173
+ expect(result.getUint32(0)).toBe(0x12_34_56_78);
174
+ });
93
175
  });
94
176
  });
95
-
96
- // Serialization: JSON to Base64
97
- function serializeJsonToBase64<T>(jsonObject: T) {
98
- const jsonString = JSON.stringify(jsonObject);
99
- return btoa(encodeURIComponent(jsonString)) as Base64String<JsonString<T>>;
100
- }
@@ -3,64 +3,88 @@
3
3
  import type { NotificationMessageData } from "@/core/kernel/messages";
4
4
  import type { TypedString } from "../typed";
5
5
 
6
+ /**
7
+ * A JSON string of a given type.
8
+ */
6
9
  export type JsonString<T = unknown> = TypedString<"Json"> & {
7
10
  _of_: T;
8
11
  };
9
12
 
10
- export type Base64String<T = unknown> = TypedString<"Base64"> & {
11
- _of_: T;
12
- };
13
-
14
- export type ByteString<T = unknown> = TypedString<"ByteString"> & {
15
- _of_: T;
16
- };
17
-
18
- export type DataURLString = `data:${string};base64,${Base64String<string>}`;
13
+ /**
14
+ * A base64-encoded string.
15
+ */
16
+ export type Base64String = TypedString<"Base64">;
19
17
 
20
- // Deserialization: Base64 to String
21
- export function deserializeBase64<T>(base64: Base64String<T>): T {
22
- const decodedString = decodeURIComponent(atob(base64));
23
- return decodedString as T;
24
- }
18
+ /**
19
+ * A data URL string.
20
+ */
21
+ export type DataURLString = `data:${string};base64,${Base64String}`;
25
22
 
26
- // Deserialization: String to JSON
23
+ /**
24
+ * Typed JSON deserialization.
25
+ */
27
26
  export function deserializeJson<T>(jsonString: JsonString<T>): T {
28
27
  return JSON.parse(jsonString) as T;
29
28
  }
30
29
 
31
- export function base64ToDataURL<T>(
32
- base64: Base64String<T>,
30
+ /**
31
+ * Convert a base64 string to a data URL string.
32
+ */
33
+ export function base64ToDataURL(
34
+ base64: Base64String,
33
35
  mimeType: string,
34
36
  ): DataURLString {
35
- return `data:${mimeType};base64,${base64}` as DataURLString;
36
- }
37
-
38
- export function typedAtob<T>(base64: Base64String<T>): ByteString<T> {
39
- return window.atob(base64) as ByteString<T>;
40
- }
41
-
42
- export function typedBtoa<T>(bytes: ByteString<T>): Base64String<T> {
43
- return window.btoa(bytes) as Base64String<T>;
37
+ return `data:${mimeType};base64,${base64}`;
44
38
  }
45
39
 
40
+ /**
41
+ * Check if a string is a data URL string.
42
+ */
46
43
  export function isDataURLString(str: string): str is DataURLString {
47
44
  return str.startsWith("data:") && str.includes(";base64,");
48
45
  }
49
46
 
47
+ /**
48
+ * Extract the base64 string from a data URL string.
49
+ */
50
50
  export function extractBase64FromDataURL(str: DataURLString): Base64String {
51
51
  return str.split(",")[1] as Base64String;
52
52
  }
53
53
 
54
- export function byteStringToBinary(bytes: ByteString): Uint8Array {
55
- return Uint8Array.from(bytes, (c) => c.charCodeAt(0));
54
+ /**
55
+ * Convert a base64 string to a Uint8Array.
56
+ */
57
+ export function base64ToUint8Array(bytes: Base64String): Uint8Array {
58
+ const binary = window.atob(bytes);
59
+ return Uint8Array.from(binary, (c) => c.charCodeAt(0));
60
+ }
61
+
62
+ /**
63
+ * Convert a base64 string to a DataView.
64
+ */
65
+ export function base64ToDataView(bytes: Base64String): DataView {
66
+ const uint8Array = base64ToUint8Array(bytes);
67
+ return new DataView(uint8Array.buffer);
68
+ }
69
+
70
+ /**
71
+ * Convert a Uint8Array to a base64 string.
72
+ */
73
+ export function uint8ArrayToBase64(binary: Uint8Array): Base64String {
74
+ const chars = Array.from(binary, (byte) => String.fromCharCode(byte));
75
+ return window.btoa(chars.join("")) as Base64String;
56
76
  }
57
77
 
58
- export function binaryToByteString(binary: Uint8Array): ByteString {
59
- let result = "";
60
- for (const byte of binary) {
61
- result += String.fromCharCode(byte);
62
- }
63
- return result as ByteString;
78
+ /**
79
+ * Convert a DataView to a base64 string.
80
+ */
81
+ export function dataViewToBase64(dataView: DataView): Base64String {
82
+ const uint8Array = new Uint8Array(
83
+ dataView.buffer,
84
+ dataView.byteOffset,
85
+ dataView.byteLength,
86
+ );
87
+ return uint8ArrayToBase64(uint8Array);
64
88
  }
65
89
 
66
90
  export function safeExtractSetUIElementMessageBuffers(
@@ -68,8 +92,5 @@ export function safeExtractSetUIElementMessageBuffers(
68
92
  ): readonly DataView[] {
69
93
  // @ts-expect-error - TypeScript doesn't know that these strings are actually base64 strings
70
94
  const strs: Base64String[] = notification.buffers ?? [];
71
- return strs.map((str) => {
72
- const bytes = byteStringToBinary(typedAtob(str));
73
- return new DataView(bytes.buffer);
74
- });
95
+ return strs.map(base64ToDataView);
75
96
  }