VulpesOS/android/ui/settings-codec.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

40 lines
1.6 KiB
JavaScript

// Android's extension storage/RPC is JSON. Preserve binary Gaia settings across
// both boundaries and reconstruct Blobs in the application's own JS realm.
(() => {
const marker = '__vulpesBlobV1';
async function encode(value) {
if (value instanceof Blob) {
if (value.size > 16 * 1024 * 1024) throw new Error('SETTING_BLOB_TOO_LARGE');
const bytes = new Uint8Array(await value.arrayBuffer());
const chunks = [];
for (let i = 0; i < bytes.length; i += 16384) {
chunks.push(String.fromCharCode(...bytes.subarray(i, i + 16384)));
}
return { [marker]: true, type: value.type, base64: btoa(chunks.join('')) };
}
if (Array.isArray(value)) return Promise.all(value.map(encode));
if (value && typeof value === 'object') {
return Object.fromEntries(
await Promise.all(
Object.entries(value).map(async ([key, item]) => [key, await encode(item)]),
),
);
}
return value;
}
function decode(value) {
if (!value || typeof value !== 'object') return value;
if (
value[marker] === true &&
typeof value.type === 'string' &&
typeof value.base64 === 'string' &&
Object.keys(value).length === 3
) {
const bytes = Uint8Array.from(atob(value.base64), (c) => c.charCodeAt(0));
return new Blob([bytes], { type: value.type });
}
if (Array.isArray(value)) return value.map(decode);
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, decode(item)]));
}
window.VulpesAndroidSettingsCodec = Object.freeze({ encode, decode });
})();