Fix base path and restore wasm engine

This commit is contained in:
codecolorist committed 2026-04-14 13:56:08 +00:00
1 parent 32c2b01062
commit 7690ba766e
4 files changed
+107 -71

No files matched your search

+4
View File
@@ -1,7 +1,11 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: "export", output: "export",
basePath,
assetPrefix: basePath || undefined,
}; };
export default nextConfig; export default nextConfig;
+8 -16
View File
@@ -1,31 +1,23 @@
import type { Engine } from "./types"; import type { Engine } from "./types";
import { WASMEngine } from "./wasm"; import { WASMEngine } from "./wasm";
import { checkWASMSupport as checkSQLiteWASMSupport } from "./wasm";
import { KVEngine } from "./kv"; import { KVEngine } from "./kv";
let wasmSupported: boolean | null = null; let wasmSupported: boolean | null = null;
const wasmDisabled = process.env.NEXT_PUBLIC_USE_WASM === "0";
async function checkWASMSupport(): Promise<boolean> { async function checkWASMSupport(): Promise<boolean> {
if (wasmSupported !== null) return wasmSupported; if (wasmSupported !== null) return wasmSupported;
try { wasmSupported = await checkSQLiteWASMSupport();
if (typeof WebAssembly === "undefined") { return wasmSupported;
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;
}
} }
export async function createEngine(group: string): Promise<Engine> { export async function createEngine(group: string): Promise<Engine> {
if (wasmDisabled) {
return new KVEngine(group);
}
const supported = await checkWASMSupport(); const supported = await checkWASMSupport();
if (supported) { if (supported) {
return new WASMEngine(); return new WASMEngine();
+3 -3
View File
@@ -1,6 +1,6 @@
import type { Engine } from "./types"; import type { Engine } from "./types";
import type { OS } from "@/lib/types"; import type { OS } from "@/lib/types";
import { addBasePath, dataBaseURL } from "@/lib/env"; import { dataBaseURL } from "@/lib/env";
import { fetchText, fetchLines } from "@/lib/client"; import { fetchText, fetchLines } from "@/lib/client";
interface KVRecord { interface KVRecord {
@@ -55,14 +55,14 @@ export class KVEngine implements Engine {
} }
async listOS(): Promise<OS[]> { async listOS(): Promise<OS[]> {
const list = await fetchText(addBasePath(`${this.#baseURL}/list.json`)); const list = await fetchText(`${this.#baseURL}/list.json`);
return JSON.parse(list); return JSON.parse(list);
} }
async getPaths(build: string): Promise<string[]> { async getPaths(build: string): Promise<string[]> {
const os = await this.findOS(build); const os = await this.findOS(build);
const tag = `${os.version}_${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<string> { async getBinaryXML(build: string, path: string): Promise<string> {
+92 -52
View File
@@ -2,40 +2,80 @@ import type { Engine } from "./types";
import type { OS } from "@/lib/types"; import type { OS } from "@/lib/types";
import { dataBaseURL } from "@/lib/env"; import { dataBaseURL } from "@/lib/env";
type SQLite3Database = {
exec: (options: {
sql: string;
bind?: unknown[];
rowMode: "array";
returnValue: "resultRows";
}) => unknown[][];
close: () => void;
};
type SQLite3API = { type SQLite3API = {
Database: new (data: ArrayLike<number | bigint>) => { oo1: {
exec: ( DB: new (filename?: string, flags?: string, vfs?: string) => SQLite3Database;
sql: string, };
bind?: unknown[] capi: {
) => { sqlite3_js_posix_create_file: (
columns: string[]; filename: string,
rows: unknown[][]; data: Uint8Array | ArrayBuffer,
}[]; dataLen?: number
close: () => void; ) => void;
}; };
}; };
let sqlite3Module: SQLite3API | null = null; let sqlite3Module: SQLite3API | null = null;
let dbInstance: InstanceType<SQLite3API["Database"]> | null = null; let dbInstance: SQLite3Database | null = null;
let dbReady = false; let dbReady = false;
async function loadSQLite(): Promise<SQLite3API> { async function loadSQLite(): Promise<SQLite3API> {
if (sqlite3Module) return sqlite3Module; if (sqlite3Module) return sqlite3Module;
const sqliteModule = await import("@sqlite.org/sqlite-wasm"); 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; return sqlite3Module;
} }
async function getDB(): Promise<InstanceType<SQLite3API["Database"]>> { export async function checkWASMSupport(): Promise<boolean> {
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<SQLite3Database> {
if (dbReady && dbInstance) return dbInstance; if (dbReady && dbInstance) return dbInstance;
const sqlite3 = await loadSQLite(); const sqlite3 = await loadSQLite();
const response = await fetch(`${dataBaseURL()}/ent.db`); 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 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; dbReady = true;
return dbInstance; return dbInstance;
} }
@@ -43,64 +83,64 @@ async function getDB(): Promise<InstanceType<SQLite3API["Database"]>> {
export class WASMEngine implements Engine { export class WASMEngine implements Engine {
async listOS(): Promise<OS[]> { async listOS(): Promise<OS[]> {
const db = await getDB(); const db = await getDB();
const results = db.exec( const rows = db.exec({
"SELECT name, version, build, devices FROM os ORDER BY version DESC" sql: "SELECT name, version, build, devices FROM os ORDER BY version DESC",
); rowMode: "array",
if (!results.length) return []; returnValue: "resultRows",
});
const cols = results[0].columns; return rows.map((row) => ({
const nameIdx = cols.indexOf("name"); name: row[0] as string,
const versionIdx = cols.indexOf("version"); version: row[1] as string,
const buildIdx = cols.indexOf("build"); build: row[2] as string,
const devicesIdx = cols.indexOf("devices"); devices: JSON.parse(row[3] as string),
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),
})); }));
} }
async getPaths(build: string): Promise<string[]> { async getPaths(build: string): Promise<string[]> {
const db = await getDB(); const db = await getDB();
const results = db.exec( const rows = db.exec({
`SELECT path FROM bin JOIN os ON bin.osid=os.id WHERE os.build=?`, sql: `SELECT path FROM bin JOIN os ON bin.osid=os.id WHERE os.build=?`,
[build] bind: [build],
); rowMode: "array",
if (!results.length) return []; returnValue: "resultRows",
return results[0].rows.map((row) => row[0] as string); });
return rows.map((row) => row[0] as string);
} }
async getBinaryXML(build: string, path: string): Promise<string> { async getBinaryXML(build: string, path: string): Promise<string> {
const db = await getDB(); const db = await getDB();
const results = db.exec( const rows = db.exec({
`SELECT xml FROM bin JOIN os ON bin.osid=os.id WHERE os.build=? AND bin.path=?`, sql: `SELECT xml FROM bin JOIN os ON bin.osid=os.id WHERE os.build=? AND bin.path=?`,
[build, path] bind: [build, path],
); rowMode: "array",
if (!results.length || !results[0].rows.length) { returnValue: "resultRows",
});
if (!rows.length) {
throw new Error(`Binary not found: ${path}`); 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<string[]> { async getKeys(build: string): Promise<string[]> {
const db = await getDB(); const db = await getDB();
const results = db.exec( const rows = db.exec({
`SELECT DISTINCT key FROM pair JOIN bin ON pair.binid=bin.id JOIN os ON bin.osid=os.id WHERE os.build=?`, sql: `SELECT DISTINCT key FROM pair JOIN bin ON pair.binid=bin.id JOIN os ON bin.osid=os.id WHERE os.build=?`,
[build] bind: [build],
); rowMode: "array",
if (!results.length) return []; returnValue: "resultRows",
return results[0].rows.map((row) => row[0] as string); });
return rows.map((row) => row[0] as string);
} }
async getPathsForKey(build: string, key: string): Promise<string[]> { async getPathsForKey(build: string, key: string): Promise<string[]> {
const db = await getDB(); const db = await getDB();
const results = db.exec( const rows = 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=?`, 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=?`,
[build, key] bind: [build, key],
); rowMode: "array",
if (!results.length) return []; returnValue: "resultRows",
return results[0].rows.map((row) => row[0] as string); });
return rows.map((row) => row[0] as string);
} }
} }