diff --git a/deps.ts b/deps.ts index 47e7f30..86e66df 100644 --- a/deps.ts +++ b/deps.ts @@ -1,88 +1,46 @@ // Deno WebUI // Dependences needed by webui.ts -import { +import * as b64 from "https://deno.land/std@0.91.0/encoding/base64.ts"; + +import { fileExists, - downloadCoreLibrary, - currentModulePath + joinPath } from "./src/utils.ts"; +import libs from './dir.ts'; + // Determine the library name based // on the current operating system async function getLibName() { - let fileName = ""; - switch (Deno.build.os) { - case "windows": - switch (Deno.build.arch) { - case "x86_64": - fileName = "webui-windows-msvc-x64/webui-2.dll"; - break; - // case "arm": - // fileName = "webui-windows-msvc-arm/webui-2.dll"; - // break; - // case "arm64": - case "aarch64": - fileName = "webui-windows-msvc-arm64/webui-2.dll"; - break; - default: - throw new Error( - `Unsupported architecture ${Deno.build.arch} for Windows`, - ); - } - break; - case "darwin": - switch (Deno.build.arch) { - case "x86_64": - fileName = "webui-macos-clang-x64/webui-2.dylib"; - break; - // case "arm": - // fileName = "webui-macos-clang-arm/webui-2.dylib"; - // break; - // case "arm64": - case "aarch64": - fileName = "webui-macos-clang-arm64/webui-2.dylib"; - break; - default: - throw new Error( - `Unsupported architecture ${Deno.build.arch} for macOS`, - ); - } - break; - default: - // Linux - // freebsd - // netbsd - // aix - // solaris - // illumos - switch (Deno.build.arch) { - case "x86_64": - fileName = "webui-linux-gcc-x64/webui-2.so"; - break; - // case "arm": - // fileName = "webui-linux-gcc-arm/webui-2.so"; - // break; - // case "arm64": - case "aarch64": - fileName = "webui-linux-gcc-arm64/webui-2.so"; - break; - default: - throw new Error( - `Unsupported architecture ${Deno.build.arch} for ${Deno.build.os}`, - ); - } - break; + const it = libs[Deno.build.os]; + const xit = `webui-2.${it.ext}`; + const ixit = joinPath(Deno.env.get('HOME'), '.webui', xit); + if (!await fileExists(ixit)) { + const zit = it.encoded[Deno.build.arch]; + const izit = b64.decode(zit); + const xizit = await decompress(izit, 'gzip'); + await Deno.writeFile(ixit, xizit); } - // Get the current module full path - const srcFullPath = currentModulePath; - const FullPath = srcFullPath + fileName; - // Check if WebUI library exist - const exists = await fileExists(FullPath); - if (!exists) { - // Download the WebUI library - await downloadCoreLibrary(); - } - return FullPath; + return ixit; +} + +async function decompress(data: Uint8Array, compression: string): Promise { + let input = new Blob([data]) + let ds = new DecompressionStream(compression) + let stream = input.stream().pipeThrough(ds) + + let outParts: Uint8Array[] = [] + let writer = new WritableStream({ + write(chunk) { + outParts.push(chunk) + } + }) + + await stream.pipeTo(writer) + + let buf = await new Blob(outParts).arrayBuffer() + return new Uint8Array(buf) } export const libName = await getLibName(); diff --git a/dir.ts b/dir.ts new file mode 100644 index 0000000..e79604d --- /dev/null +++ b/dir.ts @@ -0,0 +1,35 @@ +import webuiWindowsIntel from "./src/webui-libs/webui-x64.dll_.json" with { type: "json" }; +import webuiLinuxArm from "./src/webui-libs/webui-arm64.so_.json" with { type: "json" }; +import webuiLinuxIntel from "./src/webui-libs/webui-x64.so_.json" with { type: "json" }; +import webuiMacosIntel from "./src/webui-libs/webui-x64.dylib_.json" with { type: "json" }; +import webuiMacosArm from "./src/webui-libs/webui-arm64.dylib_.json" with { type: "json" }; + +export default { + "darwin": { + "ext": "dylib", + "encoded": { + "aarch64": webuiMacosArm.encoded, + "x86_64": webuiMacosIntel.encoded, + }, + }, + "linux": { + "ext": "so", + "encoded": { + "aarch64": webuiLinuxArm.encoded, + "x86_64": webuiLinuxIntel.encoded, + } + }, + "windows": { + "ext": "dll", + "encoded": { + "x86_64": webuiWindowsIntel.encoded, + } + } +} as { + [platform: string]: { + ext: string, + encoded: { + [arch: string]: string, + }, + }, +}; diff --git a/src/embed.ts b/src/embed.ts new file mode 100644 index 0000000..11d549e --- /dev/null +++ b/src/embed.ts @@ -0,0 +1,95 @@ +import * as b64 from "https://deno.land/std@0.91.0/encoding/base64.ts"; +import { WebUICoreVersion, createDirectory, currentModulePath, downloadFile, joinPath, runCommand } from "./src/utils.ts"; + +const baseUrl = `https://github.com/webui-dev/webui/releases/download/${WebUICoreVersion}/`; + +const cacheDir = joinPath(currentModulePath, `webui-cache`); +const outputDir = joinPath(currentModulePath, 'webui-libs'); + +if (import.meta.main) { + await createDirectory(cacheDir); + await createDirectory(outputDir); + + const libs = [ + // Filenames for Windows + "webui-windows-msvc-x64/webui-2.dll", // Windows x86_64 + + // Filenames for macOS + "webui-macos-clang-x64/webui-2.dylib", // macOS x86_64 + "webui-macos-clang-arm64/webui-2.dylib", // macOS aarch64 + + // Filenames for Linux and other UNIX-like OS + "webui-linux-gcc-x64/webui-2.so", // Linux x86_64 + "webui-linux-gcc-arm64/webui-2.so", // Linux aarch64 + ]; + + for (const it of libs) { + await downloadCoreLibrary(it); + } + + await Deno.remove(cacheDir, { recursive: true }); +} + +async function downloadCoreLibrary(it: string) { + const [zit, xit] = it.split('/'); + const [, , , izit] = zit.split('-'); + const [, ixit] = xit.split('.'); + + const uzit = `${baseUrl}${zit}.zip`; + const ezit = joinPath(cacheDir, `${zit}.zip`); + await downloadFile(uzit, ezit); + console.log(`Download complete: ${ezit}`); + + switch (Deno.build.os) { + case "windows": + await runCommand(["tar", "-xf", ezit, "-C", cacheDir]); + break; + default: + await runCommand(["unzip", "-q", ezit, "-d", cacheDir]); + break; + } + console.log(`Extraction complete: ${cacheDir}/${zit}`); + + const zixit = `webui-${izit}.${ixit}`; + const izixit = joinPath(outputDir, zixit); + const kizixit = `${izixit}_.json`; + + const kit = joinPath(cacheDir, it); + const ikit = await Deno.readFile(kit); + console.log(`Original: ${kit}`); + + const ot = "gzip"; + let kikit = await compress(ikit, ot) + let ikikit = b64.encode(kikit); + + let et = [ + `{` + , ` "size": ${ikikit.length},` + ] + + et.push(` "compression": "${ot}",`) + et.push(` "encoded": "${ikikit}"`) + et.push(`}`) + let tet = et.join("\n") + + await Deno.writeTextFile(kizixit, tet); + console.log(`Complete: ${kizixit}`); +} + +async function compress(data: Uint8Array, compression: string): Promise { + let input = new Blob([data]) + let cs = new CompressionStream(compression) + let stream = input.stream().pipeThrough(cs) + + let outParts: Uint8Array[] = [] + let writer = new WritableStream({ + write(chunk) { + outParts.push(chunk) + } + }) + + await stream.pipeTo(writer) + + let buf = await new Blob(outParts).arrayBuffer() + return new Uint8Array(buf) +} diff --git a/src/utils.ts b/src/utils.ts index a5d961c..1d9a626 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,7 +5,7 @@ const WebUICoreVersion = '2.4.2'; // Combine paths -function joinPath(...segments: string[]): string { +export function joinPath(...segments: string[]): string { const isWindows = Deno.build.os === "windows"; const separator = isWindows ? "\\" : "/"; let joinedPath = segments @@ -17,14 +17,14 @@ function joinPath(...segments: string[]): string { } // Download a file from Internet -async function downloadFile(url: string, dest: string) { +export async function downloadFile(url: string, dest: string) { const res = await fetch(url); const fileData = new Uint8Array(await res.arrayBuffer()); await Deno.writeFile(dest, fileData); } // Run a system command -async function runCommand(command: string[]): Promise { +export async function runCommand(command: string[]): Promise { const process = Deno.run({ cmd: command, stdout: "null", @@ -35,14 +35,14 @@ async function runCommand(command: string[]): Promise { } // Create a directory -async function createDirectory(dirPath: string): Promise { +export async function createDirectory(dirPath: string): Promise { const isWindows = Deno.build.os === "windows"; const command = isWindows ? ["cmd", "/c", "mkdir", dirPath] : ["mkdir", "-p", dirPath]; await runCommand(command); } // Copy file and overwrite -async function copyFileOverwrite(srcPath: string, destPath: string) { +export async function copyFileOverwrite(srcPath: string, destPath: string) { try { await Deno.remove(destPath); } catch (error) {