52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
import { unzipSync } from 'fflate';
|
|
import { parse as parseTOML } from 'smol-toml';
|
|
|
|
/** Metadata extracted from a webxdc `.xdc` ZIP archive. */
|
|
export interface WebxdcMeta {
|
|
/** App name from manifest.toml, or undefined if missing. */
|
|
name?: string;
|
|
/** Icon as a File ready for upload, or undefined if missing. */
|
|
iconFile?: File;
|
|
}
|
|
|
|
/**
|
|
* Extract the name and icon from a `.xdc` (ZIP) file.
|
|
*
|
|
* - Reads `manifest.toml` for the `name` field.
|
|
* - Reads `icon.png` or `icon.jpg` and returns it as a `File`.
|
|
*/
|
|
export async function extractWebxdcMeta(file: File): Promise<WebxdcMeta> {
|
|
const buf = await file.arrayBuffer();
|
|
const unzipped = unzipSync(new Uint8Array(buf));
|
|
|
|
const meta: WebxdcMeta = {};
|
|
|
|
// --- Parse manifest.toml for the name ---
|
|
const manifestBytes = unzipped['manifest.toml'];
|
|
if (manifestBytes) {
|
|
const text = new TextDecoder().decode(manifestBytes);
|
|
try {
|
|
const manifest = parseTOML(text);
|
|
if (typeof manifest.name === 'string') {
|
|
meta.name = manifest.name;
|
|
}
|
|
} catch {
|
|
// Silently ignore malformed TOML
|
|
}
|
|
}
|
|
|
|
// --- Extract icon (prefer png, fall back to jpg) ---
|
|
const iconPng = unzipped['icon.png'];
|
|
const iconJpg = unzipped['icon.jpg'];
|
|
const iconBytes = iconPng ?? iconJpg;
|
|
|
|
if (iconBytes && iconBytes.length > 0) {
|
|
const isPng = !!iconPng;
|
|
const mime = isPng ? 'image/png' : 'image/jpeg';
|
|
const ext = isPng ? '.png' : '.jpg';
|
|
meta.iconFile = new File([iconBytes], `icon${ext}`, { type: mime });
|
|
}
|
|
|
|
return meta;
|
|
}
|