From 7690ba766e4563837fbd51ffd41577e2869da2b8 Mon Sep 17 00:00:00 2001 From: codecolorist Date: Tue, 14 Apr 2026 13:56:08 +0000 Subject: [PATCH] Fix base path and restore wasm engine --- next.config.ts | 4 ++ src/lib/engine/index.ts | 24 +++---- src/lib/engine/kv.ts | 6 +- src/lib/engine/wasm.ts | 144 +++++++++++++++++++++++++--------------- 4 files changed, 107 insertions(+), 71 deletions(-) diff --git a/next.config.ts b/next.config.ts index 65c102b..1d9b582 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,11 @@ import type { NextConfig } from "next"; +const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; + const nextConfig: NextConfig = { output: "export", + basePath, + assetPrefix: basePath || undefined, }; export default nextConfig; diff --git a/src/lib/engine/index.ts b/src/lib/engine/index.ts index 1c388fb..7954c94 100644 --- a/src/lib/engine/index.ts +++ b/src/lib/engine/index.ts @@ -1,31 +1,23 @@ import type { Engine } from "./types"; import { WASMEngine } from "./wasm"; +import { checkWASMSupport as checkSQLiteWASMSupport } from "./wasm"; import { KVEngine } from "./kv"; let wasmSupported: boolean | null = null; +const wasmDisabled = process.env.NEXT_PUBLIC_USE_WASM === "0"; async function checkWASMSupport(): Promise { if (wasmSupported !== null) return wasmSupported; - try { - if (typeof WebAssembly === "undefined") { - wasmSupported = false; - return false; - } - - await WebAssembly.instantiate(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); - - await import("@sqlite.org/sqlite-wasm"); - - wasmSupported = true; - return true; - } catch { - wasmSupported = false; - return false; - } + wasmSupported = await checkSQLiteWASMSupport(); + return wasmSupported; } export async function createEngine(group: string): Promise { + if (wasmDisabled) { + return new KVEngine(group); + } + const supported = await checkWASMSupport(); if (supported) { return new WASMEngine(); diff --git a/src/lib/engine/kv.ts b/src/lib/engine/kv.ts index 76639f7..976ac10 100644 --- a/src/lib/engine/kv.ts +++ b/src/lib/engine/kv.ts @@ -1,6 +1,6 @@ import type { Engine } from "./types"; import type { OS } from "@/lib/types"; -import { addBasePath, dataBaseURL } from "@/lib/env"; +import { dataBaseURL } from "@/lib/env"; import { fetchText, fetchLines } from "@/lib/client"; interface KVRecord { @@ -55,14 +55,14 @@ export class KVEngine implements Engine { } async listOS(): Promise { - const list = await fetchText(addBasePath(`${this.#baseURL}/list.json`)); + const list = await fetchText(`${this.#baseURL}/list.json`); return JSON.parse(list); } async getPaths(build: string): Promise { const os = await this.findOS(build); const tag = `${os.version}_${build}`; - return fetchLines(addBasePath(`${this.#baseURL}/${tag}/paths.txt`)); + return fetchLines(`${this.#baseURL}/${tag}/paths.txt`); } async getBinaryXML(build: string, path: string): Promise { diff --git a/src/lib/engine/wasm.ts b/src/lib/engine/wasm.ts index a278208..4379012 100644 --- a/src/lib/engine/wasm.ts +++ b/src/lib/engine/wasm.ts @@ -2,40 +2,80 @@ import type { Engine } from "./types"; import type { OS } from "@/lib/types"; import { dataBaseURL } from "@/lib/env"; +type SQLite3Database = { + exec: (options: { + sql: string; + bind?: unknown[]; + rowMode: "array"; + returnValue: "resultRows"; + }) => unknown[][]; + close: () => void; +}; + type SQLite3API = { - Database: new (data: ArrayLike) => { - exec: ( - sql: string, - bind?: unknown[] - ) => { - columns: string[]; - rows: unknown[][]; - }[]; - close: () => void; + oo1: { + DB: new (filename?: string, flags?: string, vfs?: string) => SQLite3Database; + }; + capi: { + sqlite3_js_posix_create_file: ( + filename: string, + data: Uint8Array | ArrayBuffer, + dataLen?: number + ) => void; }; }; let sqlite3Module: SQLite3API | null = null; -let dbInstance: InstanceType | null = null; +let dbInstance: SQLite3Database | null = null; let dbReady = false; async function loadSQLite(): Promise { if (sqlite3Module) return sqlite3Module; const sqliteModule = await import("@sqlite.org/sqlite-wasm"); - sqlite3Module = (sqliteModule.default || sqliteModule) as unknown as SQLite3API; + const initSQLite = sqliteModule.default; + sqlite3Module = (await initSQLite()) as unknown as SQLite3API; return sqlite3Module; } -async function getDB(): Promise> { +export async function checkWASMSupport(): Promise { + try { + if (typeof WebAssembly === "undefined") { + return false; + } + + await WebAssembly.instantiate( + Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00) + ); + await loadSQLite(); + return true; + } catch { + return false; + } +} + +async function getDB(): Promise { if (dbReady && dbInstance) return dbInstance; const sqlite3 = await loadSQLite(); const response = await fetch(`${dataBaseURL()}/ent.db`); + if (!response.ok) { + throw new Error( + `Failed to fetch SQLite database: ${response.status} ${response.statusText}` + ); + } const buffer = await response.arrayBuffer(); + const bytes = new Uint8Array(buffer); + const filename = "/ent.db"; - dbInstance = new sqlite3.Database(new Uint8Array(buffer)); + try { + sqlite3.capi.sqlite3_js_posix_create_file(filename, bytes, bytes.byteLength); + } catch { + // Ignore duplicate file creation in hot-reload or repeated initialization cases. + } + + dbInstance = new sqlite3.oo1.DB(filename, "r"); dbReady = true; return dbInstance; } @@ -43,64 +83,64 @@ async function getDB(): Promise> { export class WASMEngine implements Engine { async listOS(): Promise { const db = await getDB(); - const results = db.exec( - "SELECT name, version, build, devices FROM os ORDER BY version DESC" - ); - if (!results.length) return []; + const rows = db.exec({ + sql: "SELECT name, version, build, devices FROM os ORDER BY version DESC", + rowMode: "array", + returnValue: "resultRows", + }); - const cols = results[0].columns; - const nameIdx = cols.indexOf("name"); - const versionIdx = cols.indexOf("version"); - const buildIdx = cols.indexOf("build"); - const devicesIdx = cols.indexOf("devices"); - - return results[0].rows.map((row) => ({ - name: row[nameIdx] as string, - version: row[versionIdx] as string, - build: row[buildIdx] as string, - devices: JSON.parse(row[devicesIdx] as string), + return rows.map((row) => ({ + name: row[0] as string, + version: row[1] as string, + build: row[2] as string, + devices: JSON.parse(row[3] as string), })); } async getPaths(build: string): Promise { const db = await getDB(); - const results = db.exec( - `SELECT path FROM bin JOIN os ON bin.osid=os.id WHERE os.build=?`, - [build] - ); - if (!results.length) return []; - return results[0].rows.map((row) => row[0] as string); + const rows = db.exec({ + sql: `SELECT path FROM bin JOIN os ON bin.osid=os.id WHERE os.build=?`, + bind: [build], + rowMode: "array", + returnValue: "resultRows", + }); + return rows.map((row) => row[0] as string); } async getBinaryXML(build: string, path: string): Promise { const db = await getDB(); - const results = db.exec( - `SELECT xml FROM bin JOIN os ON bin.osid=os.id WHERE os.build=? AND bin.path=?`, - [build, path] - ); - if (!results.length || !results[0].rows.length) { + const rows = db.exec({ + sql: `SELECT xml FROM bin JOIN os ON bin.osid=os.id WHERE os.build=? AND bin.path=?`, + bind: [build, path], + rowMode: "array", + returnValue: "resultRows", + }); + if (!rows.length) { throw new Error(`Binary not found: ${path}`); } - return results[0].rows[0][0] as string; + return rows[0][0] as string; } async getKeys(build: string): Promise { const db = await getDB(); - const results = db.exec( - `SELECT DISTINCT key FROM pair JOIN bin ON pair.binid=bin.id JOIN os ON bin.osid=os.id WHERE os.build=?`, - [build] - ); - if (!results.length) return []; - return results[0].rows.map((row) => row[0] as string); + const rows = db.exec({ + sql: `SELECT DISTINCT key FROM pair JOIN bin ON pair.binid=bin.id JOIN os ON bin.osid=os.id WHERE os.build=?`, + bind: [build], + rowMode: "array", + returnValue: "resultRows", + }); + return rows.map((row) => row[0] as string); } async getPathsForKey(build: string, key: string): Promise { const db = await getDB(); - const results = db.exec( - `SELECT path FROM bin JOIN pair ON bin.id=pair.binid JOIN os ON bin.osid=os.id WHERE os.build=? AND pair.key=?`, - [build, key] - ); - if (!results.length) return []; - return results[0].rows.map((row) => row[0] as string); + const rows = db.exec({ + sql: `SELECT path FROM bin JOIN pair ON bin.id=pair.binid JOIN os ON bin.osid=os.id WHERE os.build=? AND pair.key=?`, + bind: [build, key], + rowMode: "array", + returnValue: "resultRows", + }); + return rows.map((row) => row[0] as string); } }