Skip to content

Commit bf78bab

Browse files
authored
Merge pull request #1199 from joshunrau/interactive-features
handle encoding unicode characters in legacy scripts
2 parents 33d8d35 + 29c7e77 commit bf78bab

File tree

9 files changed

+44
-7
lines changed

9 files changed

+44
-7
lines changed

packages/instrument-bundler/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"tsx": "catalog:"
2323
},
2424
"dependencies": {
25+
"@opendatacapture/runtime-internal": "workspace:*",
2526
"commander": "^12.1.0",
2627
"es-module-lexer": "^1.6.0",
2728
"esbuild": "catalog:",

packages/instrument-bundler/src/bundle.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { encodeUnicodeToBase64 } from '@opendatacapture/runtime-internal';
2+
13
import { build } from './build.js';
24
import { preprocess } from './preprocess.js';
35
import { transformImports } from './transform.js';
@@ -42,9 +44,9 @@ const GLOBALS = `
4244
*/
4345
export async function createBundle(output: BuildOutput, options: { minify: boolean }) {
4446
let inject = '';
45-
const style = output.css ? `"${btoa(output.css)}"` : undefined;
47+
const style = output.css ? `"${encodeUnicodeToBase64(output.css)}"` : undefined;
4648
const scripts = output.legacyScripts?.length
47-
? `[${output.legacyScripts.map((content) => `"${btoa(content)}"`).join(', ')}]`
49+
? `[${output.legacyScripts.map((content) => `"${encodeUnicodeToBase64(content)}"`).join(', ')}]`
4850
: undefined;
4951
if (style || scripts) {
5052
inject = `Object.defineProperty(__exports.content, '__injectHead', { value: Object.freeze({ scripts: ${scripts}, style: ${style} }), writable: false });`;

packages/runtime-internal/src/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@
77
* PLAYGROUND).**
88
*/
99
export declare function evaluateInstrument(bundle: string): Promise<any>;
10+
11+
/** Encodes a Unicode string into a Base64 string */
12+
export declare function encodeUnicodeToBase64(s: string): string;
13+
14+
/** Decodes a Base64 string back into a Unicode string. */
15+
export declare function decodeBase64ToUnicode(s: string): string;

packages/runtime-internal/src/index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,22 @@
22
export async function evaluateInstrument(bundle) {
33
return await new Function(`return ${bundle}`)();
44
}
5+
6+
/** @type {import('./index.d.ts').encodeUnicodeToBase64} */
7+
export function encodeUnicodeToBase64(s) {
8+
const utf8Bytes = new TextEncoder().encode(s);
9+
10+
let binaryString = '';
11+
for (const byte of utf8Bytes) {
12+
binaryString += String.fromCharCode(byte);
13+
}
14+
15+
return btoa(binaryString);
16+
}
17+
18+
/** @type {import('./index.d.ts').decodeBase64ToUnicode} */
19+
export function decodeBase64ToUnicode(s) {
20+
const binaryString = atob(s);
21+
const bytes = Uint8Array.from(binaryString, (c) => c.charCodeAt(0));
22+
return new TextDecoder().decode(bytes);
23+
}

packages/runtime-internal/src/interactive/bootstrap.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ if (!bundle) {
4242
}
4343

4444
(async function () {
45-
const { evaluateInstrument } = await import('../index.js');
45+
const { decodeBase64ToUnicode, evaluateInstrument } = await import('../index.js');
4646

4747
/** @type {import('../../../runtime-core/src/types/instrument.interactive.js').InteractiveInstrument} */
4848
const instrument = await evaluateInstrument(bundle);
@@ -69,7 +69,7 @@ if (!bundle) {
6969

7070
const encodedStyle = instrument.content.__injectHead?.style;
7171
if (encodedStyle) {
72-
const style = atob(encodedStyle);
72+
const style = decodeBase64ToUnicode(encodedStyle);
7373
document.head.insertAdjacentHTML('beforeend', `<style>${style}</style>`);
7474
}
7575

@@ -81,7 +81,7 @@ if (!bundle) {
8181
scripts?.forEach((encodedScript) => {
8282
const script = document.createElement('script');
8383
script.type = 'text/javascript';
84-
script.textContent = atob(encodedScript);
84+
script.textContent = decodeBase64ToUnicode(encodedScript);
8585
document.head.appendChild(script);
8686
});
8787

packages/serve-instrument/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"@douglasneuroinformatics/libui": "catalog:",
1212
"@opendatacapture/instrument-bundler": "workspace:",
1313
"@opendatacapture/react-core": "workspace:*",
14+
"@opendatacapture/runtime-internal": "workspace:*",
1415
"@opendatacapture/runtime-v1": "workspace:*",
1516
"@opendatacapture/vite-plugin-runtime": "workspace:*",
1617
"@tailwindcss/vite": "catalog:",

packages/serve-instrument/src/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as path from 'node:path';
55

66
import { bundle, BUNDLER_FILE_EXT_REGEX, inferLoader } from '@opendatacapture/instrument-bundler';
77
import type { BundlerInput } from '@opendatacapture/instrument-bundler';
8+
import { encodeUnicodeToBase64 } from '@opendatacapture/runtime-internal';
89
import runtime from '@opendatacapture/vite-plugin-runtime';
910
import tailwindcss from '@tailwindcss/vite';
1011
import { Command, InvalidArgumentError } from 'commander';
@@ -55,7 +56,7 @@ program.action(async (target: string) => {
5556
name: path.basename(filepath)
5657
});
5758
}
58-
return btoa(await bundle({ inputs }));
59+
return encodeUnicodeToBase64(await bundle({ inputs }));
5960
};
6061

6162
const server = await createServer({

packages/serve-instrument/src/client/main.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createRoot } from 'react-dom/client';
33

44
import { i18n } from '@douglasneuroinformatics/libui/i18n';
55
import { ScalarInstrumentRenderer } from '@opendatacapture/react-core';
6+
import { decodeBase64ToUnicode } from '@opendatacapture/runtime-internal';
67

78
import '@opendatacapture/react-core/globals.css';
89

@@ -25,7 +26,7 @@ const App = () => {
2526
<div className="container h-screen py-16">
2627
<ScalarInstrumentRenderer
2728
key={encodedBundle}
28-
target={{ bundle: atob(encodedBundle), id: null! }}
29+
target={{ bundle: decodeBase64ToUnicode(encodedBundle), id: null! }}
2930
onSubmit={({ data }) => {
3031
// eslint-disable-next-line no-alert
3132
alert(JSON.stringify({ _message: 'The following data will be submitted', data }, null, 2));

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)