diff --git a/packages/types/src/view-props.d.ts b/packages/types/src/view-props.d.ts index 59d5ffded52..c8375ba2551 100644 --- a/packages/types/src/view-props.d.ts +++ b/packages/types/src/view-props.d.ts @@ -77,7 +77,8 @@ export type TIssueParams = | "show_empty_groups" | "cursor" | "per_page" - | "issue_type"; + | "issue_type" + | "layout"; export type TCalendarLayouts = "month" | "week"; diff --git a/web/core/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx b/web/core/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx index 322c0005460..641a82be4fc 100644 --- a/web/core/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx +++ b/web/core/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx @@ -4,6 +4,7 @@ import { useParams } from "next/navigation"; import useSWR from "swr"; // mobx store // components +import { LogoSpinner } from "@/components/common"; import { ArchivedIssueListLayout, ArchivedIssueAppliedFiltersRoot, IssuePeekOverview } from "@/components/issues"; import { EIssuesStoreType } from "@/constants/issue"; // ui @@ -16,7 +17,7 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => { // hooks const { issuesFilter } = useIssues(EIssuesStoreType.ARCHIVED); - useSWR( + const { isLoading } = useSWR( workspaceSlug && projectId ? `ARCHIVED_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}` : null, async () => { if (workspaceSlug && projectId) { @@ -26,7 +27,17 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => { { revalidateIfStale: false, revalidateOnFocus: false } ); + const issueFilters = issuesFilter?.getIssueFilters(projectId?.toString()); + if (!workspaceSlug || !projectId) return <>; + + if (isLoading && !issueFilters) + return ( +
+ +
+ ); + return ( diff --git a/web/core/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/core/components/issues/issue-layouts/roots/cycle-layout-root.tsx index 4da446aab88..1b69156ba11 100644 --- a/web/core/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/core/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -5,6 +5,7 @@ import { useParams } from "next/navigation"; import useSWR from "swr"; // hooks // components +import { LogoSpinner } from "@/components/common"; import { TransferIssues, TransferIssuesModal } from "@/components/cycles"; import { CycleAppliedFiltersRoot, @@ -50,7 +51,7 @@ export const CycleLayoutRoot: React.FC = observer(() => { // state const [transferIssuesModal, setTransferIssuesModal] = useState(false); - useSWR( + const { isLoading } = useSWR( workspaceSlug && projectId && cycleId ? `CYCLE_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}_${cycleId.toString()}` : null, @@ -62,7 +63,8 @@ export const CycleLayoutRoot: React.FC = observer(() => { { revalidateIfStale: false, revalidateOnFocus: false } ); - const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout; + const issueFilters = issuesFilter?.getIssueFilters(cycleId?.toString()); + const activeLayout = issueFilters?.displayFilters?.layout; const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined; const cycleStatus = cycleDetails?.status?.toLocaleLowerCase() ?? "draft"; @@ -75,6 +77,13 @@ export const CycleLayoutRoot: React.FC = observer(() => { if (!workspaceSlug || !projectId || !cycleId) return <>; + if (isLoading && !issueFilters) + return ( +
+ +
+ ); + return ( setTransferIssuesModal(false)} isOpen={transferIssuesModal} /> diff --git a/web/core/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx b/web/core/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx index a4b78be80d1..c13ff104e16 100644 --- a/web/core/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx +++ b/web/core/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx @@ -2,6 +2,7 @@ import React from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import useSWR from "swr"; +import { LogoSpinner } from "@/components/common"; import { IssuePeekOverview } from "@/components/issues/peek-overview"; import { EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue"; // hooks @@ -30,7 +31,7 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => { // hooks const { issuesFilter } = useIssues(EIssuesStoreType.DRAFT); - useSWR( + const { isLoading } = useSWR( workspaceSlug && projectId ? `DRAFT_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}` : null, async () => { if (workspaceSlug && projectId) { @@ -40,10 +41,18 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => { { revalidateIfStale: false, revalidateOnFocus: false } ); - const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined; + const issueFilters = issuesFilter?.getIssueFilters(projectId?.toString()); + const activeLayout = issueFilters?.displayFilters?.layout || undefined; if (!workspaceSlug || !projectId) return <>; + if (isLoading && !issueFilters) + return ( +
+ +
+ ); + return (
diff --git a/web/core/components/issues/issue-layouts/roots/module-layout-root.tsx b/web/core/components/issues/issue-layouts/roots/module-layout-root.tsx index 70d7dce43ff..b4eb7c464cd 100644 --- a/web/core/components/issues/issue-layouts/roots/module-layout-root.tsx +++ b/web/core/components/issues/issue-layouts/roots/module-layout-root.tsx @@ -5,6 +5,7 @@ import useSWR from "swr"; // mobx store // components import { Row, ERowVariant } from "@plane/ui"; +import { LogoSpinner } from "@/components/common"; import { IssuePeekOverview, ModuleAppliedFiltersRoot, @@ -43,7 +44,7 @@ export const ModuleLayoutRoot: React.FC = observer(() => { // hooks const { issuesFilter } = useIssues(EIssuesStoreType.MODULE); - useSWR( + const { isLoading } = useSWR( workspaceSlug && projectId && moduleId ? `MODULE_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}_${moduleId.toString()}` : null, @@ -55,9 +56,18 @@ export const ModuleLayoutRoot: React.FC = observer(() => { { revalidateIfStale: false, revalidateOnFocus: false } ); + const issueFilters = issuesFilter?.getIssueFilters(moduleId?.toString()); + if (!workspaceSlug || !projectId || !moduleId) return <>; - const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout || undefined; + if (isLoading && !issueFilters) + return ( +
+ +
+ ); + + const activeLayout = issueFilters?.displayFilters?.layout || undefined; return ( diff --git a/web/core/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/core/components/issues/issue-layouts/roots/project-layout-root.tsx index 24009ff8d7d..853f13dd4f2 100644 --- a/web/core/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/core/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -6,6 +6,7 @@ import { useParams } from "next/navigation"; import useSWR from "swr"; // components import { Spinner } from "@plane/ui"; +import { LogoSpinner } from "@/components/common"; import { ListLayout, CalendarLayout, @@ -44,7 +45,7 @@ export const ProjectLayoutRoot: FC = observer(() => { // hooks const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT); - useSWR( + const { isLoading } = useSWR( workspaceSlug && projectId ? `PROJECT_ISSUES_${workspaceSlug}_${projectId}` : null, async () => { if (workspaceSlug && projectId) { @@ -54,10 +55,18 @@ export const ProjectLayoutRoot: FC = observer(() => { { revalidateIfStale: false, revalidateOnFocus: false } ); - const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout; + const issueFilters = issuesFilter?.getIssueFilters(projectId?.toString()); + const activeLayout = issueFilters?.displayFilters?.layout; if (!workspaceSlug || !projectId) return <>; + if (isLoading && !issueFilters) + return ( +
+ +
+ ); + return (
diff --git a/web/core/components/issues/issue-layouts/roots/project-view-layout-root.tsx b/web/core/components/issues/issue-layouts/roots/project-view-layout-root.tsx index d04089a7e1a..8c84417578d 100644 --- a/web/core/components/issues/issue-layouts/roots/project-view-layout-root.tsx +++ b/web/core/components/issues/issue-layouts/roots/project-view-layout-root.tsx @@ -53,11 +53,12 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => { { revalidateIfStale: false, revalidateOnFocus: false } ); - const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout; + const issueFilters = issuesFilter?.getIssueFilters(viewId?.toString()); + const activeLayout = issueFilters?.displayFilters?.layout; if (!workspaceSlug || !projectId || !viewId) return <>; - if (isLoading) { + if (isLoading && !issueFilters) { return (
diff --git a/web/core/local-db/storage.sqlite.ts b/web/core/local-db/storage.sqlite.ts index 099063aca11..224b5013d01 100644 --- a/web/core/local-db/storage.sqlite.ts +++ b/web/core/local-db/storage.sqlite.ts @@ -1,4 +1,5 @@ -import * as Sentry from "@sentry/nextjs"; +import { getActiveSpan, startSpan } from "@sentry/nextjs"; +import * as Comlink from "comlink"; import set from "lodash/set"; // plane import { EIssueGroupBYServerToProperty } from "@plane/constants"; @@ -16,15 +17,11 @@ import { loadWorkSpaceData } from "./utils/load-workspace"; import { issueFilterCountQueryConstructor, issueFilterQueryConstructor } from "./utils/query-constructor"; import { runQuery } from "./utils/query-executor"; import { createTables } from "./utils/tables"; -import { getGroupedIssueResults, getSubGroupedIssueResults, log, logError, logInfo } from "./utils/utils"; - -declare module "@sqlite.org/sqlite-wasm" { - export function sqlite3Worker1Promiser(...args: any): any; -} +import { clearOPFS, getGroupedIssueResults, getSubGroupedIssueResults, log, logError } from "./utils/utils"; const DB_VERSION = 1; -const PAGE_SIZE = 1000; -const BATCH_SIZE = 200; +const PAGE_SIZE = 500; +const BATCH_SIZE = 500; type TProjectStatus = { issues: { status: undefined | "loading" | "ready" | "error" | "syncing"; sync: Promise | undefined }; @@ -43,6 +40,9 @@ export class Storage { } reset = () => { + if (this.db) { + this.db.close(); + } this.db = null; this.status = undefined; this.projectStatus = {}; @@ -51,10 +51,8 @@ export class Storage { clearStorage = async () => { try { - const storageManager = window.navigator.storage; - const fileSystemDirectoryHandle = await storageManager.getDirectory(); - //@ts-expect-error , clear local issue cache - await fileSystemDirectoryHandle.remove({ recursive: true }); + await this.db.close(); + await clearOPFS(); this.reset(); } catch (e) { console.error("Error clearing sqlite sync storage", e); @@ -62,13 +60,13 @@ export class Storage { }; initialize = async (workspaceSlug: string): Promise => { - if (document.hidden || !rootStore.user.localDBEnabled) return false; // return if the window gets hidden + if (!rootStore.user.localDBEnabled) return false; // return if the window gets hidden if (workspaceSlug !== this.workspaceSlug) { this.reset(); } try { - await Sentry.startSpan({ name: "INIT_DB" }, async () => await this._initialize(workspaceSlug)); + await startSpan({ name: "INIT_DB" }, async () => await this._initialize(workspaceSlug)); return true; } catch (err) { logError(err); @@ -91,71 +89,61 @@ export class Storage { return false; } - logInfo("Loading and initializing SQLite3 module..."); - - this.workspaceSlug = workspaceSlug; - this.dbName = workspaceSlug; - const { sqlite3Worker1Promiser } = await import("@sqlite.org/sqlite-wasm"); - try { - const promiser: any = await new Promise((resolve) => { - const _promiser = sqlite3Worker1Promiser({ - onready: () => resolve(_promiser), - }); - }); + const { DBClass } = await import("./worker/db"); + const worker = new Worker(new URL("./worker/db.ts", import.meta.url)); + const MyWorker = Comlink.wrap(worker); - const configResponse = await promiser("config-get", {}); - log("Running SQLite3 version", configResponse.result.version.libVersion); + // Add cleanup on window unload + window.addEventListener("unload", () => worker.terminate()); + + this.workspaceSlug = workspaceSlug; + this.dbName = workspaceSlug; + const instance = await new MyWorker(); + await instance.init(workspaceSlug); - const openResponse = await promiser("open", { - filename: `file:${this.dbName}.sqlite3?vfs=opfs`, - }); - const { dbId } = openResponse; this.db = { - dbId, - exec: async (val: any) => { - if (typeof val === "string") { - val = { sql: val }; - } - return promiser("exec", { dbId, ...val }); - }, + exec: instance.exec, + close: instance.close, }; - // dump DB of db version is matching - const dbVersion = await this.getOption("DB_VERSION"); - if (dbVersion !== "" && parseInt(dbVersion) !== DB_VERSION) { - await this.clearStorage(); - this.reset(); - await this._initialize(workspaceSlug); - return false; - } - - log( - "OPFS is available, created persisted database at", - openResponse.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, "$1") - ); this.status = "ready"; // Your SQLite code here. await createTables(); await this.setOption("DB_VERSION", DB_VERSION.toString()); - } catch (err) { - logError(err); - throw err; + return true; + } catch (error) { + this.status = "error"; + throw new Error(`Failed to initialize database worker: ${error}`); } - - return true; }; syncWorkspace = async () => { - if (document.hidden || !rootStore.user.localDBEnabled) return; // return if the window gets hidden - await Sentry.startSpan({ name: "LOAD_WS", attributes: { slug: this.workspaceSlug } }, async () => { - await loadWorkSpaceData(this.workspaceSlug); - }); + if (!rootStore.user.localDBEnabled) return; + const syncInProgress = await this.getIsWriteInProgress("sync_workspace"); + if (syncInProgress) { + log("Sync in progress, skipping"); + return; + } + try { + await startSpan({ name: "LOAD_WS", attributes: { slug: this.workspaceSlug } }, async () => { + this.setOption("sync_workspace", new Date().toUTCString()); + await loadWorkSpaceData(this.workspaceSlug); + this.deleteOption("sync_workspace"); + }); + } catch (e) { + logError(e); + this.deleteOption("sync_workspace"); + } }; syncProject = async (projectId: string) => { - if (document.hidden || !rootStore.user.localDBEnabled) return false; // return if the window gets hidden + if ( + // document.hidden || + !rootStore.user.localDBEnabled + ) + return false; // return if the window gets hidden // Load labels, members, states, modules, cycles await this.syncIssues(projectId); @@ -173,10 +161,11 @@ export class Storage { }; syncIssues = async (projectId: string) => { - if (document.hidden || !rootStore.user.localDBEnabled) return false; // return if the window gets hidden - + if (!rootStore.user.localDBEnabled || !this.db) { + return false; + } try { - const sync = Sentry.startSpan({ name: `SYNC_ISSUES` }, () => this._syncIssues(projectId)); + const sync = startSpan({ name: `SYNC_ISSUES` }, () => this._syncIssues(projectId)); this.setSync(projectId, sync); await sync; } catch (e) { @@ -186,12 +175,12 @@ export class Storage { }; _syncIssues = async (projectId: string) => { - const activeSpan = Sentry.getActiveSpan(); + const activeSpan = getActiveSpan(); log("### Sync started"); let status = this.getStatus(projectId); if (status === "loading" || status === "syncing") { - logInfo(`Project ${projectId} is already loading or syncing`); + log(`Project ${projectId} is already loading or syncing`); return; } const syncPromise = this.getSync(projectId); @@ -222,8 +211,8 @@ export class Storage { const issueService = new IssueService(); const response = await issueService.getIssuesForSync(this.workspaceSlug, projectId, queryParams); - addIssuesBulk(response.results, BATCH_SIZE); + await addIssuesBulk(response.results, BATCH_SIZE); if (response.total_pages > 1) { const promiseArray = []; for (let i = 1; i < response.total_pages; i++) { @@ -290,7 +279,7 @@ export class Storage { !rootStore.user.localDBEnabled ) { if (rootStore.user.localDBEnabled) { - logInfo(`Project ${projectId} is loading, falling back to server`); + log(`Project ${projectId} is loading, falling back to server`); } const issueService = new IssueService(); return await issueService.getIssuesFromServer(workspaceSlug, projectId, queries); @@ -310,11 +299,9 @@ export class Storage { const issueService = new IssueService(); return await issueService.getIssuesFromServer(workspaceSlug, projectId, queries); } - // const issuesRaw = await runQuery(query); const end = performance.now(); const { total_count } = count[0]; - // const total_count = 2300; const [pageSize, page, offset] = cursor.split(":"); @@ -347,7 +334,6 @@ export class Storage { Parsing: parsingEnd - parsingStart, Grouping: groupingEnd - grouping, }; - log(issueResults); if ((window as any).DEBUG) { console.table(times); } @@ -364,7 +350,7 @@ export class Storage { total_pages, }; - const activeSpan = Sentry.getActiveSpan(); + const activeSpan = getActiveSpan(); activeSpan?.setAttributes({ projectId, count: total_count, @@ -413,7 +399,7 @@ export class Storage { set(this.projectStatus, `${projectId}.issues.sync`, sync); }; - getOption = async (key: string, fallback = "") => { + getOption = async (key: string, fallback?: string | boolean | number) => { try { const options = await runQuery(`select * from options where key='${key}'`); if (options.length) { @@ -429,6 +415,9 @@ export class Storage { await runQuery(`insert or replace into options (key, value) values ('${key}', '${value}')`); }; + deleteOption = async (key: string) => { + await runQuery(` DELETE FROM options where key='${key}'`); + }; getOptions = async (keys: string[]) => { const options = await runQuery(`select * from options where key in ('${keys.join("','")}')`); return options.reduce((acc: any, option: any) => { @@ -436,6 +425,21 @@ export class Storage { return acc; }, {}); }; + + getIsWriteInProgress = async (op: string) => { + const writeStartTime = await this.getOption(op, false); + if (writeStartTime) { + // Check if it has been more than 5seconds + const current = new Date(); + const start = new Date(writeStartTime); + + if (current.getTime() - start.getTime() < 5000) { + return true; + } + return false; + } + return false; + }; } export const persistence = new Storage(); diff --git a/web/core/local-db/utils/indexes.ts b/web/core/local-db/utils/indexes.ts index 214bacb44f5..0b2e987ed37 100644 --- a/web/core/local-db/utils/indexes.ts +++ b/web/core/local-db/utils/indexes.ts @@ -1,6 +1,6 @@ import { persistence } from "../storage.sqlite"; +import { log } from "./utils"; -const log = console.log; export const createIssueIndexes = async () => { const columns = [ "state_id", @@ -34,10 +34,8 @@ export const createWorkSpaceIndexes = async () => { const promises: Promise[] = []; // Labels promises.push(persistence.db.exec({ sql: `CREATE INDEX labels_name_idx ON labels (id,name,project_id)` })); - // Modules promises.push(persistence.db.exec({ sql: `CREATE INDEX modules_name_idx ON modules (id,name,project_id)` })); - // States promises.push(persistence.db.exec({ sql: `CREATE INDEX states_name_idx ON states (id,name,project_id)` })); // Cycles @@ -49,7 +47,7 @@ export const createWorkSpaceIndexes = async () => { // Estimate Points @todo promises.push(persistence.db.exec({ sql: `CREATE INDEX estimate_points_name_idx ON estimate_points (id,value)` })); // Options - promises.push(persistence.db.exec({ sql: `CREATE INDEX options_name_idx ON options (name)` })); + promises.push(persistence.db.exec({ sql: `CREATE INDEX options_key_idx ON options (key)` })); await Promise.all(promises); }; diff --git a/web/core/local-db/utils/load-issues.ts b/web/core/local-db/utils/load-issues.ts index 1524edea7d3..4a1986f1e19 100644 --- a/web/core/local-db/utils/load-issues.ts +++ b/web/core/local-db/utils/load-issues.ts @@ -4,47 +4,53 @@ import { IssueService } from "@/services/issue"; import { persistence } from "../storage.sqlite"; import { ARRAY_FIELDS, PRIORITY_MAP } from "./constants"; import { issueSchema } from "./schemas"; +import { log } from "./utils"; export const PROJECT_OFFLINE_STATUS: Record = {}; export const addIssue = async (issue: any) => { - if (document.hidden || !rootStore.user.localDBEnabled) return; - - persistence.db.exec("BEGIN TRANSACTION;"); - stageIssueInserts(issue); - persistence.db.exec("COMMIT;"); + if (document.hidden || !rootStore.user.localDBEnabled || !persistence.db) return; + await persistence.db.exec("BEGIN;"); + await stageIssueInserts(issue); + await persistence.db.exec("COMMIT;"); }; export const addIssuesBulk = async (issues: any, batchSize = 100) => { - if (!rootStore.user.localDBEnabled) return; + if (!rootStore.user.localDBEnabled || !persistence.db) return; + + const insertStart = performance.now(); + await persistence.db.exec("BEGIN;"); for (let i = 0; i < issues.length; i += batchSize) { const batch = issues.slice(i, i + batchSize); - persistence.db.exec("BEGIN TRANSACTION;"); - batch.forEach((issue: any) => { + for (let j = 0; j < batch.length; j++) { + const issue = batch[j]; if (!issue.type_id) { issue.type_id = ""; } - stageIssueInserts(issue); - }); - await persistence.db.exec("COMMIT;"); + await stageIssueInserts(issue); + } } + await persistence.db.exec("COMMIT;"); + + const insertEnd = performance.now(); + log("Inserted issues in ", `${insertEnd - insertStart}ms`); }; export const deleteIssueFromLocal = async (issue_id: any) => { - if (!rootStore.user.localDBEnabled) return; + if (!rootStore.user.localDBEnabled || !persistence.db) return; const deleteQuery = `delete from issues where id='${issue_id}'`; const deleteMetaQuery = `delete from issue_meta where issue_id='${issue_id}'`; - persistence.db.exec("BEGIN TRANSACTION;"); + persistence.db.exec("BEGIN;"); persistence.db.exec(deleteQuery); persistence.db.exec(deleteMetaQuery); persistence.db.exec("COMMIT;"); }; // @todo: Update deletes the issue description from local. Implement a separate update. export const updateIssue = async (issue: TIssue & { is_local_update: number }) => { - if (document.hidden || !rootStore.user.localDBEnabled) return; + if (document.hidden || !rootStore.user.localDBEnabled || !persistence.db) return; const issue_id = issue.id; // delete the issue and its meta data @@ -53,7 +59,7 @@ export const updateIssue = async (issue: TIssue & { is_local_update: number }) = }; export const syncDeletesToLocal = async (workspaceId: string, projectId: string, queries: any) => { - if (!rootStore.user.localDBEnabled) return; + if (!rootStore.user.localDBEnabled || !persistence.db) return; const issueService = new IssueService(); const response = await issueService.getDeletedIssues(workspaceId, projectId, queries); @@ -62,7 +68,7 @@ export const syncDeletesToLocal = async (workspaceId: string, projectId: string, } }; -const stageIssueInserts = (issue: any) => { +const stageIssueInserts = async (issue: any) => { const issue_id = issue.id; issue.priority_proxy = PRIORITY_MAP[issue.priority as keyof typeof PRIORITY_MAP]; @@ -94,7 +100,7 @@ const stageIssueInserts = (issue: any) => { const query = `INSERT OR REPLACE INTO issues (${columns}) VALUES (${values});`; persistence.db.exec(query); - persistence.db.exec({ + await persistence.db.exec({ sql: `DELETE from issue_meta where issue_id='${issue_id}'`, }); diff --git a/web/core/local-db/utils/load-workspace.ts b/web/core/local-db/utils/load-workspace.ts index 87231161c6f..bb8ee1454da 100644 --- a/web/core/local-db/utils/load-workspace.ts +++ b/web/core/local-db/utils/load-workspace.ts @@ -7,9 +7,18 @@ import { ModuleService } from "@/services/module.service"; import { ProjectStateService } from "@/services/project"; import { WorkspaceService } from "@/services/workspace.service"; import { persistence } from "../storage.sqlite"; -import { cycleSchema, estimatePointSchema, labelSchema, memberSchema, Schema, stateSchema } from "./schemas"; +import { + cycleSchema, + estimatePointSchema, + labelSchema, + memberSchema, + moduleSchema, + Schema, + stateSchema, +} from "./schemas"; +import { log } from "./utils"; -const stageInserts = (table: string, schema: Schema, data: any) => { +const stageInserts = async (table: string, schema: Schema, data: any) => { const keys = Object.keys(schema); // Pick only the keys that are in the schema const filteredData = keys.reduce((acc: any, key) => { @@ -36,67 +45,46 @@ const stageInserts = (table: string, schema: Schema, data: any) => { }) .join(", "); const query = `INSERT OR REPLACE INTO ${table} (${columns}) VALUES (${values});`; - persistence.db.exec(query); + await persistence.db.exec(query); }; -export const loadLabels = async (workspaceSlug: string, batchSize = 500) => { +const batchInserts = async (data: any[], table: string, schema: Schema, batchSize = 500) => { + for (let i = 0; i < data.length; i += batchSize) { + const batch = data.slice(i, i + batchSize); + for (let j = 0; j < batch.length; j++) { + const item = batch[j]; + await stageInserts(table, schema, item); + } + } +}; + +export const getLabels = async (workspaceSlug: string) => { const issueLabelService = new IssueLabelService(); const objects = await issueLabelService.getWorkspaceIssueLabels(workspaceSlug); - for (let i = 0; i < objects.length; i += batchSize) { - const batch = objects.slice(i, i + batchSize); - persistence.db.exec("BEGIN TRANSACTION;"); - batch.forEach((label: any) => { - stageInserts("labels", labelSchema, label); - }); - await persistence.db.exec("COMMIT;"); - } + return objects; }; -export const loadModules = async (workspaceSlug: string, batchSize = 500) => { +export const getModules = async (workspaceSlug: string) => { const moduleService = new ModuleService(); const objects = await moduleService.getWorkspaceModules(workspaceSlug); - for (let i = 0; i < objects.length; i += batchSize) { - const batch = objects.slice(i, i + batchSize); - - persistence.db.exec("BEGIN TRANSACTION;"); - batch.forEach((label: any) => { - stageInserts("modules", labelSchema, label); - }); - await persistence.db.exec("COMMIT;"); - } + return objects; }; -export const loadCycles = async (workspaceSlug: string, batchSize = 500) => { +export const getCycles = async (workspaceSlug: string) => { const cycleService = new CycleService(); const objects = await cycleService.getWorkspaceCycles(workspaceSlug); - for (let i = 0; i < objects.length; i += batchSize) { - const batch = objects.slice(i, i + batchSize); - - persistence.db.exec("BEGIN TRANSACTION;"); - batch.forEach((cycle: any) => { - stageInserts("cycles", cycleSchema, cycle); - }); - await persistence.db.exec("COMMIT;"); - } + return objects; }; -export const loadStates = async (workspaceSlug: string, batchSize = 500) => { +export const getStates = async (workspaceSlug: string) => { const stateService = new ProjectStateService(); const objects = await stateService.getWorkspaceStates(workspaceSlug); - for (let i = 0; i < objects.length; i += batchSize) { - const batch = objects.slice(i, i + batchSize); - - persistence.db.exec("BEGIN TRANSACTION;"); - batch.forEach((state: any) => { - stageInserts("states", stateSchema, state); - }); - await persistence.db.exec("COMMIT;"); - } + return objects; }; -export const loadEstimatePoints = async (workspaceSlug: string, batchSize = 500) => { +export const getEstimatePoints = async (workspaceSlug: string) => { const estimateService = new EstimateService(); const estimates = await estimateService.fetchWorkspaceEstimates(workspaceSlug); const objects: IEstimatePoint[] = []; @@ -105,38 +93,36 @@ export const loadEstimatePoints = async (workspaceSlug: string, batchSize = 500) objects.concat(estimate.points); } }); - for (let i = 0; i < objects.length; i += batchSize) { - const batch = objects.slice(i, i + batchSize); - - persistence.db.exec("BEGIN TRANSACTION;"); - batch.forEach((point: any) => { - stageInserts("estimate_points", estimatePointSchema, point); - }); - await persistence.db.exec("COMMIT;"); - } + return objects; }; -export const loadMembers = async (workspaceSlug: string, batchSize = 500) => { +export const getMembers = async (workspaceSlug: string) => { const workspaceService = new WorkspaceService(API_BASE_URL); const members = await workspaceService.fetchWorkspaceMembers(workspaceSlug); const objects = members.map((member: IWorkspaceMember) => member.member); - for (let i = 0; i < objects.length; i += batchSize) { - const batch = objects.slice(i, i + batchSize); - persistence.db.exec("BEGIN TRANSACTION;"); - batch.forEach((member: any) => { - stageInserts("members", memberSchema, member); - }); - await persistence.db.exec("COMMIT;"); - } + return objects; }; export const loadWorkSpaceData = async (workspaceSlug: string) => { + log("Loading workspace data"); const promises = []; - promises.push(loadLabels(workspaceSlug)); - promises.push(loadModules(workspaceSlug)); - promises.push(loadCycles(workspaceSlug)); - promises.push(loadStates(workspaceSlug)); - promises.push(loadEstimatePoints(workspaceSlug)); - promises.push(loadMembers(workspaceSlug)); - await Promise.all(promises); + promises.push(getLabels(workspaceSlug)); + promises.push(getModules(workspaceSlug)); + promises.push(getCycles(workspaceSlug)); + promises.push(getStates(workspaceSlug)); + promises.push(getEstimatePoints(workspaceSlug)); + promises.push(getMembers(workspaceSlug)); + const [labels, modules, cycles, states, estimates, memebers] = await Promise.all(promises); + + const start = performance.now(); + await persistence.db.exec("BEGIN TRANSACTION;"); + await batchInserts(labels, "labels", labelSchema); + await batchInserts(modules, "modules", moduleSchema); + await batchInserts(cycles, "cycles", cycleSchema); + await batchInserts(states, "states", stateSchema); + await batchInserts(estimates, "estimate_points", estimatePointSchema); + await batchInserts(memebers, "members", memberSchema); + await persistence.db.exec("COMMIT"); + const end = performance.now(); + log("Time taken to load workspace data", end - start); }; diff --git a/web/core/local-db/utils/query-constructor.ts b/web/core/local-db/utils/query-constructor.ts index 864f160706e..6fd538f1e77 100644 --- a/web/core/local-db/utils/query-constructor.ts +++ b/web/core/local-db/utils/query-constructor.ts @@ -25,7 +25,7 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st per_page, group_by, sub_group_by, - order_by = "created_at", + order_by = "-created_at", ...otherProps } = translateQueryParams(queries); diff --git a/web/core/local-db/utils/query-executor.ts b/web/core/local-db/utils/query-executor.ts index 9002d5a7bfc..a6844dff434 100644 --- a/web/core/local-db/utils/query-executor.ts +++ b/web/core/local-db/utils/query-executor.ts @@ -1,13 +1,11 @@ -// import { SQL } from "./sqlite"; - import { persistence } from "../storage.sqlite"; export const runQuery = async (sql: string) => { - const data = await persistence.db.exec({ + const data = await persistence.db?.exec({ sql, rowMode: "object", returnValue: "resultRows", }); - return data.result.resultRows; + return data; }; diff --git a/web/core/local-db/utils/query.utils.ts b/web/core/local-db/utils/query.utils.ts index fbc42fec991..520338e6af6 100644 --- a/web/core/local-db/utils/query.utils.ts +++ b/web/core/local-db/utils/query.utils.ts @@ -4,8 +4,20 @@ import { issueSchema } from "./schemas"; import { wrapDateTime } from "./utils"; export const translateQueryParams = (queries: any) => { - const { group_by, sub_group_by, labels, assignees, state, cycle, module, priority, type, issue_type, ...otherProps } = - queries; + const { + group_by, + layout, + sub_group_by, + labels, + assignees, + state, + cycle, + module, + priority, + type, + issue_type, + ...otherProps + } = queries; const order_by = queries.order_by; if (state) otherProps.state_id = state; @@ -33,7 +45,7 @@ export const translateQueryParams = (queries: any) => { } // Fix invalid orderby when switching from spreadsheet layout - if ((group_by || sub_group_by) && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) { + if (layout === "spreadsheet" && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) { otherProps.order_by = "sort_order"; } // For each property value, replace None with empty string diff --git a/web/core/local-db/utils/tables.ts b/web/core/local-db/utils/tables.ts index 32ad5d50cf8..67ada8465e2 100644 --- a/web/core/local-db/utils/tables.ts +++ b/web/core/local-db/utils/tables.ts @@ -24,17 +24,18 @@ const createTableSQLfromSchema = (tableName: string, schema: Schema) => { }; export const createTables = async () => { - persistence.db.exec("BEGIN TRANSACTION;"); + //@todo use promise.all or send all statements in one go + await persistence.db.exec("BEGIN;"); - persistence.db.exec(createTableSQLfromSchema("issues", issueSchema)); - persistence.db.exec(createTableSQLfromSchema("issue_meta", issueMetaSchema)); - persistence.db.exec(createTableSQLfromSchema("modules", moduleSchema)); - persistence.db.exec(createTableSQLfromSchema("labels", labelSchema)); - persistence.db.exec(createTableSQLfromSchema("states", stateSchema)); - persistence.db.exec(createTableSQLfromSchema("cycles", cycleSchema)); - persistence.db.exec(createTableSQLfromSchema("estimate_points", estimatePointSchema)); - persistence.db.exec(createTableSQLfromSchema("members", memberSchema)); - persistence.db.exec(createTableSQLfromSchema("options", optionsSchema)); + await persistence.db.exec(createTableSQLfromSchema("issues", issueSchema)); + await persistence.db.exec(createTableSQLfromSchema("issue_meta", issueMetaSchema)); + await persistence.db.exec(createTableSQLfromSchema("modules", moduleSchema)); + await persistence.db.exec(createTableSQLfromSchema("labels", labelSchema)); + await persistence.db.exec(createTableSQLfromSchema("states", stateSchema)); + await persistence.db.exec(createTableSQLfromSchema("cycles", cycleSchema)); + await persistence.db.exec(createTableSQLfromSchema("estimate_points", estimatePointSchema)); + await persistence.db.exec(createTableSQLfromSchema("members", memberSchema)); + await persistence.db.exec(createTableSQLfromSchema("options", optionsSchema)); - persistence.db.exec("COMMIT;"); + await persistence.db.exec("COMMIT;"); }; diff --git a/web/core/local-db/utils/utils.ts b/web/core/local-db/utils/utils.ts index 39c49d745cc..9fb81d4c250 100644 --- a/web/core/local-db/utils/utils.ts +++ b/web/core/local-db/utils/utils.ts @@ -1,4 +1,4 @@ -import * as Sentry from "@sentry/nextjs"; +import { captureException } from "@sentry/nextjs"; import pick from "lodash/pick"; import { TIssue } from "@plane/types"; import { rootStore } from "@/lib/store-context"; @@ -14,8 +14,8 @@ export const logError = (e: any) => { if (e?.result?.errorClass === "SQLite3Error") { e = parseSQLite3Error(e); } - Sentry.captureException(e); - console.log(e); + console.error(e); + captureException(e); }; export const logInfo = console.info; @@ -152,3 +152,25 @@ const parseSQLite3Error = (error: any) => { error.result = JSON.stringify(error.result); return error; }; + +export const clearOPFS = async () => { + const storageManager = window.navigator.storage; + const fileSystemDirectoryHandle = await storageManager.getDirectory(); + const userAgent = navigator.userAgent; + const isChrome = userAgent.includes("Chrome") && !userAgent.includes("Edg") && !userAgent.includes("OPR"); + + if (isChrome) { + await (fileSystemDirectoryHandle as any).remove({ recursive: true }); + return; + } + + const entries = await (fileSystemDirectoryHandle as any).entries(); + for await (const entry of entries) { + const [name] = entry; + try { + await fileSystemDirectoryHandle.removeEntry(name); + } catch (e) { + console.log("Error", e); + } + } +}; diff --git a/web/core/local-db/worker/db.ts b/web/core/local-db/worker/db.ts new file mode 100644 index 00000000000..17e326b7877 --- /dev/null +++ b/web/core/local-db/worker/db.ts @@ -0,0 +1,114 @@ +import * as Comlink from "comlink"; +import { OPFSCoopSyncVFS as MyVFS } from "./wa-sqlite/src/OPFSCoopSyncVFS"; +import * as SQLite from "./wa-sqlite/src/sqlite-api"; +import SQLiteESMFactory from "./wa-sqlite/src/wa-sqlite.mjs"; + +type TQueryProps = { + sql: string; + rowMode: string; + returnValue: string; + bind: any[]; +}; +const mergeToObject = (columns: string[], row: any[]) => { + const obj: any = {}; + columns.forEach((column, index) => { + obj[column] = row[index]; + }); + return obj; +}; +interface SQLiteInstance { + db: unknown; + exec: (sql: string) => Promise; +} + +export class DBClass { + private instance: SQLiteInstance = {} as SQLiteInstance; + private sqlite3: any; + private tp: Promise | null = null; + private tpResolver: any; + async init(dbName: string) { + if (!dbName || typeof dbName !== "string") { + throw new Error("Invalid database name"); + } + + try { + const m = await SQLiteESMFactory(); + this.sqlite3 = SQLite.Factory(m); + const vfs = await MyVFS.create("plane", m); + this.sqlite3.vfs_register(vfs, true); + const db = await this.sqlite3.open_v2(`${dbName}.sqlite3`); + this.instance.db = db; + this.instance.exec = async (sql: string) => { + const rows: any[] = []; + await this.sqlite3.exec(db, sql, (row: any[], columns: string[]) => { + rows.push(mergeToObject(columns, row)); + }); + + return rows; + }; + return true; + } catch (error) { + throw new Error(`Failed to initialize database: ${(error as any)?.message}`); + } + } + + runQuery(sql: string) { + return this.instance.exec(sql); + } + + async exec(props: string | TQueryProps) { + if (this.tp && props === "BEGIN;") { + await this.tp; + } + let sql: string, bind: any[]; + if (typeof props === "string") { + sql = props; + } else { + ({ sql, bind } = props); + if (bind) { + for await (const stmt of this.sqlite3.statements(this.instance.db, sql)) { + bind.forEach((b, i) => { + this.sqlite3.bind(stmt, i + 1, b); + }); + + const rows = []; + + do { + const columns = await this.sqlite3.column_names(stmt); + const row = await this.sqlite3.row(stmt); + rows.push(mergeToObject(columns, row)); + } while ((await this.sqlite3.step(stmt)) === SQLite.SQLITE_ROW); + + return rows; + } + } + } + + if (sql === "BEGIN;") { + this.tp = new Promise((resolve, reject) => { + this.tpResolver = { resolve, reject }; + }); + } + + if (sql === "COMMIT;" && this.tp) { + await this.instance.exec(sql); + this.tpResolver.resolve(); + this.tp = null; + return; + } + return await this.instance.exec(sql); + } + async close() { + try { + if (!this.instance.db) { + return; + } + await this.sqlite3.close(this.instance.db); + // Clear instance to prevent usage after closing + this.instance = {} as SQLiteInstance; + } catch (error) { + throw new Error(`Failed to close database: ${(error as any)?.message}`); + } + } +} +Comlink.expose(DBClass); diff --git a/web/core/local-db/worker/wa-sqlite/src/FacadeVFS.js b/web/core/local-db/worker/wa-sqlite/src/FacadeVFS.js new file mode 100644 index 00000000000..c975fdc913c --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/FacadeVFS.js @@ -0,0 +1,508 @@ +// Copyright 2024 Roy T. Hashimoto. All Rights Reserved. +import * as VFS from './VFS.js'; + +const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; + +// Convenience base class for a JavaScript VFS. +// The raw xOpen, xRead, etc. function signatures receive only C primitives +// which aren't easy to work with. This class provides corresponding calls +// like jOpen, jRead, etc., which receive JavaScript-friendlier arguments +// such as string, Uint8Array, and DataView. +export class FacadeVFS extends VFS.Base { + /** + * @param {string} name + * @param {object} module + */ + constructor(name, module) { + super(name, module); + } + + /** + * Override to indicate which methods are asynchronous. + * @param {string} methodName + * @returns {boolean} + */ + hasAsyncMethod(methodName) { + // The input argument is a string like "xOpen", so convert to "jOpen". + // Then check if the method exists and is async. + const jMethodName = `j${methodName.slice(1)}`; + return this[jMethodName] instanceof AsyncFunction; + } + + /** + * Return the filename for a file id for use by mixins. + * @param {number} pFile + * @returns {string} + */ + getFilename(pFile) { + throw new Error('unimplemented'); + } + + /** + * @param {string?} filename + * @param {number} pFile + * @param {number} flags + * @param {DataView} pOutFlags + * @returns {number|Promise} + */ + jOpen(filename, pFile, flags, pOutFlags) { + return VFS.SQLITE_CANTOPEN; + } + + /** + * @param {string} filename + * @param {number} syncDir + * @returns {number|Promise} + */ + jDelete(filename, syncDir) { + return VFS.SQLITE_OK; + } + + /** + * @param {string} filename + * @param {number} flags + * @param {DataView} pResOut + * @returns {number|Promise} + */ + jAccess(filename, flags, pResOut) { + return VFS.SQLITE_OK; + } + + /** + * @param {string} filename + * @param {Uint8Array} zOut + * @returns {number|Promise} + */ + jFullPathname(filename, zOut) { + // Copy the filename to the output buffer. + const { read, written } = new TextEncoder().encodeInto(filename, zOut); + if (read < filename.length) return VFS.SQLITE_IOERR; + if (written >= zOut.length) return VFS.SQLITE_IOERR; + zOut[written] = 0; + return VFS.SQLITE_OK; + } + + /** + * @param {Uint8Array} zBuf + * @returns {number|Promise} + */ + jGetLastError(zBuf) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + jClose(pFile) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number|Promise} + */ + jRead(pFile, pData, iOffset) { + pData.fill(0); + return VFS.SQLITE_IOERR_SHORT_READ; + } + + /** + * @param {number} pFile + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number|Promise} + */ + jWrite(pFile, pData, iOffset) { + return VFS.SQLITE_IOERR_WRITE; + } + + /** + * @param {number} pFile + * @param {number} size + * @returns {number|Promise} + */ + jTruncate(pFile, size) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} flags + * @returns {number|Promise} + */ + jSync(pFile, flags) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {DataView} pSize + * @returns {number|Promise} + */ + jFileSize(pFile, pSize) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + jLock(pFile, lockType) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + jUnlock(pFile, lockType) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {DataView} pResOut + * @returns {number|Promise} + */ + jCheckReservedLock(pFile, pResOut) { + pResOut.setInt32(0, 0, true); + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} op + * @param {DataView} pArg + * @returns {number|Promise} + */ + jFileControl(pFile, op, pArg) { + return VFS.SQLITE_NOTFOUND; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + jSectorSize(pFile) { + return super.xSectorSize(pFile); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + jDeviceCharacteristics(pFile) { + return 0; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} pFile + * @param {number} flags + * @param {number} pOutFlags + * @returns {number|Promise} + */ + xOpen(pVfs, zName, pFile, flags, pOutFlags) { + const filename = this.#decodeFilename(zName, flags); + const pOutFlagsView = this.#makeTypedDataView('Int32', pOutFlags); + this['log']?.('jOpen', filename, pFile, '0x' + flags.toString(16)); + return this.jOpen(filename, pFile, flags, pOutFlagsView); + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} syncDir + * @returns {number|Promise} + */ + xDelete(pVfs, zName, syncDir) { + const filename = this._module.UTF8ToString(zName); + this['log']?.('jDelete', filename, syncDir); + return this.jDelete(filename, syncDir); + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} flags + * @param {number} pResOut + * @returns {number|Promise} + */ + xAccess(pVfs, zName, flags, pResOut) { + const filename = this._module.UTF8ToString(zName); + const pResOutView = this.#makeTypedDataView('Int32', pResOut); + this['log']?.('jAccess', filename, flags); + return this.jAccess(filename, flags, pResOutView); + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} nOut + * @param {number} zOut + * @returns {number|Promise} + */ + xFullPathname(pVfs, zName, nOut, zOut) { + const filename = this._module.UTF8ToString(zName); + const zOutArray = this._module.HEAPU8.subarray(zOut, zOut + nOut); + this['log']?.('jFullPathname', filename, nOut); + return this.jFullPathname(filename, zOutArray); + } + + /** + * @param {number} pVfs + * @param {number} nBuf + * @param {number} zBuf + * @returns {number|Promise} + */ + xGetLastError(pVfs, nBuf, zBuf) { + const zBufArray = this._module.HEAPU8.subarray(zBuf, zBuf + nBuf); + this['log']?.('jGetLastError', nBuf); + return this.jGetLastError(zBufArray); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xClose(pFile) { + this['log']?.('jClose', pFile); + return this.jClose(pFile); + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + const pDataArray = this.#makeDataArray(pData, iAmt); + const iOffset = delegalize(iOffsetLo, iOffsetHi); + this['log']?.('jRead', pFile, iAmt, iOffset); + return this.jRead(pFile, pDataArray, iOffset); + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + const pDataArray = this.#makeDataArray(pData, iAmt); + const iOffset = delegalize(iOffsetLo, iOffsetHi); + this['log']?.('jWrite', pFile, pDataArray, iOffset); + return this.jWrite(pFile, pDataArray, iOffset); + } + + /** + * @param {number} pFile + * @param {number} sizeLo + * @param {number} sizeHi + * @returns {number|Promise} + */ + xTruncate(pFile, sizeLo, sizeHi) { + const size = delegalize(sizeLo, sizeHi); + this['log']?.('jTruncate', pFile, size); + return this.jTruncate(pFile, size); + } + + /** + * @param {number} pFile + * @param {number} flags + * @returns {number|Promise} + */ + xSync(pFile, flags) { + this['log']?.('jSync', pFile, flags); + return this.jSync(pFile, flags); + } + + /** + * + * @param {number} pFile + * @param {number} pSize + * @returns {number|Promise} + */ + xFileSize(pFile, pSize) { + const pSizeView = this.#makeTypedDataView('BigInt64', pSize); + this['log']?.('jFileSize', pFile); + return this.jFileSize(pFile, pSizeView); + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xLock(pFile, lockType) { + this['log']?.('jLock', pFile, lockType); + return this.jLock(pFile, lockType); + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xUnlock(pFile, lockType) { + this['log']?.('jUnlock', pFile, lockType); + return this.jUnlock(pFile, lockType); + } + + /** + * @param {number} pFile + * @param {number} pResOut + * @returns {number|Promise} + */ + xCheckReservedLock(pFile, pResOut) { + const pResOutView = this.#makeTypedDataView('Int32', pResOut); + this['log']?.('jCheckReservedLock', pFile); + return this.jCheckReservedLock(pFile, pResOutView); + } + + /** + * @param {number} pFile + * @param {number} op + * @param {number} pArg + * @returns {number|Promise} + */ + xFileControl(pFile, op, pArg) { + const pArgView = new DataView( + this._module.HEAPU8.buffer, + this._module.HEAPU8.byteOffset + pArg); + this['log']?.('jFileControl', pFile, op, pArgView); + return this.jFileControl(pFile, op, pArgView); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xSectorSize(pFile) { + this['log']?.('jSectorSize', pFile); + return this.jSectorSize(pFile); + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xDeviceCharacteristics(pFile) { + this['log']?.('jDeviceCharacteristics', pFile); + return this.jDeviceCharacteristics(pFile); + } + + /** + * Wrapped DataView for pointer arguments. + * Pointers to a single value are passed using DataView. A Proxy + * wrapper prevents use of incorrect type or endianness. + * @param {'Int32'|'BigInt64'} type + * @param {number} byteOffset + * @returns {DataView} + */ + #makeTypedDataView(type, byteOffset) { + const byteLength = type === 'Int32' ? 4 : 8; + const getter = `get${type}`; + const setter = `set${type}`; + const makeDataView = () => new DataView( + this._module.HEAPU8.buffer, + this._module.HEAPU8.byteOffset + byteOffset, + byteLength); + let dataView = makeDataView(); + return new Proxy(dataView, { + get(_, prop) { + if (dataView.buffer.byteLength === 0) { + // WebAssembly memory resize detached the buffer. + dataView = makeDataView(); + } + if (prop === getter) { + return function(byteOffset, littleEndian) { + if (!littleEndian) throw new Error('must be little endian'); + return dataView[prop](byteOffset, littleEndian); + } + } + if (prop === setter) { + return function(byteOffset, value, littleEndian) { + if (!littleEndian) throw new Error('must be little endian'); + return dataView[prop](byteOffset, value, littleEndian); + } + } + if (typeof prop === 'string' && (prop.match(/^(get)|(set)/))) { + throw new Error('invalid type'); + } + const result = dataView[prop]; + return typeof result === 'function' ? result.bind(dataView) : result; + } + }); + } + + /** + * @param {number} byteOffset + * @param {number} byteLength + */ + #makeDataArray(byteOffset, byteLength) { + let target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength); + return new Proxy(target, { + get: (_, prop, receiver) => { + if (target.buffer.byteLength === 0) { + // WebAssembly memory resize detached the buffer. + target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength); + } + const result = target[prop]; + return typeof result === 'function' ? result.bind(target) : result; + } + }); + } + + #decodeFilename(zName, flags) { + if (flags & VFS.SQLITE_OPEN_URI) { + // The first null-terminated string is the URI path. Subsequent + // strings are query parameter keys and values. + // https://www.sqlite.org/c3ref/open.html#urifilenamesinsqlite3open + let pName = zName; + let state = 1; + const charCodes = []; + while (state) { + const charCode = this._module.HEAPU8[pName++]; + if (charCode) { + charCodes.push(charCode); + } else { + if (!this._module.HEAPU8[pName]) state = null; + switch (state) { + case 1: // path + charCodes.push('?'.charCodeAt(0)); + state = 2; + break; + case 2: // key + charCodes.push('='.charCodeAt(0)); + state = 3; + break; + case 3: // value + charCodes.push('&'.charCodeAt(0)); + state = 2; + break; + } + } + } + return new TextDecoder().decode(new Uint8Array(charCodes)); + } + return zName ? this._module.UTF8ToString(zName) : null; + } +} + +// Emscripten "legalizes" 64-bit integer arguments by passing them as +// two 32-bit signed integers. +function delegalize(lo32, hi32) { + return (hi32 * 0x100000000) + lo32 + (lo32 < 0 ? 2**32 : 0); +} diff --git a/web/core/local-db/worker/wa-sqlite/src/OPFSCoopSyncVFS.js b/web/core/local-db/worker/wa-sqlite/src/OPFSCoopSyncVFS.js new file mode 100644 index 00000000000..2757ca9db9d --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/OPFSCoopSyncVFS.js @@ -0,0 +1,592 @@ +// Copyright 2024 Roy T. Hashimoto. All Rights Reserved. +import { FacadeVFS } from "./FacadeVFS.js"; +import * as VFS from "./VFS.js"; + +const DEFAULT_TEMPORARY_FILES = 10; +const LOCK_NOTIFY_INTERVAL = 1000; + +const DB_RELATED_FILE_SUFFIXES = ["", "-journal", "-wal"]; + +const finalizationRegistry = new FinalizationRegistry((releaser) => releaser()); + +class File { + /** @type {string} */ path; + /** @type {number} */ flags; + /** @type {FileSystemSyncAccessHandle} */ accessHandle; + + /** @type {PersistentFile?} */ persistentFile; + + constructor(path, flags) { + this.path = path; + this.flags = flags; + } +} + +class PersistentFile { + /** @type {FileSystemFileHandle} */ fileHandle; + /** @type {FileSystemSyncAccessHandle} */ accessHandle = null; + + // The following properties are for main database files. + + /** @type {boolean} */ isLockBusy = false; + /** @type {boolean} */ isFileLocked = false; + /** @type {boolean} */ isRequestInProgress = false; + /** @type {function} */ handleLockReleaser = null; + + /** @type {BroadcastChannel} */ handleRequestChannel; + /** @type {boolean} */ isHandleRequested = false; + + constructor(fileHandle) { + this.fileHandle = fileHandle; + } +} + +export class OPFSCoopSyncVFS extends FacadeVFS { + /** @type {Map} */ mapIdToFile = new Map(); + + lastError = null; + log = null; //function(...args) { console.log(`[${contextName}]`, ...args) }; + + /** @type {Map} */ persistentFiles = new Map(); + /** @type {Map} */ boundAccessHandles = new Map(); + /** @type {Set} */ unboundAccessHandles = new Set(); + /** @type {Set} */ accessiblePaths = new Set(); + releaser = null; + + static async create(name, module) { + const vfs = new OPFSCoopSyncVFS(name, module); + await Promise.all([vfs.isReady(), vfs.#initialize(DEFAULT_TEMPORARY_FILES)]); + return vfs; + } + + constructor(name, module) { + super(name, module); + } + + async #initialize(nTemporaryFiles) { + // Delete temporary directories no longer in use. + const root = await navigator.storage.getDirectory(); + // @ts-ignore + for await (const entry of root.values()) { + if (entry.kind === "directory" && entry.name.startsWith(".ahp-")) { + // A lock with the same name as the directory protects it from + // being deleted. + await navigator.locks.request(entry.name, { ifAvailable: true }, async (lock) => { + if (lock) { + this.log?.(`Deleting temporary directory ${entry.name}`); + await root.removeEntry(entry.name, { recursive: true }); + } else { + this.log?.(`Temporary directory ${entry.name} is in use`); + } + }); + } + } + + // Create our temporary directory. + const tmpDirName = `.ahp-${Math.random().toString(36).slice(2)}`; + this.releaser = await new Promise((resolve) => { + navigator.locks.request(tmpDirName, () => { + return new Promise((release) => { + resolve(release); + }); + }); + }); + finalizationRegistry.register(this, this.releaser); + const tmpDir = await root.getDirectoryHandle(tmpDirName, { create: true }); + + // Populate temporary directory. + for (let i = 0; i < nTemporaryFiles; i++) { + const tmpFile = await tmpDir.getFileHandle(`${i}.tmp`, { create: true }); + const tmpAccessHandle = await tmpFile.createSyncAccessHandle(); + this.unboundAccessHandles.add(tmpAccessHandle); + } + } + + /** + * @param {string?} zName + * @param {number} fileId + * @param {number} flags + * @param {DataView} pOutFlags + * @returns {number} + */ + jOpen(zName, fileId, flags, pOutFlags) { + try { + const url = new URL(zName || Math.random().toString(36).slice(2), "file://"); + const path = url.pathname; + + if (flags & VFS.SQLITE_OPEN_MAIN_DB) { + const persistentFile = this.persistentFiles.get(path); + if (persistentFile?.isRequestInProgress) { + // Should not reach here unless SQLite itself retries an open. + // Otherwise, asynchronous operations started on a previous + // open try should have completed. + return VFS.SQLITE_BUSY; + } else if (!persistentFile) { + // This is the usual starting point for opening a database. + // Register a Promise that resolves when the database and related + // files are ready to be used. + this.log?.(`creating persistent file for ${path}`); + const create = !!(flags & VFS.SQLITE_OPEN_CREATE); + this._module.retryOps.push( + (async () => { + try { + // Get the path directory handle. + let dirHandle = await navigator.storage.getDirectory(); + const directories = path.split("/").filter((d) => d); + const filename = directories.pop(); + for (const directory of directories) { + dirHandle = await dirHandle.getDirectoryHandle(directory, { create }); + } + + // Get file handles for the database and related files, + // and create persistent file instances. + for (const suffix of DB_RELATED_FILE_SUFFIXES) { + const fileHandle = await dirHandle.getFileHandle(filename + suffix, { create }); + await this.#createPersistentFile(fileHandle); + } + + // Get access handles for the files. + const file = new File(path, flags); + file.persistentFile = this.persistentFiles.get(path); + await this.#requestAccessHandle(file); + } catch (e) { + // Use an invalid persistent file to signal this error + // for the retried open. + const persistentFile = new PersistentFile(null); + this.persistentFiles.set(path, persistentFile); + console.error(e); + } + })() + ); + return VFS.SQLITE_BUSY; + } else if (!persistentFile.fileHandle) { + // The asynchronous open operation failed. + this.persistentFiles.delete(path); + return VFS.SQLITE_CANTOPEN; + } else if (!persistentFile.accessHandle) { + // This branch is reached if the database was previously opened + // and closed. + this._module.retryOps.push( + (async () => { + const file = new File(path, flags); + file.persistentFile = this.persistentFiles.get(path); + await this.#requestAccessHandle(file); + })() + ); + return VFS.SQLITE_BUSY; + } + } + + if (!this.accessiblePaths.has(path) && !(flags & VFS.SQLITE_OPEN_CREATE)) { + throw new Error(`File ${path} not found`); + } + + const file = new File(path, flags); + this.mapIdToFile.set(fileId, file); + + if (this.persistentFiles.has(path)) { + file.persistentFile = this.persistentFiles.get(path); + } else if (this.boundAccessHandles.has(path)) { + // This temporary file was previously created and closed. Reopen + // the same access handle. + file.accessHandle = this.boundAccessHandles.get(path); + } else if (this.unboundAccessHandles.size) { + // Associate an unbound access handle to this file. + file.accessHandle = this.unboundAccessHandles.values().next().value; + file.accessHandle.truncate(0); + this.unboundAccessHandles.delete(file.accessHandle); + this.boundAccessHandles.set(path, file.accessHandle); + } + this.accessiblePaths.add(path); + + pOutFlags.setInt32(0, flags, true); + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_CANTOPEN; + } + } + + /** + * @param {string} zName + * @param {number} syncDir + * @returns {number} + */ + jDelete(zName, syncDir) { + try { + const url = new URL(zName, "file://"); + const path = url.pathname; + if (this.persistentFiles.has(path)) { + const persistentFile = this.persistentFiles.get(path); + persistentFile.accessHandle.truncate(0); + } else { + this.boundAccessHandles.get(path)?.truncate(0); + } + this.accessiblePaths.delete(path); + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_DELETE; + } + } + + /** + * @param {string} zName + * @param {number} flags + * @param {DataView} pResOut + * @returns {number} + */ + jAccess(zName, flags, pResOut) { + try { + const url = new URL(zName, "file://"); + const path = url.pathname; + pResOut.setInt32(0, this.accessiblePaths.has(path) ? 1 : 0, true); + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_ACCESS; + } + } + + /** + * @param {number} fileId + * @returns {number} + */ + jClose(fileId) { + try { + const file = this.mapIdToFile.get(fileId); + this.mapIdToFile.delete(fileId); + + if (file?.flags & VFS.SQLITE_OPEN_MAIN_DB) { + if (file.persistentFile?.handleLockReleaser) { + this.#releaseAccessHandle(file); + } + } else if (file?.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) { + file.accessHandle.truncate(0); + this.accessiblePaths.delete(file.path); + if (!this.persistentFiles.has(file.path)) { + this.boundAccessHandles.delete(file.path); + this.unboundAccessHandles.add(file.accessHandle); + } + } + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_CLOSE; + } + } + + /** + * @param {number} fileId + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number} + */ + jRead(fileId, pData, iOffset) { + try { + const file = this.mapIdToFile.get(fileId); + + // On Chrome (at least), passing pData to accessHandle.read() is + // an error because pData is a Proxy of a Uint8Array. Calling + // subarray() produces a real Uint8Array and that works. + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + const bytesRead = accessHandle.read(pData.subarray(), { at: iOffset }); + + // Opening a database file performs one read without a xLock call. + if (file.flags & VFS.SQLITE_OPEN_MAIN_DB && !file.persistentFile.isFileLocked) { + this.#releaseAccessHandle(file); + } + + if (bytesRead < pData.byteLength) { + pData.fill(0, bytesRead); + return VFS.SQLITE_IOERR_SHORT_READ; + } + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_READ; + } + } + + /** + * @param {number} fileId + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number} + */ + jWrite(fileId, pData, iOffset) { + try { + const file = this.mapIdToFile.get(fileId); + + // On Chrome (at least), passing pData to accessHandle.write() is + // an error because pData is a Proxy of a Uint8Array. Calling + // subarray() produces a real Uint8Array and that works. + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + const nBytes = accessHandle.write(pData.subarray(), { at: iOffset }); + if (nBytes !== pData.byteLength) throw new Error("short write"); + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_WRITE; + } + } + + /** + * @param {number} fileId + * @param {number} iSize + * @returns {number} + */ + jTruncate(fileId, iSize) { + try { + const file = this.mapIdToFile.get(fileId); + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + accessHandle.truncate(iSize); + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_TRUNCATE; + } + } + + /** + * @param {number} fileId + * @param {number} flags + * @returns {number} + */ + jSync(fileId, flags) { + try { + const file = this.mapIdToFile.get(fileId); + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + accessHandle.flush(); + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_FSYNC; + } + } + + /** + * @param {number} fileId + * @param {DataView} pSize64 + * @returns {number} + */ + jFileSize(fileId, pSize64) { + try { + const file = this.mapIdToFile.get(fileId); + const accessHandle = file.accessHandle || file.persistentFile.accessHandle; + const size = accessHandle.getSize(); + pSize64.setBigInt64(0, BigInt(size), true); + return VFS.SQLITE_OK; + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR_FSTAT; + } + } + + /** + * @param {number} fileId + * @param {number} lockType + * @returns {number} + */ + jLock(fileId, lockType) { + const file = this.mapIdToFile.get(fileId); + if (file.persistentFile.isRequestInProgress) { + file.persistentFile.isLockBusy = true; + return VFS.SQLITE_BUSY; + } + + file.persistentFile.isFileLocked = true; + if (!file.persistentFile.handleLockReleaser) { + // Start listening for notifications from other connections. + // This is before we actually get access handles, but waiting to + // listen until then allows a race condition where notifications + // are missed. + file.persistentFile.handleRequestChannel.onmessage = () => { + this.log?.(`received notification for ${file.path}`); + if (file.persistentFile.isFileLocked) { + // We're still using the access handle, so mark it to be + // released when we're done. + file.persistentFile.isHandleRequested = true; + } else { + // Release the access handles immediately. + this.#releaseAccessHandle(file); + } + file.persistentFile.handleRequestChannel.onmessage = null; + }; + + this.#requestAccessHandle(file); + this.log?.("returning SQLITE_BUSY"); + file.persistentFile.isLockBusy = true; + return VFS.SQLITE_BUSY; + } + file.persistentFile.isLockBusy = false; + return VFS.SQLITE_OK; + } + + /** + * @param {number} fileId + * @param {number} lockType + * @returns {number} + */ + jUnlock(fileId, lockType) { + const file = this.mapIdToFile.get(fileId); + if (lockType === VFS.SQLITE_LOCK_NONE) { + // Don't change any state if this unlock is because xLock returned + // SQLITE_BUSY. + if (!file.persistentFile.isLockBusy) { + if (file.persistentFile.isHandleRequested) { + // Another connection wants the access handle. + this.#releaseAccessHandle(file); + this.isHandleRequested = false; + } + file.persistentFile.isFileLocked = false; + } + } + return VFS.SQLITE_OK; + } + + /** + * @param {number} fileId + * @param {number} op + * @param {DataView} pArg + * @returns {number|Promise} + */ + jFileControl(fileId, op, pArg) { + try { + const file = this.mapIdToFile.get(fileId); + switch (op) { + case VFS.SQLITE_FCNTL_PRAGMA: + const key = extractString(pArg, 4); + const value = extractString(pArg, 8); + this.log?.("xFileControl", file.path, "PRAGMA", key, value); + switch (key.toLowerCase()) { + case "journal_mode": + if (value && !["off", "memory", "delete", "wal"].includes(value.toLowerCase())) { + throw new Error('journal_mode must be "off", "memory", "delete", or "wal"'); + } + break; + } + break; + } + } catch (e) { + this.lastError = e; + return VFS.SQLITE_IOERR; + } + return VFS.SQLITE_NOTFOUND; + } + + /** + * @param {Uint8Array} zBuf + * @returns + */ + jGetLastError(zBuf) { + if (this.lastError) { + console.error(this.lastError); + const outputArray = zBuf.subarray(0, zBuf.byteLength - 1); + const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray); + zBuf[written] = 0; + } + return VFS.SQLITE_OK; + } + + /** + * @param {FileSystemFileHandle} fileHandle + * @returns {Promise} + */ + async #createPersistentFile(fileHandle) { + const persistentFile = new PersistentFile(fileHandle); + const root = await navigator.storage.getDirectory(); + const relativePath = await root.resolve(fileHandle); + const path = `/${relativePath.join("/")}`; + persistentFile.handleRequestChannel = new BroadcastChannel(`ahp:${path}`); + this.persistentFiles.set(path, persistentFile); + + const f = await fileHandle.getFile(); + if (f.size) { + this.accessiblePaths.add(path); + } + return persistentFile; + } + + /** + * @param {File} file + */ + #requestAccessHandle(file) { + console.assert(!file.persistentFile.handleLockReleaser); + if (!file.persistentFile.isRequestInProgress) { + file.persistentFile.isRequestInProgress = true; + this._module.retryOps.push( + (async () => { + // Acquire the Web Lock. + file.persistentFile.handleLockReleaser = await this.#acquireLock(file.persistentFile); + + // Get access handles for the database and releated files in parallel. + this.log?.(`creating access handles for ${file.path}`); + await Promise.all( + DB_RELATED_FILE_SUFFIXES.map(async (suffix) => { + const persistentFile = this.persistentFiles.get(file.path + suffix); + if (persistentFile) { + persistentFile.accessHandle = await persistentFile.fileHandle.createSyncAccessHandle(); + } + }) + ); + file.persistentFile.isRequestInProgress = false; + })() + ); + return this._module.retryOps.at(-1); + } + return Promise.resolve(); + } + + /** + * @param {File} file + */ + async #releaseAccessHandle(file) { + DB_RELATED_FILE_SUFFIXES.forEach(async (suffix) => { + const persistentFile = this.persistentFiles.get(file.path + suffix); + if (persistentFile) { + persistentFile.accessHandle?.close(); + persistentFile.accessHandle = null; + } + }); + this.log?.(`access handles closed for ${file.path}`); + + file.persistentFile.handleLockReleaser?.(); + file.persistentFile.handleLockReleaser = null; + this.log?.(`lock released for ${file.path}`); + } + + /** + * @param {PersistentFile} persistentFile + * @returns {Promise} lock releaser + */ + #acquireLock(persistentFile) { + return new Promise((resolve) => { + // Tell other connections we want the access handle. + const lockName = persistentFile.handleRequestChannel.name; + const notify = () => { + this.log?.(`notifying for ${lockName}`); + persistentFile.handleRequestChannel.postMessage(null); + }; + const notifyId = setInterval(notify, LOCK_NOTIFY_INTERVAL); + setTimeout(notify); + + this.log?.(`lock requested: ${lockName}`); + navigator.locks.request(lockName, (lock) => { + // We have the lock. Stop asking other connections for it. + this.log?.(`lock acquired: ${lockName}`, lock); + clearInterval(notifyId); + return new Promise(resolve); + }); + }); + } +} + +function extractString(dataView, offset) { + const p = dataView.getUint32(offset, true); + if (p) { + const chars = new Uint8Array(dataView.buffer, p); + return new TextDecoder().decode(chars.subarray(0, chars.indexOf(0))); + } + return null; +} diff --git a/web/core/local-db/worker/wa-sqlite/src/VFS.js b/web/core/local-db/worker/wa-sqlite/src/VFS.js new file mode 100644 index 00000000000..12966b9cfbe --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/VFS.js @@ -0,0 +1,222 @@ +// Copyright 2024 Roy T. Hashimoto. All Rights Reserved. +import * as VFS from './sqlite-constants.js'; +export * from './sqlite-constants.js'; + +const DEFAULT_SECTOR_SIZE = 512; + +// Base class for a VFS. +export class Base { + name; + mxPathname = 64; + _module; + + /** + * @param {string} name + * @param {object} module + */ + constructor(name, module) { + this.name = name; + this._module = module; + } + + /** + * @returns {void|Promise} + */ + close() { + } + + /** + * @returns {boolean|Promise} + */ + isReady() { + return true; + } + + /** + * Overload in subclasses to indicate which methods are asynchronous. + * @param {string} methodName + * @returns {boolean} + */ + hasAsyncMethod(methodName) { + return false; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} pFile + * @param {number} flags + * @param {number} pOutFlags + * @returns {number|Promise} + */ + xOpen(pVfs, zName, pFile, flags, pOutFlags) { + return VFS.SQLITE_CANTOPEN; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} syncDir + * @returns {number|Promise} + */ + xDelete(pVfs, zName, syncDir) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} flags + * @param {number} pResOut + * @returns {number|Promise} + */ + xAccess(pVfs, zName, flags, pResOut) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pVfs + * @param {number} zName + * @param {number} nOut + * @param {number} zOut + * @returns {number|Promise} + */ + xFullPathname(pVfs, zName, nOut, zOut) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pVfs + * @param {number} nBuf + * @param {number} zBuf + * @returns {number|Promise} + */ + xGetLastError(pVfs, nBuf, zBuf) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xClose(pFile) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} pData + * @param {number} iAmt + * @param {number} iOffsetLo + * @param {number} iOffsetHi + * @returns {number|Promise} + */ + xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} sizeLo + * @param {number} sizeHi + * @returns {number|Promise} + */ + xTruncate(pFile, sizeLo, sizeHi) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} flags + * @returns {number|Promise} + */ + xSync(pFile, flags) { + return VFS.SQLITE_OK; + } + + /** + * + * @param {number} pFile + * @param {number} pSize + * @returns {number|Promise} + */ + xFileSize(pFile, pSize) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xLock(pFile, lockType) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} lockType + * @returns {number|Promise} + */ + xUnlock(pFile, lockType) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} pResOut + * @returns {number|Promise} + */ + xCheckReservedLock(pFile, pResOut) { + return VFS.SQLITE_OK; + } + + /** + * @param {number} pFile + * @param {number} op + * @param {number} pArg + * @returns {number|Promise} + */ + xFileControl(pFile, op, pArg) { + return VFS.SQLITE_NOTFOUND; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xSectorSize(pFile) { + return DEFAULT_SECTOR_SIZE; + } + + /** + * @param {number} pFile + * @returns {number|Promise} + */ + xDeviceCharacteristics(pFile) { + return 0; + } +} + +export const FILE_TYPE_MASK = [ + VFS.SQLITE_OPEN_MAIN_DB, + VFS.SQLITE_OPEN_MAIN_JOURNAL, + VFS.SQLITE_OPEN_TEMP_DB, + VFS.SQLITE_OPEN_TEMP_JOURNAL, + VFS.SQLITE_OPEN_TRANSIENT_DB, + VFS.SQLITE_OPEN_SUBJOURNAL, + VFS.SQLITE_OPEN_SUPER_JOURNAL, + VFS.SQLITE_OPEN_WAL +].reduce((mask, element) => mask | element); \ No newline at end of file diff --git a/web/core/local-db/worker/wa-sqlite/src/sqlite-api.js b/web/core/local-db/worker/wa-sqlite/src/sqlite-api.js new file mode 100644 index 00000000000..500980b4ecd --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/sqlite-api.js @@ -0,0 +1,899 @@ +// Copyright 2021 Roy T. Hashimoto. All Rights Reserved. + +import * as SQLite from "./sqlite-constants.js"; +export * from "./sqlite-constants.js"; + +const MAX_INT64 = 0x7fffffffffffffffn; +const MIN_INT64 = -0x8000000000000000n; + +const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; + +export class SQLiteError extends Error { + constructor(message, code) { + super(message); + this.code = code; + } +} + +const async = true; + +/** + * Builds a Javascript API from the Emscripten module. This API is still + * low-level and closely corresponds to the C API exported by the module, + * but differs in some specifics like throwing exceptions on errors. + * @param {*} Module SQLite Emscripten module + * @returns {SQLiteAPI} + */ +export function Factory(Module) { + /** @type {SQLiteAPI} */ const sqlite3 = {}; + + Module.retryOps = []; + const sqliteFreeAddress = Module._getSqliteFree(); + + // Allocate some space for 32-bit returned values. + const tmp = Module._malloc(8); + const tmpPtr = [tmp, tmp + 4]; + + // Convert a JS string to a C string. sqlite3_malloc is used to allocate + // memory (use sqlite3_free to deallocate). + function createUTF8(s) { + if (typeof s !== "string") return 0; + const utf8 = new TextEncoder().encode(s); + const zts = Module._sqlite3_malloc(utf8.byteLength + 1); + Module.HEAPU8.set(utf8, zts); + Module.HEAPU8[zts + utf8.byteLength] = 0; + return zts; + } + + /** + * Concatenate 32-bit numbers into a 64-bit (signed) BigInt. + * @param {number} lo32 + * @param {number} hi32 + * @returns {bigint} + */ + function cvt32x2ToBigInt(lo32, hi32) { + return (BigInt(hi32) << 32n) | (BigInt(lo32) & 0xffffffffn); + } + + /** + * Concatenate 32-bit numbers and return as number or BigInt, depending + * on the value. + * @param {number} lo32 + * @param {number} hi32 + * @returns {number|bigint} + */ + const cvt32x2AsSafe = (function () { + const hiMax = BigInt(Number.MAX_SAFE_INTEGER) >> 32n; + const hiMin = BigInt(Number.MIN_SAFE_INTEGER) >> 32n; + + return function (lo32, hi32) { + if (hi32 > hiMax || hi32 < hiMin) { + // Can't be expressed as a Number so use BigInt. + return cvt32x2ToBigInt(lo32, hi32); + } else { + // Combine the upper and lower 32-bit numbers. The complication is + // that lo32 is a signed integer which makes manipulating its bits + // a little tricky - the sign bit gets handled separately. + return hi32 * 0x100000000 + (lo32 & 0x7fffffff) - (lo32 & 0x80000000); + } + }; + })(); + + const databases = new Set(); + function verifyDatabase(db) { + if (!databases.has(db)) { + throw new SQLiteError("not a database", SQLite.SQLITE_MISUSE); + } + } + + const mapStmtToDB = new Map(); + function verifyStatement(stmt) { + if (!mapStmtToDB.has(stmt)) { + throw new SQLiteError("not a statement", SQLite.SQLITE_MISUSE); + } + } + + sqlite3.bind_collection = function (stmt, bindings) { + verifyStatement(stmt); + const isArray = Array.isArray(bindings); + const nBindings = sqlite3.bind_parameter_count(stmt); + for (let i = 1; i <= nBindings; ++i) { + const key = isArray ? i - 1 : sqlite3.bind_parameter_name(stmt, i); + const value = bindings[key]; + if (value !== undefined) { + sqlite3.bind(stmt, i, value); + } + } + return SQLite.SQLITE_OK; + }; + + sqlite3.bind = function (stmt, i, value) { + verifyStatement(stmt); + switch (typeof value) { + case "number": + if (value === (value | 0)) { + return sqlite3.bind_int(stmt, i, value); + } else { + return sqlite3.bind_double(stmt, i, value); + } + case "string": + return sqlite3.bind_text(stmt, i, value); + default: + if (value instanceof Uint8Array || Array.isArray(value)) { + return sqlite3.bind_blob(stmt, i, value); + } else if (value === null) { + return sqlite3.bind_null(stmt, i); + } else if (typeof value === "bigint") { + return sqlite3.bind_int64(stmt, i, value); + } else if (value === undefined) { + // Existing binding (or NULL) will be used. + return SQLite.SQLITE_NOTICE; + } else { + console.warn("unknown binding converted to null", value); + return sqlite3.bind_null(stmt, i); + } + } + }; + + sqlite3.bind_blob = (function () { + const fname = "sqlite3_bind_blob"; + const f = Module.cwrap(fname, ...decl("nnnnn:n")); + return function (stmt, i, value) { + verifyStatement(stmt); + // @ts-ignore + const byteLength = value.byteLength ?? value.length; + const ptr = Module._sqlite3_malloc(byteLength); + Module.HEAPU8.subarray(ptr).set(value); + const result = f(stmt, i, ptr, byteLength, sqliteFreeAddress); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_parameter_count = (function () { + const fname = "sqlite3_bind_parameter_count"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.bind_double = (function () { + const fname = "sqlite3_bind_double"; + const f = Module.cwrap(fname, ...decl("nnn:n")); + return function (stmt, i, value) { + verifyStatement(stmt); + const result = f(stmt, i, value); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_int = (function () { + const fname = "sqlite3_bind_int"; + const f = Module.cwrap(fname, ...decl("nnn:n")); + return function (stmt, i, value) { + verifyStatement(stmt); + if (value > 0x7fffffff || value < -0x80000000) return SQLite.SQLITE_RANGE; + + const result = f(stmt, i, value); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_int64 = (function () { + const fname = "sqlite3_bind_int64"; + const f = Module.cwrap(fname, ...decl("nnnn:n")); + return function (stmt, i, value) { + verifyStatement(stmt); + if (value > MAX_INT64 || value < MIN_INT64) return SQLite.SQLITE_RANGE; + + const lo32 = value & 0xffffffffn; + const hi32 = value >> 32n; + const result = f(stmt, i, Number(lo32), Number(hi32)); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_null = (function () { + const fname = "sqlite3_bind_null"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (stmt, i) { + verifyStatement(stmt); + const result = f(stmt, i); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.bind_parameter_name = (function () { + const fname = "sqlite3_bind_parameter_name"; + const f = Module.cwrap(fname, ...decl("n:s")); + return function (stmt, i) { + verifyStatement(stmt); + const result = f(stmt, i); + return result; + }; + })(); + + sqlite3.bind_text = (function () { + const fname = "sqlite3_bind_text"; + const f = Module.cwrap(fname, ...decl("nnnnn:n")); + return function (stmt, i, value) { + verifyStatement(stmt); + const ptr = createUTF8(value); + const result = f(stmt, i, ptr, -1, sqliteFreeAddress); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.changes = (function () { + const fname = "sqlite3_changes"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (db) { + verifyDatabase(db); + const result = f(db); + return result; + }; + })(); + + sqlite3.clear_bindings = (function () { + const fname = "sqlite3_clear_bindings"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (stmt) { + verifyStatement(stmt); + const result = f(stmt); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.close = (function () { + const fname = "sqlite3_close"; + const f = Module.cwrap(fname, ...decl("n:n"), { async }); + return async function (db) { + verifyDatabase(db); + const result = await f(db); + databases.delete(db); + return check(fname, result, db); + }; + })(); + + sqlite3.column = function (stmt, iCol) { + verifyStatement(stmt); + const type = sqlite3.column_type(stmt, iCol); + switch (type) { + case SQLite.SQLITE_BLOB: + return sqlite3.column_blob(stmt, iCol); + case SQLite.SQLITE_FLOAT: + return sqlite3.column_double(stmt, iCol); + case SQLite.SQLITE_INTEGER: + const lo32 = sqlite3.column_int(stmt, iCol); + const hi32 = Module.getTempRet0(); + return cvt32x2AsSafe(lo32, hi32); + case SQLite.SQLITE_NULL: + return null; + case SQLite.SQLITE_TEXT: + return sqlite3.column_text(stmt, iCol); + default: + throw new SQLiteError("unknown type", type); + } + }; + + sqlite3.column_blob = (function () { + const fname = "sqlite3_column_blob"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (stmt, iCol) { + verifyStatement(stmt); + const nBytes = sqlite3.column_bytes(stmt, iCol); + const address = f(stmt, iCol); + const result = Module.HEAPU8.subarray(address, address + nBytes); + return result; + }; + })(); + + sqlite3.column_bytes = (function () { + const fname = "sqlite3_column_bytes"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_count = (function () { + const fname = "sqlite3_column_count"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.column_double = (function () { + const fname = "sqlite3_column_double"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_int = (function () { + // Retrieve int64 but use only the lower 32 bits. The upper 32-bits are + // accessible with Module.getTempRet0(). + const fname = "sqlite3_column_int64"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_int64 = (function () { + const fname = "sqlite3_column_int64"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (stmt, iCol) { + verifyStatement(stmt); + const lo32 = f(stmt, iCol); + const hi32 = Module.getTempRet0(); + const result = cvt32x2ToBigInt(lo32, hi32); + return result; + }; + })(); + + sqlite3.column_name = (function () { + const fname = "sqlite3_column_name"; + const f = Module.cwrap(fname, ...decl("nn:s")); + return function (stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_names = function (stmt) { + const columns = []; + const nColumns = sqlite3.column_count(stmt); + for (let i = 0; i < nColumns; ++i) { + columns.push(sqlite3.column_name(stmt, i)); + } + return columns; + }; + + sqlite3.column_text = (function () { + const fname = "sqlite3_column_text"; + const f = Module.cwrap(fname, ...decl("nn:s")); + return function (stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.column_type = (function () { + const fname = "sqlite3_column_type"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (stmt, iCol) { + verifyStatement(stmt); + const result = f(stmt, iCol); + return result; + }; + })(); + + sqlite3.create_function = function (db, zFunctionName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) { + verifyDatabase(db); + + // Convert SQLite callback arguments to JavaScript-friendly arguments. + function adapt(f) { + return f instanceof AsyncFunction + ? async (ctx, n, values) => f(ctx, Module.HEAP32.subarray(values / 4, values / 4 + n)) + : (ctx, n, values) => f(ctx, Module.HEAP32.subarray(values / 4, values / 4 + n)); + } + + const result = Module.create_function( + db, + zFunctionName, + nArg, + eTextRep, + pApp, + xFunc && adapt(xFunc), + xStep && adapt(xStep), + xFinal + ); + return check("sqlite3_create_function", result, db); + }; + + sqlite3.data_count = (function () { + const fname = "sqlite3_data_count"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.exec = async function (db, sql, callback) { + for await (const stmt of sqlite3.statements(db, sql)) { + let columns; + while ((await sqlite3.step(stmt)) === SQLite.SQLITE_ROW) { + if (callback) { + columns = columns ?? sqlite3.column_names(stmt); + const row = sqlite3.row(stmt); + await callback(row, columns); + } + } + } + return SQLite.SQLITE_OK; + }; + + sqlite3.finalize = (function () { + const fname = "sqlite3_finalize"; + const f = Module.cwrap(fname, ...decl("n:n"), { async }); + return async function (stmt) { + const result = await f(stmt); + mapStmtToDB.delete(stmt); + + // Don't throw on error here. Typically the error has already been + // thrown and finalize() is part of the cleanup. + return result; + }; + })(); + + sqlite3.get_autocommit = (function () { + const fname = "sqlite3_get_autocommit"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (db) { + const result = f(db); + return result; + }; + })(); + + sqlite3.libversion = (function () { + const fname = "sqlite3_libversion"; + const f = Module.cwrap(fname, ...decl(":s")); + return function () { + const result = f(); + return result; + }; + })(); + + sqlite3.libversion_number = (function () { + const fname = "sqlite3_libversion_number"; + const f = Module.cwrap(fname, ...decl(":n")); + return function () { + const result = f(); + return result; + }; + })(); + + sqlite3.limit = (function () { + const fname = "sqlite3_limit"; + const f = Module.cwrap(fname, ...decl("nnn:n")); + return function (db, id, newVal) { + const result = f(db, id, newVal); + return result; + }; + })(); + + sqlite3.open_v2 = (function () { + const fname = "sqlite3_open_v2"; + const f = Module.cwrap(fname, ...decl("snnn:n"), { async }); + return async function (zFilename, flags, zVfs) { + flags = flags || SQLite.SQLITE_OPEN_CREATE | SQLite.SQLITE_OPEN_READWRITE; + zVfs = createUTF8(zVfs); + try { + // Allow retry operations. + const rc = await retry(() => f(zFilename, tmpPtr[0], flags, zVfs)); + + const db = Module.getValue(tmpPtr[0], "*"); + databases.add(db); + + Module.ccall("RegisterExtensionFunctions", "void", ["number"], [db]); + check(fname, rc); + return db; + } finally { + Module._sqlite3_free(zVfs); + } + }; + })(); + + sqlite3.progress_handler = function (db, nProgressOps, handler, userData) { + verifyDatabase(db); + Module.progress_handler(db, nProgressOps, handler, userData); + }; + + sqlite3.reset = (function () { + const fname = "sqlite3_reset"; + const f = Module.cwrap(fname, ...decl("n:n"), { async }); + return async function (stmt) { + verifyStatement(stmt); + const result = await f(stmt); + return check(fname, result, mapStmtToDB.get(stmt)); + }; + })(); + + sqlite3.result = function (context, value) { + switch (typeof value) { + case "number": + if (value === (value | 0)) { + sqlite3.result_int(context, value); + } else { + sqlite3.result_double(context, value); + } + break; + case "string": + sqlite3.result_text(context, value); + break; + default: + if (value instanceof Uint8Array || Array.isArray(value)) { + sqlite3.result_blob(context, value); + } else if (value === null) { + sqlite3.result_null(context); + } else if (typeof value === "bigint") { + return sqlite3.result_int64(context, value); + } else { + console.warn("unknown result converted to null", value); + sqlite3.result_null(context); + } + break; + } + }; + + sqlite3.result_blob = (function () { + const fname = "sqlite3_result_blob"; + const f = Module.cwrap(fname, ...decl("nnnn:n")); + return function (context, value) { + // @ts-ignore + const byteLength = value.byteLength ?? value.length; + const ptr = Module._sqlite3_malloc(byteLength); + Module.HEAPU8.subarray(ptr).set(value); + f(context, ptr, byteLength, sqliteFreeAddress); // void return + }; + })(); + + sqlite3.result_double = (function () { + const fname = "sqlite3_result_double"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (context, value) { + f(context, value); // void return + }; + })(); + + sqlite3.result_int = (function () { + const fname = "sqlite3_result_int"; + const f = Module.cwrap(fname, ...decl("nn:n")); + return function (context, value) { + f(context, value); // void return + }; + })(); + + sqlite3.result_int64 = (function () { + const fname = "sqlite3_result_int64"; + const f = Module.cwrap(fname, ...decl("nnn:n")); + return function (context, value) { + if (value > MAX_INT64 || value < MIN_INT64) return SQLite.SQLITE_RANGE; + + const lo32 = value & 0xffffffffn; + const hi32 = value >> 32n; + f(context, Number(lo32), Number(hi32)); // void return + }; + })(); + + sqlite3.result_null = (function () { + const fname = "sqlite3_result_null"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (context) { + f(context); // void return + }; + })(); + + sqlite3.result_text = (function () { + const fname = "sqlite3_result_text"; + const f = Module.cwrap(fname, ...decl("nnnn:n")); + return function (context, value) { + const ptr = createUTF8(value); + f(context, ptr, -1, sqliteFreeAddress); // void return + }; + })(); + + sqlite3.row = function (stmt) { + const row = []; + const nColumns = sqlite3.data_count(stmt); + for (let i = 0; i < nColumns; ++i) { + const value = sqlite3.column(stmt, i); + + // Copy blob if aliasing volatile WebAssembly memory. This avoids an + // unnecessary copy if users monkey patch column_blob to copy. + // @ts-ignore + row.push(value?.buffer === Module.HEAPU8.buffer ? value.slice() : value); + } + return row; + }; + + sqlite3.set_authorizer = function (db, xAuth, pApp) { + verifyDatabase(db); + + // Convert SQLite callback arguments to JavaScript-friendly arguments. + function cvtArgs(_, iAction, p3, p4, p5, p6) { + return [ + _, + iAction, + Module.UTF8ToString(p3), + Module.UTF8ToString(p4), + Module.UTF8ToString(p5), + Module.UTF8ToString(p6), + ]; + } + function adapt(f) { + return f instanceof AsyncFunction + ? async (_, iAction, p3, p4, p5, p6) => f(...cvtArgs(_, iAction, p3, p4, p5, p6)) + : (_, iAction, p3, p4, p5, p6) => f(...cvtArgs(_, iAction, p3, p4, p5, p6)); + } + + const result = Module.set_authorizer(db, adapt(xAuth), pApp); + return check("sqlite3_set_authorizer", result, db); + }; + + sqlite3.sql = (function () { + const fname = "sqlite3_sql"; + const f = Module.cwrap(fname, ...decl("n:s")); + return function (stmt) { + verifyStatement(stmt); + const result = f(stmt); + return result; + }; + })(); + + sqlite3.statements = function (db, sql, options = {}) { + const prepare = Module.cwrap( + "sqlite3_prepare_v3", + "number", + ["number", "number", "number", "number", "number", "number"], + { async: true } + ); + + return (async function* () { + const onFinally = []; + try { + // Encode SQL string to UTF-8. + const utf8 = new TextEncoder().encode(sql); + + // Copy encoded string to WebAssembly memory. The SQLite docs say + // zero-termination is a minor optimization so add room for that. + // Also add space for the statement handle and SQL tail pointer. + const allocSize = utf8.byteLength - (utf8.byteLength % 4) + 12; + const pzHead = Module._sqlite3_malloc(allocSize); + const pzEnd = pzHead + utf8.byteLength + 1; + onFinally.push(() => Module._sqlite3_free(pzHead)); + Module.HEAPU8.set(utf8, pzHead); + Module.HEAPU8[pzEnd - 1] = 0; + + // Use extra space for the statement handle and SQL tail pointer. + const pStmt = pzHead + allocSize - 8; + const pzTail = pzHead + allocSize - 4; + + // Ensure that statement handles are not leaked. + let stmt; + function maybeFinalize() { + if (stmt && !options.unscoped) { + sqlite3.finalize(stmt); + } + stmt = 0; + } + onFinally.push(maybeFinalize); + + // Loop over statements. + Module.setValue(pzTail, pzHead, "*"); + do { + // Reclaim resources for the previous iteration. + maybeFinalize(); + + // Call sqlite3_prepare_v3() for the next statement. + // Allow retry operations. + const zTail = Module.getValue(pzTail, "*"); + const rc = await retry(() => { + return prepare(db, zTail, pzEnd - pzTail, options.flags || 0, pStmt, pzTail); + }); + + if (rc !== SQLite.SQLITE_OK) { + check("sqlite3_prepare_v3", rc, db); + } + + stmt = Module.getValue(pStmt, "*"); + if (stmt) { + mapStmtToDB.set(stmt, db); + yield stmt; + } + } while (stmt); + } finally { + while (onFinally.length) { + onFinally.pop()(); + } + } + })(); + }; + + sqlite3.step = (function () { + const fname = "sqlite3_step"; + const f = Module.cwrap(fname, ...decl("n:n"), { async }); + return async function (stmt) { + verifyStatement(stmt); + + // Allow retry operations. + const rc = await retry(() => f(stmt)); + + return check(fname, rc, mapStmtToDB.get(stmt), [SQLite.SQLITE_ROW, SQLite.SQLITE_DONE]); + }; + })(); + + sqlite3.update_hook = function (db, xUpdateHook) { + verifyDatabase(db); + + // Convert SQLite callback arguments to JavaScript-friendly arguments. + function cvtArgs(iUpdateType, dbName, tblName, lo32, hi32) { + return [iUpdateType, Module.UTF8ToString(dbName), Module.UTF8ToString(tblName), cvt32x2ToBigInt(lo32, hi32)]; + } + function adapt(f) { + return f instanceof AsyncFunction + ? async (iUpdateType, dbName, tblName, lo32, hi32) => f(...cvtArgs(iUpdateType, dbName, tblName, lo32, hi32)) + : (iUpdateType, dbName, tblName, lo32, hi32) => f(...cvtArgs(iUpdateType, dbName, tblName, lo32, hi32)); + } + + Module.update_hook(db, adapt(xUpdateHook)); + }; + + sqlite3.value = function (pValue) { + const type = sqlite3.value_type(pValue); + switch (type) { + case SQLite.SQLITE_BLOB: + return sqlite3.value_blob(pValue); + case SQLite.SQLITE_FLOAT: + return sqlite3.value_double(pValue); + case SQLite.SQLITE_INTEGER: + const lo32 = sqlite3.value_int(pValue); + const hi32 = Module.getTempRet0(); + return cvt32x2AsSafe(lo32, hi32); + case SQLite.SQLITE_NULL: + return null; + case SQLite.SQLITE_TEXT: + return sqlite3.value_text(pValue); + default: + throw new SQLiteError("unknown type", type); + } + }; + + sqlite3.value_blob = (function () { + const fname = "sqlite3_value_blob"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (pValue) { + const nBytes = sqlite3.value_bytes(pValue); + const address = f(pValue); + const result = Module.HEAPU8.subarray(address, address + nBytes); + return result; + }; + })(); + + sqlite3.value_bytes = (function () { + const fname = "sqlite3_value_bytes"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_double = (function () { + const fname = "sqlite3_value_double"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_int = (function () { + const fname = "sqlite3_value_int64"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_int64 = (function () { + const fname = "sqlite3_value_int64"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (pValue) { + const lo32 = f(pValue); + const hi32 = Module.getTempRet0(); + const result = cvt32x2ToBigInt(lo32, hi32); + return result; + }; + })(); + + sqlite3.value_text = (function () { + const fname = "sqlite3_value_text"; + const f = Module.cwrap(fname, ...decl("n:s")); + return function (pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.value_type = (function () { + const fname = "sqlite3_value_type"; + const f = Module.cwrap(fname, ...decl("n:n")); + return function (pValue) { + const result = f(pValue); + return result; + }; + })(); + + sqlite3.vfs_register = function (vfs, makeDefault) { + const result = Module.vfs_register(vfs, makeDefault); + return check("sqlite3_vfs_register", result); + }; + + function check(fname, result, db = null, allowed = [SQLite.SQLITE_OK]) { + if (allowed.includes(result)) return result; + const message = db ? Module.ccall("sqlite3_errmsg", "string", ["number"], [db]) : fname; + throw new SQLiteError(message, result); + } + + // This function is used to automatically retry failed calls that + // have pending retry operations that should allow the retry to + // succeed. + async function retry(f) { + let rc; + do { + // Wait for all pending retry operations to complete. This is + // normally empty on the first loop iteration. + if (Module.retryOps.length) { + await Promise.all(Module.retryOps); + Module.retryOps = []; + } + + rc = await f(); + + // Retry on failure with new pending retry operations. + } while (rc && Module.retryOps.length); + return rc; + } + + return sqlite3; +} + +// Helper function to use a more compact signature specification. +function decl(s) { + const result = []; + const m = s.match(/([ns@]*):([nsv@])/); + switch (m[2]) { + case "n": + result.push("number"); + break; + case "s": + result.push("string"); + break; + case "v": + result.push(null); + break; + } + + const args = []; + for (let c of m[1]) { + switch (c) { + case "n": + args.push("number"); + break; + case "s": + args.push("string"); + break; + } + } + result.push(args); + return result; +} diff --git a/web/core/local-db/worker/wa-sqlite/src/sqlite-constants.js b/web/core/local-db/worker/wa-sqlite/src/sqlite-constants.js new file mode 100644 index 00000000000..3878b169631 --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/sqlite-constants.js @@ -0,0 +1,275 @@ +// Primary result codes. +// https://www.sqlite.org/rescode.html +export const SQLITE_OK = 0; +export const SQLITE_ERROR = 1; +export const SQLITE_INTERNAL = 2; +export const SQLITE_PERM = 3; +export const SQLITE_ABORT = 4; +export const SQLITE_BUSY = 5; +export const SQLITE_LOCKED = 6; +export const SQLITE_NOMEM = 7; +export const SQLITE_READONLY = 8; +export const SQLITE_INTERRUPT = 9; +export const SQLITE_IOERR = 10; +export const SQLITE_CORRUPT = 11; +export const SQLITE_NOTFOUND = 12; +export const SQLITE_FULL = 13; +export const SQLITE_CANTOPEN = 14; +export const SQLITE_PROTOCOL = 15; +export const SQLITE_EMPTY = 16; +export const SQLITE_SCHEMA = 17; +export const SQLITE_TOOBIG = 18; +export const SQLITE_CONSTRAINT = 19; +export const SQLITE_MISMATCH = 20; +export const SQLITE_MISUSE = 21; +export const SQLITE_NOLFS = 22; +export const SQLITE_AUTH = 23; +export const SQLITE_FORMAT = 24; +export const SQLITE_RANGE = 25; +export const SQLITE_NOTADB = 26; +export const SQLITE_NOTICE = 27; +export const SQLITE_WARNING = 28; +export const SQLITE_ROW = 100; +export const SQLITE_DONE = 101; + +// Extended error codes. +export const SQLITE_IOERR_ACCESS = 3338; +export const SQLITE_IOERR_CHECKRESERVEDLOCK = 3594; +export const SQLITE_IOERR_CLOSE = 4106; +export const SQLITE_IOERR_DATA = 8202; +export const SQLITE_IOERR_DELETE = 2570; +export const SQLITE_IOERR_DELETE_NOENT = 5898; +export const SQLITE_IOERR_DIR_FSYNC = 1290; +export const SQLITE_IOERR_FSTAT = 1802; +export const SQLITE_IOERR_FSYNC = 1034; +export const SQLITE_IOERR_GETTEMPPATH = 6410; +export const SQLITE_IOERR_LOCK = 3850; +export const SQLITE_IOERR_NOMEM = 3082; +export const SQLITE_IOERR_READ = 266; +export const SQLITE_IOERR_RDLOCK = 2314; +export const SQLITE_IOERR_SEEK = 5642; +export const SQLITE_IOERR_SHORT_READ = 522; +export const SQLITE_IOERR_TRUNCATE = 1546; +export const SQLITE_IOERR_UNLOCK = 2058; +export const SQLITE_IOERR_VNODE = 6922; +export const SQLITE_IOERR_WRITE = 778; +export const SQLITE_IOERR_BEGIN_ATOMIC = 7434; +export const SQLITE_IOERR_COMMIT_ATOMIC = 7690; +export const SQLITE_IOERR_ROLLBACK_ATOMIC = 7946; + +// Other extended result codes. +export const SQLITE_CONSTRAINT_CHECK = 275; +export const SQLITE_CONSTRAINT_COMMITHOOK = 531; +export const SQLITE_CONSTRAINT_FOREIGNKEY = 787; +export const SQLITE_CONSTRAINT_FUNCTION = 1043; +export const SQLITE_CONSTRAINT_NOTNULL = 1299; +export const SQLITE_CONSTRAINT_PINNED = 2835; +export const SQLITE_CONSTRAINT_PRIMARYKEY = 1555; +export const SQLITE_CONSTRAINT_ROWID = 2579; +export const SQLITE_CONSTRAINT_TRIGGER = 1811; +export const SQLITE_CONSTRAINT_UNIQUE = 2067; +export const SQLITE_CONSTRAINT_VTAB = 2323; + +// Open flags. +// https://www.sqlite.org/c3ref/c_open_autoproxy.html +export const SQLITE_OPEN_READONLY = 0x00000001; +export const SQLITE_OPEN_READWRITE = 0x00000002; +export const SQLITE_OPEN_CREATE = 0x00000004; +export const SQLITE_OPEN_DELETEONCLOSE = 0x00000008; +export const SQLITE_OPEN_EXCLUSIVE = 0x00000010; +export const SQLITE_OPEN_AUTOPROXY = 0x00000020; +export const SQLITE_OPEN_URI = 0x00000040; +export const SQLITE_OPEN_MEMORY = 0x00000080; +export const SQLITE_OPEN_MAIN_DB = 0x00000100; +export const SQLITE_OPEN_TEMP_DB = 0x00000200; +export const SQLITE_OPEN_TRANSIENT_DB = 0x00000400; +export const SQLITE_OPEN_MAIN_JOURNAL = 0x00000800; +export const SQLITE_OPEN_TEMP_JOURNAL = 0x00001000; +export const SQLITE_OPEN_SUBJOURNAL = 0x00002000; +export const SQLITE_OPEN_SUPER_JOURNAL = 0x00004000; +export const SQLITE_OPEN_NOMUTEX = 0x00008000; +export const SQLITE_OPEN_FULLMUTEX = 0x00010000; +export const SQLITE_OPEN_SHAREDCACHE = 0x00020000; +export const SQLITE_OPEN_PRIVATECACHE = 0x00040000; +export const SQLITE_OPEN_WAL = 0x00080000; +export const SQLITE_OPEN_NOFOLLOW = 0x01000000; + +// Locking levels. +// https://www.sqlite.org/c3ref/c_lock_exclusive.html +export const SQLITE_LOCK_NONE = 0; +export const SQLITE_LOCK_SHARED = 1; +export const SQLITE_LOCK_RESERVED = 2; +export const SQLITE_LOCK_PENDING = 3; +export const SQLITE_LOCK_EXCLUSIVE = 4; + +// Device characteristics. +// https://www.sqlite.org/c3ref/c_iocap_atomic.html +export const SQLITE_IOCAP_ATOMIC = 0x00000001; +export const SQLITE_IOCAP_ATOMIC512 = 0x00000002; +export const SQLITE_IOCAP_ATOMIC1K = 0x00000004; +export const SQLITE_IOCAP_ATOMIC2K = 0x00000008; +export const SQLITE_IOCAP_ATOMIC4K = 0x00000010; +export const SQLITE_IOCAP_ATOMIC8K = 0x00000020; +export const SQLITE_IOCAP_ATOMIC16K = 0x00000040; +export const SQLITE_IOCAP_ATOMIC32K = 0x00000080; +export const SQLITE_IOCAP_ATOMIC64K = 0x00000100; +export const SQLITE_IOCAP_SAFE_APPEND = 0x00000200; +export const SQLITE_IOCAP_SEQUENTIAL = 0x00000400; +export const SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 0x00000800; +export const SQLITE_IOCAP_POWERSAFE_OVERWRITE = 0x00001000; +export const SQLITE_IOCAP_IMMUTABLE = 0x00002000; +export const SQLITE_IOCAP_BATCH_ATOMIC = 0x00004000; + +// xAccess flags. +// https://www.sqlite.org/c3ref/c_access_exists.html +export const SQLITE_ACCESS_EXISTS = 0; +export const SQLITE_ACCESS_READWRITE = 1; +export const SQLITE_ACCESS_READ = 2; + +// File control opcodes +// https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlbeginatomicwrite +export const SQLITE_FCNTL_LOCKSTATE = 1; +export const SQLITE_FCNTL_GET_LOCKPROXYFILE = 2; +export const SQLITE_FCNTL_SET_LOCKPROXYFILE = 3; +export const SQLITE_FCNTL_LAST_ERRNO = 4; +export const SQLITE_FCNTL_SIZE_HINT = 5; +export const SQLITE_FCNTL_CHUNK_SIZE = 6; +export const SQLITE_FCNTL_FILE_POINTER = 7; +export const SQLITE_FCNTL_SYNC_OMITTED = 8; +export const SQLITE_FCNTL_WIN32_AV_RETRY = 9; +export const SQLITE_FCNTL_PERSIST_WAL = 10; +export const SQLITE_FCNTL_OVERWRITE = 11; +export const SQLITE_FCNTL_VFSNAME = 12; +export const SQLITE_FCNTL_POWERSAFE_OVERWRITE = 13; +export const SQLITE_FCNTL_PRAGMA = 14; +export const SQLITE_FCNTL_BUSYHANDLER = 15; +export const SQLITE_FCNTL_TEMPFILENAME = 16; +export const SQLITE_FCNTL_MMAP_SIZE = 18; +export const SQLITE_FCNTL_TRACE = 19; +export const SQLITE_FCNTL_HAS_MOVED = 20; +export const SQLITE_FCNTL_SYNC = 21; +export const SQLITE_FCNTL_COMMIT_PHASETWO = 22; +export const SQLITE_FCNTL_WIN32_SET_HANDLE = 23; +export const SQLITE_FCNTL_WAL_BLOCK = 24; +export const SQLITE_FCNTL_ZIPVFS = 25; +export const SQLITE_FCNTL_RBU = 26; +export const SQLITE_FCNTL_VFS_POINTER = 27; +export const SQLITE_FCNTL_JOURNAL_POINTER = 28; +export const SQLITE_FCNTL_WIN32_GET_HANDLE = 29; +export const SQLITE_FCNTL_PDB = 30; +export const SQLITE_FCNTL_BEGIN_ATOMIC_WRITE = 31; +export const SQLITE_FCNTL_COMMIT_ATOMIC_WRITE = 32; +export const SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE = 33; +export const SQLITE_FCNTL_LOCK_TIMEOUT = 34; +export const SQLITE_FCNTL_DATA_VERSION = 35; +export const SQLITE_FCNTL_SIZE_LIMIT = 36; +export const SQLITE_FCNTL_CKPT_DONE = 37; +export const SQLITE_FCNTL_RESERVE_BYTES = 38; +export const SQLITE_FCNTL_CKPT_START = 39; + +// Fundamental datatypes. +// https://www.sqlite.org/c3ref/c_blob.html +export const SQLITE_INTEGER = 1; +export const SQLITE_FLOAT = 2; +export const SQLITE_TEXT = 3; +export const SQLITE_BLOB = 4; +export const SQLITE_NULL = 5; + +// Special destructor behavior. +// https://www.sqlite.org/c3ref/c_static.html +export const SQLITE_STATIC = 0; +export const SQLITE_TRANSIENT = -1; + +// Text encodings. +// https://sqlite.org/c3ref/c_any.html +export const SQLITE_UTF8 = 1; /* IMP: R-37514-35566 */ +export const SQLITE_UTF16LE = 2; /* IMP: R-03371-37637 */ +export const SQLITE_UTF16BE = 3; /* IMP: R-51971-34154 */ +export const SQLITE_UTF16 = 4; /* Use native byte order */ + +// Module constraint ops. +export const SQLITE_INDEX_CONSTRAINT_EQ = 2; +export const SQLITE_INDEX_CONSTRAINT_GT = 4; +export const SQLITE_INDEX_CONSTRAINT_LE = 8; +export const SQLITE_INDEX_CONSTRAINT_LT = 16; +export const SQLITE_INDEX_CONSTRAINT_GE = 32; +export const SQLITE_INDEX_CONSTRAINT_MATCH = 64; +export const SQLITE_INDEX_CONSTRAINT_LIKE = 65; +export const SQLITE_INDEX_CONSTRAINT_GLOB = 66; +export const SQLITE_INDEX_CONSTRAINT_REGEXP = 67; +export const SQLITE_INDEX_CONSTRAINT_NE = 68; +export const SQLITE_INDEX_CONSTRAINT_ISNOT = 69; +export const SQLITE_INDEX_CONSTRAINT_ISNOTNULL = 70; +export const SQLITE_INDEX_CONSTRAINT_ISNULL = 71; +export const SQLITE_INDEX_CONSTRAINT_IS = 72; +export const SQLITE_INDEX_CONSTRAINT_FUNCTION = 150; +export const SQLITE_INDEX_SCAN_UNIQUE = 1; /* Scan visits at most = 1 row */ + +// Function flags +export const SQLITE_DETERMINISTIC = 0x000000800; +export const SQLITE_DIRECTONLY = 0x000080000; +export const SQLITE_SUBTYPE = 0x000100000; +export const SQLITE_INNOCUOUS = 0x000200000; + +// Sync flags +export const SQLITE_SYNC_NORMAL = 0x00002; +export const SQLITE_SYNC_FULL = 0x00003; +export const SQLITE_SYNC_DATAONLY = 0x00010; + +// Authorizer action codes +export const SQLITE_CREATE_INDEX = 1; +export const SQLITE_CREATE_TABLE = 2; +export const SQLITE_CREATE_TEMP_INDEX = 3; +export const SQLITE_CREATE_TEMP_TABLE = 4; +export const SQLITE_CREATE_TEMP_TRIGGER = 5; +export const SQLITE_CREATE_TEMP_VIEW = 6; +export const SQLITE_CREATE_TRIGGER = 7; +export const SQLITE_CREATE_VIEW = 8; +export const SQLITE_DELETE = 9; +export const SQLITE_DROP_INDEX = 10; +export const SQLITE_DROP_TABLE = 11; +export const SQLITE_DROP_TEMP_INDEX = 12; +export const SQLITE_DROP_TEMP_TABLE = 13; +export const SQLITE_DROP_TEMP_TRIGGER = 14; +export const SQLITE_DROP_TEMP_VIEW = 15; +export const SQLITE_DROP_TRIGGER = 16; +export const SQLITE_DROP_VIEW = 17; +export const SQLITE_INSERT = 18; +export const SQLITE_PRAGMA = 19; +export const SQLITE_READ = 20; +export const SQLITE_SELECT = 21; +export const SQLITE_TRANSACTION = 22; +export const SQLITE_UPDATE = 23; +export const SQLITE_ATTACH = 24; +export const SQLITE_DETACH = 25; +export const SQLITE_ALTER_TABLE = 26; +export const SQLITE_REINDEX = 27; +export const SQLITE_ANALYZE = 28; +export const SQLITE_CREATE_VTABLE = 29; +export const SQLITE_DROP_VTABLE = 30; +export const SQLITE_FUNCTION = 31; +export const SQLITE_SAVEPOINT = 32; +export const SQLITE_COPY = 0; +export const SQLITE_RECURSIVE = 33; + +// Authorizer return codes +export const SQLITE_DENY = 1; +export const SQLITE_IGNORE = 2; + +// Limit categories +export const SQLITE_LIMIT_LENGTH = 0; +export const SQLITE_LIMIT_SQL_LENGTH = 1; +export const SQLITE_LIMIT_COLUMN = 2; +export const SQLITE_LIMIT_EXPR_DEPTH = 3; +export const SQLITE_LIMIT_COMPOUND_SELECT = 4; +export const SQLITE_LIMIT_VDBE_OP = 5; +export const SQLITE_LIMIT_FUNCTION_ARG = 6; +export const SQLITE_LIMIT_ATTACHED = 7; +export const SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8; +export const SQLITE_LIMIT_VARIABLE_NUMBER = 9; +export const SQLITE_LIMIT_TRIGGER_DEPTH = 10; +export const SQLITE_LIMIT_WORKER_THREADS = 11; + +export const SQLITE_PREPARE_PERSISTENT = 0x01; +export const SQLITE_PREPARE_NORMALIZED = 0x02; +export const SQLITE_PREPARE_NO_VTAB = 0x04; \ No newline at end of file diff --git a/web/core/local-db/worker/wa-sqlite/src/types/globals.d.ts b/web/core/local-db/worker/wa-sqlite/src/types/globals.d.ts new file mode 100644 index 00000000000..7c4507cec10 --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/types/globals.d.ts @@ -0,0 +1,60 @@ +declare namespace Asyncify { + function handleAsync(f: () => Promise); +} + +declare function UTF8ToString(ptr: number): string; +declare function lengthBytesUTF8(s: string): number; +declare function stringToUTF8(s: string, p: number, n: number); +declare function ccall(name: string, returns: string, args: Array, options?: object): any; +declare function getValue(ptr: number, type: string): number; +declare function setValue(ptr: number, value: number, type: string): number; +declare function mergeInto(library: object, methods: object): void; + +declare var HEAPU8: Uint8Array; +declare var HEAPU32: Uint32Array; +declare var LibraryManager; +declare var Module; +declare var _vfsAccess; +declare var _vfsCheckReservedLock; +declare var _vfsClose; +declare var _vfsDelete; +declare var _vfsDeviceCharacteristics; +declare var _vfsFileControl; +declare var _vfsFileSize; +declare var _vfsLock; +declare var _vfsOpen; +declare var _vfsRead; +declare var _vfsSectorSize; +declare var _vfsSync; +declare var _vfsTruncate; +declare var _vfsUnlock; +declare var _vfsWrite; + +declare var _jsFunc; +declare var _jsStep; +declare var _jsFinal; + +declare var _modStruct; +declare var _modCreate; +declare var _modConnect; +declare var _modBestIndex; +declare var _modDisconnect; +declare var _modDestroy; +declare var _modOpen; +declare var _modClose; +declare var _modFilter; +declare var _modNext; +declare var _modEof; +declare var _modColumn; +declare var _modRowid; +declare var _modUpdate; +declare var _modBegin; +declare var _modSync; +declare var _modCommit; +declare var _modRollback; +declare var _modFindFunction; +declare var _modRename; + +declare var _jsAuth; + +declare var _jsProgress; \ No newline at end of file diff --git a/web/core/local-db/worker/wa-sqlite/src/types/index.d.ts b/web/core/local-db/worker/wa-sqlite/src/types/index.d.ts new file mode 100644 index 00000000000..4056786243d --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/types/index.d.ts @@ -0,0 +1,1317 @@ +/** + * This is a WebAssembly build of SQLite with experimental support for + * writing SQLite virtual file systems and modules (for virtual tables) + * in Javascript. Also see the + * [GitHub repository](https://github.com/rhashimoto/wa-sqlite) and the + * [online demo](https://rhashimoto.github.io/wa-sqlite/demo/). + * @module + */ + +/** + * Javascript types that SQLite can use + * + * C integer and floating-point types both map to/from Javascript `number`. + * Blob data can be provided to SQLite as `Uint8Array` or `number[]` (with + * each element converted to a byte); SQLite always returns blob data as + * `Uint8Array` + */ +type SQLiteCompatibleType = number|string|Uint8Array|Array|bigint|null; + +/** + * SQLite Virtual File System object + * + * Objects with this interface can be passed to {@link SQLiteAPI.vfs_register} + * to define a new filesystem. + * + * There are examples of a synchronous + * [MemoryVFS.js](https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/MemoryVFS.js), + * and asynchronous + * [MemoryAsyncVFS.js](https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/MemoryAsyncVFS.js) + * and + * [IndexedDbVFS.js](https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/IndexedDbVFS.js). + * + * @see https://sqlite.org/vfs.html + * @see https://sqlite.org/c3ref/io_methods.html + */ +declare interface SQLiteVFS { + /** Maximum length of a file path in UTF-8 bytes (default 64) */ + mxPathName?: number; + + close(): void|Promise; + isReady(): boolean|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xClose(fileId: number): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xRead( + fileId: number, + pData: number, + iAmt: number, + iOffsetLo: number, + iOffsetHi: number + ): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xWrite( + fileId: number, + pData: number, + iAmt: number, + iOffsetLo: number, + iOffsetHi: number + ): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xTruncate(fileId: number, iSizeLo: number, iSizeHi): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xSync(fileId: number, flags: number): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xFileSize( + fileId: number, + pSize64: number + ): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xLock(fileId: number, flags: number): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xUnlock(fileId: number, flags: number): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xCheckReservedLock( + fileId: number, + pResOut: number + ): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xFileControl( + fileId: number, + flags: number, + pOut: number + ): number|Promise; + + /** @see https://sqlite.org/c3ref/io_methods.html */ + xDeviceCharacteristics(fileId: number): number|Promise; + + /** @see https://sqlite.org/c3ref/vfs.html */ + xOpen( + pVfs: number, + zName: number, + pFile: number, + flags: number, + pOutFlags: number + ): number|Promise; + + /** @see https://sqlite.org/c3ref/vfs.html */ + xDelete(pVfs: number, zName: number, syncDir: number): number|Promise; + + /** @see https://sqlite.org/c3ref/vfs.html */ + xAccess( + pVfs: number, + zName: number, + flags: number, + pResOut: number + ): number|Promise; +} + +/** + * Options object argument for {@link SQLiteAPI.statements} + */ +declare interface SQLitePrepareOptions { + /** + * Statement handles prepared and yielded by {@link SQLiteAPI.statements} + * are normally valid only within the scope of an iteration. + * Set `unscoped` to `true` to give iterated statements an arbitrary + * lifetime. + */ + unscoped?: boolean; + + /** + * SQLITE_PREPARE_* flags + * @see https://www.sqlite.org/c3ref/c_prepare_normalize.html#sqlitepreparepersistent + */ + flags?: number; +} + +/** + * Javascript wrappers for the SQLite C API (plus a few convenience functions) + * + * Function signatures have been slightly modified to be more + * Javascript-friendly. For the C functions that return an error code, + * the corresponding Javascript wrapper will throw an exception with a + * `code` property on an error. + * + * Note that a few functions return a Promise in order to accomodate + * either a synchronous or asynchronous SQLite build, generally those + * involved with opening/closing a database or executing a statement. + * + * To create an instance of the API, follow these steps: + * + * ```javascript + * // Import an ES6 module factory function from one of the + * // package builds, either 'wa-sqlite.mjs' (synchronous) or + * // 'wa-sqlite-async.mjs' (asynchronous). You should only + * // use the asynchronous build if you plan to use an + * // asynchronous VFS or module. + * import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite.mjs'; + * + * // Import the Javascript API wrappers. + * import * as SQLite from 'wa-sqlite'; + * + * // Use an async function to simplify Promise handling. + * (async function() { + * // Invoke the ES6 module factory to create the SQLite + * // Emscripten module. This will fetch and compile the + * // .wasm file. + * const module = await SQLiteESMFactory(); + * + * // Use the module to build the API instance. + * const sqlite3 = SQLite.Factory(module); + * + * // Use the API to open and access a database. + * const db = await sqlite3.open_v2('myDB'); + * ... + * })(); + * ``` + * + * @see https://sqlite.org/c3ref/funclist.html + */ +declare interface SQLiteAPI { + /** + * Bind a collection of values to a statement + * + * This convenience function binds values from either an array or object + * to a prepared statement with placeholder parameters. + * + * Array example using numbered parameters (numbering is implicit in + * this example): + * ``` + * const sql = 'INSERT INTO tbl VALUES (?, ?, ?)'; + * for await (const stmt of sqlite3.statements(db, sql) { + * sqlite3.bind_collection(stmt, [42, 'hello', null]); + * ... + * } + * ``` + * + * Object example using named parameters (':', '@', or '$' prefixes + * are allowed): + * ``` + * const sql = 'INSERT INTO tbl VALUES (?, ?, ?)'; + * for await (const stmt of sqlite3.statements(db, sql) { + * sqlite3.bind_collection(stmt, { + * '@foo': 42, + * '@bar': 'hello', + * '@baz': null, + * }); + * ... + * } + * ``` + * + * Note that SQLite bindings are indexed beginning with 1, but when + * binding values from an array `a` the values begin with `a[0]`. + * @param stmt prepared statement pointer + * @param bindings + * @returns `SQLITE_OK` (throws exception on error) + */ + bind_collection( + stmt: number, + bindings: {[index: string]: SQLiteCompatibleType|null}|Array + ): number; + + /** + * Bind value to prepared statement + * + * This convenience function calls the appropriate `bind_*` function + * based on the type of `value`. Note that binding indices begin with 1. + * @param stmt prepared statement pointer + * @param i binding index + * @param value + * @returns `SQLITE_OK` (throws exception on error) + */ + bind(stmt: number, i: number, value: SQLiteCompatibleType|null): number; + + /** + * Bind blob to prepared statement parameter + * + * Note that binding indices begin with 1. + * @see https://www.sqlite.org/c3ref/bind_blob.html + * @param stmt prepared statement pointer + * @param i binding index + * @param value + * @returns `SQLITE_OK` (throws exception on error) + */ + bind_blob(stmt: number, i: number, value: Uint8Array|Array): number; + + /** + * Bind number to prepared statement parameter + * + * Note that binding indices begin with 1. + * @see https://www.sqlite.org/c3ref/bind_blob.html + * @param stmt prepared statement pointer + * @param i binding index + * @param value + * @returns `SQLITE_OK` (throws exception on error) + */ + bind_double(stmt: number, i: number, value: number): number; + + /** + * Bind number to prepared statement parameter + * + * Note that binding indices begin with 1. + * @see https://www.sqlite.org/c3ref/bind_blob.html + * @param stmt prepared statement pointer + * @param i binding index + * @param value + * @returns `SQLITE_OK` (throws exception on error) + */ + bind_int(stmt: number, i: number, value: number): number; + + /** + * Bind number to prepared statement parameter + * + * Note that binding indices begin with 1. + * @see https://www.sqlite.org/c3ref/bind_blob.html + * @param stmt prepared statement pointer + * @param i binding index + * @param value + * @returns `SQLITE_OK` (throws exception on error) + */ + bind_int64(stmt: number, i: number, value: bigint): number; + + /** + * Bind null to prepared statement + * + * Note that binding indices begin with 1. + * @see https://www.sqlite.org/c3ref/bind_blob.html + * @param stmt prepared statement pointer + * @param i binding index + * @returns `SQLITE_OK` (throws exception on error) + */ + bind_null(stmt: number, i: number): number; + + /** + * Get number of bound parameters + * @see https://www.sqlite.org/c3ref/bind_parameter_count.html + * @param stmt prepared statement pointer + * @returns number of statement binding locations + */ + bind_parameter_count(stmt: number): number; + + /** + * Get name of bound parameter + * + * Note that binding indices begin with 1. + * @see https://www.sqlite.org/c3ref/bind_parameter_name.html + * @param stmt prepared statement pointer + * @param i binding index + * @returns binding name + */ + bind_parameter_name(stmt: number, i: number): string; + + /** + * Bind string to prepared statement + * + * Note that binding indices begin with 1. + * @see https://www.sqlite.org/c3ref/bind_blob.html + * @param stmt prepared statement pointer + * @param i binding index + * @param value + * @returns `SQLITE_OK` (throws exception on error) + */ + bind_text(stmt: number, i: number, value: string): number; + + /** + * Get count of rows modified by last insert/update + * @see https://www.sqlite.org/c3ref/changes.html + * @param db database pointer + * @returns number of rows modified + */ + changes(db): number; + + /** + * Reset all bindings on a prepared statement. + * @see https://www.sqlite.org/c3ref/clear_bindings.html + * @param stmt prepared statement pointer + * @returns `SQLITE_OK` (throws exception on error) + */ + clear_bindings(stmt: number): number; + + /** + * Close database connection + * @see https://www.sqlite.org/c3ref/close.html + * @param db database pointer + * @returns `SQLITE_OK` (throws exception on error) + */ + close(db): Promise; + + /** + * Call the appropriate `column_*` function based on the column type + * + * The type is determined by calling {@link column_type}, which may + * not match the type declared in `CREATE TABLE`. Note that if the column + * value is a blob then as with `column_blob` the result may be invalid + * after the next SQLite call; copy if it needs to be retained. + * + * Integer values are returned as Number if within the min/max safe + * integer bounds, otherwise they are returned as BigInt. + * @param stmt prepared statement pointer + * @param i column index + * @returns column value + */ + column(stmt: number, i: number): SQLiteCompatibleType; + + /** + * Extract a column value from a row after a prepared statment {@link step} + * + * The contents of the returned buffer may be invalid after the + * next SQLite call. Make a copy of the data (e.g. with `.slice()`) + * if longer retention is required. + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns column value + */ + column_blob(stmt: number, i: number): Uint8Array; + + /** + * Get storage size for column text or blob + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns number of bytes in column text or blob + */ + column_bytes(stmt: number, i: number): number; + + /** + * Get number of columns for a prepared statement + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @returns number of columns + */ + column_count(stmt: number): number; + + /** + * Extract a column value from a row after a prepared statment {@link step} + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns column value + */ + column_double(stmt: number, i: number): number; + + /** + * Extract a column value from a row after a prepared statment {@link step} + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns column value + */ + column_int(stmt: number, i: number): number; + + /** + * Extract a column value from a row after a prepared statment {@link step} + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns column value + */ + column_int64(stmt: number, i: number): bigint; + + /** + * Get a column name for a prepared statement + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns column name + */ + column_name(stmt: number, i: number): string; + + /** + * Get names for all columns of a prepared statement + * + * This is a convenience function that calls {@link column_count} and + * {@link column_name}. + * @param stmt + * @returns array of column names + */ + column_names(stmt: number): Array; + + /** + * Extract a column value from a row after a prepared statment {@link step} + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns column value + */ + column_text(stmt: number, i: number): string; + + /** + * Get column type for a prepared statement + * + * Note that this type may not match the type declared in `CREATE TABLE`. + * @see https://www.sqlite.org/c3ref/column_blob.html + * @param stmt prepared statement pointer + * @param i column index + * @returns enumeration value for type + */ + column_type(stmt: number, i: number): number; + + /** + * Create or redefine SQL functions + * + * The application data passed is ignored. Use closures instead. + * + * If any callback function returns a Promise, that function must + * be declared `async`, i.e. it must allow use of `await`. + * @see https://sqlite.org/c3ref/create_function.html + * @param db database pointer + * @param zFunctionName + * @param nArg number of function arguments + * @param eTextRep text encoding (and other flags) + * @param pApp application data (ignored) + * @param xFunc + * @param xStep + * @param xFinal + * @returns `SQLITE_OK` (throws exception on error) + */ + create_function( + db: number, + zFunctionName: string, + nArg: number, + eTextRep: number, + pApp: number, + xFunc?: (context: number, values: Uint32Array) => void|Promise, + xStep?: (context: number, values: Uint32Array) => void|Promise, + xFinal?: (context: number) => void|Promise): number; + + /** + * Get number of columns in current row of a prepared statement + * @see https://www.sqlite.org/c3ref/data_count.html + * @param stmt prepared statement pointer + * @returns number of columns + */ + data_count(stmt: number): number; + + /** + * One-step query execution interface + * + * The implementation of this function uses {@link row}, which makes a + * copy of blobs and returns BigInt for integers outside the safe integer + * bounds for Number. + * @see https://www.sqlite.org/c3ref/exec.html + * @param db database pointer + * @param zSQL queries + * @param callback called for each output row + * @returns Promise resolving to `SQLITE_OK` (rejects on error) + */ + exec( + db: number, + zSQL: string, + callback?: (row: Array, columns: string[]) => void + ): Promise; + + /** + * Destroy a prepared statement object compiled by {@link statements} + * with the `unscoped` option set to `true` + * + * This function does *not* throw on error. + * @see https://www.sqlite.org/c3ref/finalize.html + * @param stmt prepared statement pointer + * @returns Promise resolving to `SQLITE_OK` or error status + */ + finalize(stmt: number): Promise; + + /** + * Test for autocommit mode + * @see https://sqlite.org/c3ref/get_autocommit.html + * @param db database pointer + * @returns Non-zero if autocommit mode is on, zero otherwise + */ + get_autocommit(db: number): number; + + /** + * Get SQLite library version + * @see https://www.sqlite.org/c3ref/libversion.html + * @returns version string, e.g. '3.35.5' + */ + libversion(): string; + + /** + * Get SQLite library version + * @see https://www.sqlite.org/c3ref/libversion.html + * @returns version number, e.g. 3035005 + */ + libversion_number(): number + + /** + * Set a usage limit on a connection. + * @see https://www.sqlite.org/c3ref/limit.html + * @param db database pointer + * @param id limit category + * @param newVal + * @returns previous setting + */ + limit( + db: number, + id: number, + newVal: number): number; + + /** + * Opening a new database connection. + * + * Note that this function differs from the C API in that it + * returns the Promise-wrapped database pointer (instead of a + * result code). + * @see https://sqlite.org/c3ref/open.html + * @param zFilename + * @param iFlags `SQLite.SQLITE_OPEN_CREATE | SQLite.SQLITE_OPEN_READWRITE` (0x6) if omitted + * @param zVfs VFS name + * @returns Promise-wrapped database pointer. + */ + open_v2( + zFilename: string, + iFlags?: number, + zVfs?: string + ): Promise; + + /** + * Specify callback to be invoked between long-running queries + * + * The application data passed is ignored. Use closures instead. + * + * If any callback function returns a Promise, that function must + * be declared `async`, i.e. it must allow use of `await`. + * @param db database pointer + * @param nProgressOps target number of database operations between handler invocations + * @param handler + * @param userData + */ + progress_handler(db: number, nProgressOps: number, handler: (userData: any) => number|Promise, userData); + + /** + * Reset a prepared statement object + * @see https://www.sqlite.org/c3ref/reset.html + * @param stmt prepared statement pointer + * @returns Promise-wrapped `SQLITE_OK` (rejects on error) + */ + reset(stmt: number): Promise; + + /** + * Convenience function to call `result_*` based of the type of `value` + * @param context context pointer + * @param value + */ + result(context: number, value: (SQLiteCompatibleType|number[])|null): void; + + /** + * Set the result of a function or vtable column + * @see https://sqlite.org/c3ref/result_blob.html + * @param context context pointer + * @param value + */ + result_blob(context: number, value: Uint8Array|number[]): void; + + /** + * Set the result of a function or vtable column + * @see https://sqlite.org/c3ref/result_blob.html + * @param context context pointer + * @param value + */ + result_double(context: number, value: number): void; + + /** + * Set the result of a function or vtable column + * @see https://sqlite.org/c3ref/result_blob.html + * @param context context pointer + * @param value + */ + result_int(context: number, value: number): void; + + /** + * Set the result of a function or vtable column + * @see https://sqlite.org/c3ref/result_blob.html + * @param context context pointer + * @param value + */ + result_int64(context: number, value: bigint): void; + + /** + * Set the result of a function or vtable column + * @see https://sqlite.org/c3ref/result_blob.html + * @param context context pointer + */ + result_null(context: number): void; + + /** + * Set the result of a function or vtable column + * @see https://sqlite.org/c3ref/result_blob.html + * @param context context pointer + * @param value + */ + result_text(context: number, value: string): void; + + /** + * Get all column data for a row from a prepared statement step + * + * This convenience function will return a copy of any blob, unlike + * {@link column_blob} which returns a value referencing volatile WASM + * memory with short validity. Like {@link column}, it will return a + * BigInt for integers outside the safe integer bounds for Number. + * @param stmt prepared statement pointer + * @returns row data + */ + row(stmt: number): Array; + + /** + * Register a callback function that is invoked to authorize certain SQL statement actions. + * @see https://www.sqlite.org/c3ref/set_authorizer.html + * @param db database pointer + * @param authFunction + * @param userData + */ + set_authorizer( + db: number, + authFunction: (userData: any, iActionCode: number, param3: string|null, param4: string|null, param5: string|null, param6: string|null) => number|Promise, + userData: any): number; + + /** + * Get statement SQL + * @see https://www.sqlite.org/c3ref/expanded_sql.html + * @param stmt prepared statement pointer + * @returns SQL + */ + sql(stmt: number): string; + + /** + * SQL statement iterator + * + * This function manages statement compilation by creating an async + * iterator that yields a prepared statement handle on each iteration. + * It is typically used with a `for await` loop (in an async function), + * like this: + * ```javascript + * // Compile one statement on each iteration of this loop. + * for await (const stmt of sqlite3.statements(db, sql)) { + * // Bind parameters here if using SQLite placeholders. + * + * // Execute the statement with this loop. + * while (await sqlite3.step(stmt) === SQLite.SQLITE_ROW) { + * // Collect row data here. + * } + * + * // Change bindings, reset, and execute again if desired. + * } + * ``` + * + * By default, the lifetime of a yielded prepared statement is managed + * automatically by the iterator, ending at the end of each iteration. + * {@link finalize} should *not* be called on a statement provided by + * the iterator unless the `unscoped` option is set to `true` (that + * option is provided for applications that wish to manage statement + * lifetimes manually). + * + * If using the iterator manually, i.e. by calling its `next` + * method, be sure to call the `return` method if iteration + * is abandoned before completion (`for await` and other implicit + * traversals provided by Javascript do this automatically) + * to ensure that all allocated resources are released. + * @see https://www.sqlite.org/c3ref/prepare.html + * @param db database pointer + * @param sql + * @param options + */ + statements(db: number, sql: string, options?: SQLitePrepareOptions): AsyncIterable; + + /** + * Evaluate an SQL statement + * @see https://www.sqlite.org/c3ref/step.html + * @param stmt prepared statement pointer + * @returns Promise resolving to `SQLITE_ROW` or `SQLITE_DONE` + * (rejects on error) + */ + step(stmt: number): Promise; + + /** + * Register an update hook + * + * The callback is invoked whenever a row is updated, inserted, or deleted + * in a rowid table on this connection. + * @see https://www.sqlite.org/c3ref/update_hook.html + * + * updateType is one of: + * - SQLITE_DELETE: 9 + * - SQLITE_INSERT: 18 + * - SQLITE_UPDATE: 23 + * @see https://www.sqlite.org/c3ref/c_alter_table.html + * + * @param db database pointer + * @param callback + */ + update_hook( + db: number, + callback: (updateType: number, dbName: string|null, tblName: string|null, rowid: bigint) => void): void; + + /** + * Extract a value from `sqlite3_value` + * + * This is a convenience function that calls the appropriate `value_*` + * function based on its type. Note that if the value is a blob then as + * with `value_blob` the result may be invalid after the next SQLite call. + * + * Integer values are returned as Number if within the min/max safe + * integer bounds, otherwise they are returned as BigInt. + * @param pValue `sqlite3_value` pointer + * @returns value + */ + value(pValue: number): SQLiteCompatibleType; + + /** + * Extract a value from `sqlite3_value` + * + * The contents of the returned buffer may be invalid after the + * next SQLite call. Make a copy of the data (e.g. with `.slice()`) + * if longer retention is required. + * @see https://sqlite.org/c3ref/value_blob.html + * @param pValue `sqlite3_value` pointer + * @returns value + */ + value_blob(pValue: number): Uint8Array; + + /** + * Get blob or text size for value + * @see https://sqlite.org/c3ref/value_blob.html + * @param pValue `sqlite3_value` pointer + * @returns size + */ + value_bytes(pValue: number): number; + + /** + * Extract a value from `sqlite3_value` + * @see https://sqlite.org/c3ref/value_blob.html + * @param pValue `sqlite3_value` pointer + * @returns value + */ + value_double(pValue: number): number; + + /** + * Extract a value from `sqlite3_value` + * @see https://sqlite.org/c3ref/value_blob.html + * @param pValue `sqlite3_value` pointer + * @returns value + */ + value_int(pValue: number): number; + + /** + * Extract a value from `sqlite3_value` + * @see https://sqlite.org/c3ref/value_blob.html + * @param pValue `sqlite3_value` pointer + * @returns value + */ + value_int64(pValue: number): bigint; + + /** + * Extract a value from `sqlite3_value` + * @see https://sqlite.org/c3ref/value_blob.html + * @param pValue `sqlite3_value` pointer + * @returns value + */ + value_text(pValue: number): string; + + /** + * Get type of `sqlite3_value` + * @see https://sqlite.org/c3ref/value_blob.html + * @param pValue `sqlite3_value` pointer + * @returns enumeration value for type + */ + value_type(pValue: number): number; + + /** + * Register a new Virtual File System. + * + * @see https://www.sqlite.org/c3ref/vfs_find.html + * @param vfs VFS object + * @param makeDefault + * @returns `SQLITE_OK` (throws exception on error) + */ + vfs_register(vfs: SQLiteVFS, makeDefault?: boolean): number; +} + +/** @ignore */ +declare module 'wa-sqlite/src/sqlite-constants.js' { + export const SQLITE_OK: 0; + export const SQLITE_ERROR: 1; + export const SQLITE_INTERNAL: 2; + export const SQLITE_PERM: 3; + export const SQLITE_ABORT: 4; + export const SQLITE_BUSY: 5; + export const SQLITE_LOCKED: 6; + export const SQLITE_NOMEM: 7; + export const SQLITE_READONLY: 8; + export const SQLITE_INTERRUPT: 9; + export const SQLITE_IOERR: 10; + export const SQLITE_CORRUPT: 11; + export const SQLITE_NOTFOUND: 12; + export const SQLITE_FULL: 13; + export const SQLITE_CANTOPEN: 14; + export const SQLITE_PROTOCOL: 15; + export const SQLITE_EMPTY: 16; + export const SQLITE_SCHEMA: 17; + export const SQLITE_TOOBIG: 18; + export const SQLITE_CONSTRAINT: 19; + export const SQLITE_MISMATCH: 20; + export const SQLITE_MISUSE: 21; + export const SQLITE_NOLFS: 22; + export const SQLITE_AUTH: 23; + export const SQLITE_FORMAT: 24; + export const SQLITE_RANGE: 25; + export const SQLITE_NOTADB: 26; + export const SQLITE_NOTICE: 27; + export const SQLITE_WARNING: 28; + export const SQLITE_ROW: 100; + export const SQLITE_DONE: 101; + export const SQLITE_IOERR_ACCESS: 3338; + export const SQLITE_IOERR_CHECKRESERVEDLOCK: 3594; + export const SQLITE_IOERR_CLOSE: 4106; + export const SQLITE_IOERR_DATA: 8202; + export const SQLITE_IOERR_DELETE: 2570; + export const SQLITE_IOERR_DELETE_NOENT: 5898; + export const SQLITE_IOERR_DIR_FSYNC: 1290; + export const SQLITE_IOERR_FSTAT: 1802; + export const SQLITE_IOERR_FSYNC: 1034; + export const SQLITE_IOERR_GETTEMPPATH: 6410; + export const SQLITE_IOERR_LOCK: 3850; + export const SQLITE_IOERR_NOMEM: 3082; + export const SQLITE_IOERR_READ: 266; + export const SQLITE_IOERR_RDLOCK: 2314; + export const SQLITE_IOERR_SEEK: 5642; + export const SQLITE_IOERR_SHORT_READ: 522; + export const SQLITE_IOERR_TRUNCATE: 1546; + export const SQLITE_IOERR_UNLOCK: 2058; + export const SQLITE_IOERR_VNODE: 6922; + export const SQLITE_IOERR_WRITE: 778; + export const SQLITE_IOERR_BEGIN_ATOMIC: 7434; + export const SQLITE_IOERR_COMMIT_ATOMIC: 7690; + export const SQLITE_IOERR_ROLLBACK_ATOMIC: 7946; + export const SQLITE_CONSTRAINT_CHECK: 275; + export const SQLITE_CONSTRAINT_COMMITHOOK: 531; + export const SQLITE_CONSTRAINT_FOREIGNKEY: 787; + export const SQLITE_CONSTRAINT_FUNCTION: 1043; + export const SQLITE_CONSTRAINT_NOTNULL: 1299; + export const SQLITE_CONSTRAINT_PINNED: 2835; + export const SQLITE_CONSTRAINT_PRIMARYKEY: 1555; + export const SQLITE_CONSTRAINT_ROWID: 2579; + export const SQLITE_CONSTRAINT_TRIGGER: 1811; + export const SQLITE_CONSTRAINT_UNIQUE: 2067; + export const SQLITE_CONSTRAINT_VTAB: 2323; + export const SQLITE_OPEN_READONLY: 1; + export const SQLITE_OPEN_READWRITE: 2; + export const SQLITE_OPEN_CREATE: 4; + export const SQLITE_OPEN_DELETEONCLOSE: 8; + export const SQLITE_OPEN_EXCLUSIVE: 16; + export const SQLITE_OPEN_AUTOPROXY: 32; + export const SQLITE_OPEN_URI: 64; + export const SQLITE_OPEN_MEMORY: 128; + export const SQLITE_OPEN_MAIN_DB: 256; + export const SQLITE_OPEN_TEMP_DB: 512; + export const SQLITE_OPEN_TRANSIENT_DB: 1024; + export const SQLITE_OPEN_MAIN_JOURNAL: 2048; + export const SQLITE_OPEN_TEMP_JOURNAL: 4096; + export const SQLITE_OPEN_SUBJOURNAL: 8192; + export const SQLITE_OPEN_SUPER_JOURNAL: 16384; + export const SQLITE_OPEN_NOMUTEX: 32768; + export const SQLITE_OPEN_FULLMUTEX: 65536; + export const SQLITE_OPEN_SHAREDCACHE: 131072; + export const SQLITE_OPEN_PRIVATECACHE: 262144; + export const SQLITE_OPEN_WAL: 524288; + export const SQLITE_OPEN_NOFOLLOW: 16777216; + export const SQLITE_LOCK_NONE: 0; + export const SQLITE_LOCK_SHARED: 1; + export const SQLITE_LOCK_RESERVED: 2; + export const SQLITE_LOCK_PENDING: 3; + export const SQLITE_LOCK_EXCLUSIVE: 4; + export const SQLITE_IOCAP_ATOMIC: 1; + export const SQLITE_IOCAP_ATOMIC512: 2; + export const SQLITE_IOCAP_ATOMIC1K: 4; + export const SQLITE_IOCAP_ATOMIC2K: 8; + export const SQLITE_IOCAP_ATOMIC4K: 16; + export const SQLITE_IOCAP_ATOMIC8K: 32; + export const SQLITE_IOCAP_ATOMIC16K: 64; + export const SQLITE_IOCAP_ATOMIC32K: 128; + export const SQLITE_IOCAP_ATOMIC64K: 256; + export const SQLITE_IOCAP_SAFE_APPEND: 512; + export const SQLITE_IOCAP_SEQUENTIAL: 1024; + export const SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN: 2048; + export const SQLITE_IOCAP_POWERSAFE_OVERWRITE: 4096; + export const SQLITE_IOCAP_IMMUTABLE: 8192; + export const SQLITE_IOCAP_BATCH_ATOMIC: 16384; + export const SQLITE_ACCESS_EXISTS: 0; + export const SQLITE_ACCESS_READWRITE: 1; + export const SQLITE_ACCESS_READ: 2; + export const SQLITE_FCNTL_LOCKSTATE: 1; + export const SQLITE_FCNTL_GET_LOCKPROXYFILE: 2; + export const SQLITE_FCNTL_SET_LOCKPROXYFILE: 3; + export const SQLITE_FCNTL_LAST_ERRNO: 4; + export const SQLITE_FCNTL_SIZE_HINT: 5; + export const SQLITE_FCNTL_CHUNK_SIZE: 6; + export const SQLITE_FCNTL_FILE_POINTER: 7; + export const SQLITE_FCNTL_SYNC_OMITTED: 8; + export const SQLITE_FCNTL_WIN32_AV_RETRY: 9; + export const SQLITE_FCNTL_PERSIST_WAL: 10; + export const SQLITE_FCNTL_OVERWRITE: 11; + export const SQLITE_FCNTL_VFSNAME: 12; + export const SQLITE_FCNTL_POWERSAFE_OVERWRITE: 13; + export const SQLITE_FCNTL_PRAGMA: 14; + export const SQLITE_FCNTL_BUSYHANDLER: 15; + export const SQLITE_FCNTL_TEMPFILENAME: 16; + export const SQLITE_FCNTL_MMAP_SIZE: 18; + export const SQLITE_FCNTL_TRACE: 19; + export const SQLITE_FCNTL_HAS_MOVED: 20; + export const SQLITE_FCNTL_SYNC: 21; + export const SQLITE_FCNTL_COMMIT_PHASETWO: 22; + export const SQLITE_FCNTL_WIN32_SET_HANDLE: 23; + export const SQLITE_FCNTL_WAL_BLOCK: 24; + export const SQLITE_FCNTL_ZIPVFS: 25; + export const SQLITE_FCNTL_RBU: 26; + export const SQLITE_FCNTL_VFS_POINTER: 27; + export const SQLITE_FCNTL_JOURNAL_POINTER: 28; + export const SQLITE_FCNTL_WIN32_GET_HANDLE: 29; + export const SQLITE_FCNTL_PDB: 30; + export const SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: 31; + export const SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: 32; + export const SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: 33; + export const SQLITE_FCNTL_LOCK_TIMEOUT: 34; + export const SQLITE_FCNTL_DATA_VERSION: 35; + export const SQLITE_FCNTL_SIZE_LIMIT: 36; + export const SQLITE_FCNTL_CKPT_DONE: 37; + export const SQLITE_FCNTL_RESERVE_BYTES: 38; + export const SQLITE_FCNTL_CKPT_START: 39; + export const SQLITE_INTEGER: 1; + export const SQLITE_FLOAT: 2; + export const SQLITE_TEXT: 3; + export const SQLITE_BLOB: 4; + export const SQLITE_NULL: 5; + export const SQLITE_STATIC: 0; + export const SQLITE_TRANSIENT: -1; + export const SQLITE_UTF8: 1; + export const SQLITE_UTF16LE: 2; + export const SQLITE_UTF16BE: 3; + export const SQLITE_UTF16: 4; + export const SQLITE_INDEX_CONSTRAINT_EQ: 2; + export const SQLITE_INDEX_CONSTRAINT_GT: 4; + export const SQLITE_INDEX_CONSTRAINT_LE: 8; + export const SQLITE_INDEX_CONSTRAINT_LT: 16; + export const SQLITE_INDEX_CONSTRAINT_GE: 32; + export const SQLITE_INDEX_CONSTRAINT_MATCH: 64; + export const SQLITE_INDEX_CONSTRAINT_LIKE: 65; + export const SQLITE_INDEX_CONSTRAINT_GLOB: 66; + export const SQLITE_INDEX_CONSTRAINT_REGEXP: 67; + export const SQLITE_INDEX_CONSTRAINT_NE: 68; + export const SQLITE_INDEX_CONSTRAINT_ISNOT: 69; + export const SQLITE_INDEX_CONSTRAINT_ISNOTNULL: 70; + export const SQLITE_INDEX_CONSTRAINT_ISNULL: 71; + export const SQLITE_INDEX_CONSTRAINT_IS: 72; + export const SQLITE_INDEX_CONSTRAINT_FUNCTION: 150; + export const SQLITE_INDEX_SCAN_UNIQUE: 1; + export const SQLITE_DETERMINISTIC: 0x000000800; + export const SQLITE_DIRECTONLY: 0x000080000; + export const SQLITE_SUBTYPE: 0x000100000; + export const SQLITE_INNOCUOUS: 0x000200000; + export const SQLITE_SYNC_NORMAL: 0x00002; + export const SQLITE_SYNC_FULL: 0x00003; + export const SQLITE_SYNC_DATAONLY: 0x00010; + export const SQLITE_CREATE_INDEX: 1; + export const SQLITE_CREATE_TABLE: 2; + export const SQLITE_CREATE_TEMP_INDEX: 3; + export const SQLITE_CREATE_TEMP_TABLE: 4; + export const SQLITE_CREATE_TEMP_TRIGGER: 5; + export const SQLITE_CREATE_TEMP_VIEW: 6; + export const SQLITE_CREATE_TRIGGER: 7; + export const SQLITE_CREATE_VIEW: 8; + export const SQLITE_DELETE: 9; + export const SQLITE_DROP_INDEX: 10; + export const SQLITE_DROP_TABLE: 11; + export const SQLITE_DROP_TEMP_INDEX: 12; + export const SQLITE_DROP_TEMP_TABLE: 13; + export const SQLITE_DROP_TEMP_TRIGGER: 14; + export const SQLITE_DROP_TEMP_VIEW: 15; + export const SQLITE_DROP_TRIGGER: 16; + export const SQLITE_DROP_VIEW: 17; + export const SQLITE_INSERT: 18; + export const SQLITE_PRAGMA: 19; + export const SQLITE_READ: 20; + export const SQLITE_SELECT: 21; + export const SQLITE_TRANSACTION: 22; + export const SQLITE_UPDATE: 23; + export const SQLITE_ATTACH: 24; + export const SQLITE_DETACH: 25; + export const SQLITE_ALTER_TABLE: 26; + export const SQLITE_REINDEX: 27; + export const SQLITE_ANALYZE: 28; + export const SQLITE_CREATE_VTABLE: 29; + export const SQLITE_DROP_VTABLE: 30; + export const SQLITE_FUNCTION: 31; + export const SQLITE_SAVEPOINT: 32; + export const SQLITE_COPY: 0; + export const SQLITE_RECURSIVE: 33; + export const SQLITE_DENY: 1; + export const SQLITE_IGNORE: 2; + export const SQLITE_LIMIT_LENGTH: 0; + export const SQLITE_LIMIT_SQL_LENGTH: 1; + export const SQLITE_LIMIT_COLUMN: 2; + export const SQLITE_LIMIT_EXPR_DEPTH: 3; + export const SQLITE_LIMIT_COMPOUND_SELECT: 4; + export const SQLITE_LIMIT_VDBE_OP: 5; + export const SQLITE_LIMIT_FUNCTION_ARG: 6; + export const SQLITE_LIMIT_ATTACHED: 7; + export const SQLITE_LIMIT_LIKE_PATTERN_LENGTH: 8; + export const SQLITE_LIMIT_VARIABLE_NUMBER: 9; + export const SQLITE_LIMIT_TRIGGER_DEPTH: 10; + export const SQLITE_LIMIT_WORKER_THREADS: 11; + export const SQLITE_PREPARE_PERSISTENT: 0x01; + export const SQLITE_PREPARE_NORMALIZED: 0x02; + export const SQLITE_PREPARE_NO_VTAB: 0x04; +} + +declare module 'wa-sqlite' { + export * from 'wa-sqlite/src/sqlite-constants.js'; + + /** + * @ignore + * Builds a Javascript API from the Emscripten module. This API is still + * low-level and closely corresponds to the C API exported by the module, + * but differs in some specifics like throwing exceptions on errors. + * @param {*} Module SQLite module + * @returns {SQLiteAPI} + */ + export function Factory(Module: any): SQLiteAPI; + + export class SQLiteError extends Error { + constructor(message: any, code: any); + code: any; + } +} + +/** @ignore */ +declare module 'wa-sqlite/dist/wa-sqlite.mjs' { + function ModuleFactory(config?: object): Promise; + export = ModuleFactory; +} + +/** @ignore */ +declare module 'wa-sqlite/dist/wa-sqlite-async.mjs' { + function ModuleFactory(config?: object): Promise; + export = ModuleFactory; +} + +/** @ignore */ +declare module 'wa-sqlite/src/VFS.js' { + export * from 'wa-sqlite/src/sqlite-constants.js'; + + export class Base { + mxPathName: number; + /** + * @param {number} fileId + * @returns {number|Promise} + */ + xClose(fileId: number): number; + /** + * @param {number} fileId + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number} + */ + xRead(fileId: number, pData: { + size: number; + value: Uint8Array; + }, iOffset: number): number; + /** + * @param {number} fileId + * @param {Uint8Array} pData + * @param {number} iOffset + * @returns {number} + */ + xWrite(fileId: number, pData: { + size: number; + value: Uint8Array; + }, iOffset: number): number; + /** + * @param {number} fileId + * @param {number} iSize + * @returns {number} + */ + xTruncate(fileId: number, iSize: number): number; + /** + * @param {number} fileId + * @param {*} flags + * @returns {number} + */ + xSync(fileId: number, flags: any): number; + /** + * @param {number} fileId + * @param {DataView} pSize64 + * @returns {number|Promise} + */ + xFileSize(fileId: number, pSize64: DataView): number; + /** + * @param {number} fileId + * @param {number} flags + * @returns {number} + */ + xLock(fileId: number, flags: number): number; + /** + * @param {number} fileId + * @param {number} flags + * @returns {number} + */ + xUnlock(fileId: number, flags: number): number; + /** + * @param {number} fileId + * @param {DataView} pResOut + * @returns {number} + */ + xCheckReservedLock(fileId: number, pResOut: DataView): number; + /** + * @param {number} fileId + * @param {number} flags + * @param {DataView} pArg + * @returns {number} + */ + xFileControl(fileId: number, flags: number, pArg: DataView): number; + /** + * @param {number} fileId + * @returns {number} + */ + xSectorSize(fileId: number): number; + /** + * @param {number} fileId + * @returns {number} + */ + xDeviceCharacteristics(fileId: number): number; + /** + * @param {string?} name + * @param {number} fileId + * @param {number} flags + * @param {DataView} pOutFlags + * @returns {number} + */ + xOpen(name: string | null, fileId: number, flags: number, pOutFlags: DataView): number; + /** + * + * @param {string} name + * @param {number} syncDir + * @returns {number} + */ + xDelete(name: string, syncDir: number): number; + /** + * @param {string} name + * @param {number} flags + * @param {DataView} pResOut + * @returns {number} + */ + xAccess(name: string, flags: number, pResOut: DataView): number; + /** + * Handle asynchronous operation. This implementation will be overriden on + * registration by an Asyncify build. + * @param {function(): Promise} f + * @returns {number} + */ + handleAsync(f: () => Promise): number; + } +} + +/** @ignore */ +declare module 'wa-sqlite/src/examples/IndexedDbVFS.js' { + import * as VFS from "wa-sqlite/src/VFS.js"; + export class IndexedDbVFS extends VFS.Base { + /** + * @param {string} idbName Name of IndexedDB database. + */ + constructor(idbName?: string); + name: string; + mapIdToFile: Map; + cacheSize: number; + db: any; + close(): Promise; + /** + * Delete a file from IndexedDB. + * @param {string} name + */ + deleteFile(name: string): Promise; + /** + * Forcibly clear an orphaned file lock. + * @param {string} name + */ + forceClearLock(name: string): Promise; + _getStore(mode?: string): any; + /** + * Returns the key for file metadata. + * @param {string} name + * @returns + */ + _metaKey(name: string): string; + /** + * Returns the key for file block data. + * @param {string} name + * @param {number} index + * @returns + */ + _blockKey(name: string, index: number): string; + _getBlock(store: any, file: any, index: any): Promise; + _putBlock(store: any, file: any, index: any, blockData: any): void; + _purgeCache(store: any, file: any, size?: number): void; + _flushCache(store: any, file: any): Promise; + _sync(file: any): Promise; + /** + * Helper function that deletes all keys greater or equal to `key` + * provided they start with `prefix`. + * @param {string} key + * @param {string} [prefix] + * @returns + */ + _delete(key: string, prefix?: string): Promise; + } +} + +/** @ignore */ +declare module 'wa-sqlite/src/examples/MemoryVFS.js' { + import * as VFS from "wa-sqlite/src/VFS.js"; + /** @ignore */ + export class MemoryVFS extends VFS.Base { + name: string; + mapNameToFile: Map; + mapIdToFile: Map; + } +} + +/** @ignore */ +declare module 'wa-sqlite/src/examples/MemoryAsyncVFS.js' { + import { MemoryVFS } from "wa-sqlite/src/examples/MemoryVFS.js"; + export class MemoryAsyncVFS extends MemoryVFS { + } +} + +/** @ignore */ +declare module 'wa-sqlite/src/examples/tag.js' { + /** + * @ignore + * Template tag builder. This function creates a tag with an API and + * database from the same module, then the tag can be used like this: + * ``` + * const sql = tag(sqlite3, db); + * const results = await sql` + * SELECT 1 + 1; + * SELECT 6 * 7; + * `; + * ``` + * The returned Promise value contains an array of results for each + * SQL statement that produces output. Each result is an object with + * properties `columns` (array of names) and `rows` (array of array + * of values). + * @param {SQLiteAPI} sqlite3 + * @param {number} db + * @returns {function(TemplateStringsArray, ...any): Promise} + */ + export function tag(sqlite3: any, db: number): (arg0: TemplateStringsArray, ...args: any[]) => Promise; +} diff --git a/web/core/local-db/worker/wa-sqlite/src/wa-sqlite.mjs b/web/core/local-db/worker/wa-sqlite/src/wa-sqlite.mjs new file mode 100644 index 00000000000..7ba5e77d463 --- /dev/null +++ b/web/core/local-db/worker/wa-sqlite/src/wa-sqlite.mjs @@ -0,0 +1,16 @@ + +var Module = (() => { + var _scriptName = import.meta.url; + + return ( +function(moduleArg = {}) { + var moduleRtn; + +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);function findWasmBinary(){if(Module["locateFile"]){var f="wa-sqlite.wasm";if(!isDataURI(f)){return locateFile(f)}return f}return new URL("wa-sqlite.wasm",import.meta.url).href}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["pa"];updateMemoryViews();wasmTable=wasmExports["hf"];addOnInit(wasmExports["qa"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":abort("to do getValue(i64) use WASM_BIGINT");case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=Module["noExitRuntime"]||true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":abort("to do setValue(i64) use WASM_BIGINT");case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ___assert_fail=(condition,filename,line,func)=>{abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])};var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else abort("initRandomDevice")};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};var zeroMemory=(address,size)=>{HEAPU8.fill(0,address,address+size);return address};var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(!ptr)return 0;return zeroMemory(ptr,size)};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir(node){var entries=[".",".."];for(var key of Object.keys(node.contents)){entries.push(key)}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var dep=!noRunDep?getUniqueRunDependency(`al ${url}`):"";readAsync(url).then(arrayBuffer=>{onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},err=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)};var FS_createDataFile=(parent,name,fileData,canRead,canWrite,canOwn)=>{FS.createDataFile(parent,name,fileData,canRead,canWrite,canOwn)};var preloadPlugins=Module["preloadPlugins"]||[];var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}onload?.();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,processData,onerror)}else{processData(url)}};var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:class{constructor(errno){this.name="ErrnoError";this.errno=errno}},genericErrors:{},filesystems:null,syncFSRequests:0,FSStream:class{constructor(){this.shared={}}get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev;this.readMode=292|73;this.writeMode=146}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;iFS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""});FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){var stat=func(path);HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAP32[buf+12>>2]=stat.uid;HEAP32[buf+16>>2]=stat.gid;HEAP32[buf+20>>2]=stat.rdev;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+24>>2]=tempI64[0],HEAP32[buf+28>>2]=tempI64[1];HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+56>>2]=tempI64[0],HEAP32[buf+60>>2]=tempI64[1];HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+72>>2]=tempI64[0],HEAP32[buf+76>>2]=tempI64[1];HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+88>>2]=tempI64[0],HEAP32[buf+92>>2]=tempI64[1];return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_chmod(path,mode){try{path=SYSCALLS.getStr(path);FS.chmod(path,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_faccessat(dirfd,path,amode,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fchmod(fd,mode){try{FS.fchmod(fd,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fchown32(fd,owner,group){try{FS.fchown(fd,owner,group);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function syscallGetVarargI(){var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret}var syscallGetVarargP=syscallGetVarargI;function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{var stream=SYSCALLS.getStreamFromFD(fd);return SYSCALLS.doStat(FS.stat,stream.path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function ___syscall_ftruncate64(fd,length_low,length_high){var length=convertI32PairToI53Checked(length_low,length_high);try{if(isNaN(length))return 61;FS.ftruncate(fd,length);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(sizeHEAPU32[ptr>>2]+HEAP32[ptr+4>>2]*4294967296;function ___syscall_utimensat(dirfd,path,times,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path,true);var now=Date.now(),atime,mtime;if(!times){atime=now;mtime=now}else{var seconds=readI53FromI64(times);var nanoseconds=HEAP32[times+8>>2];if(nanoseconds==1073741823){atime=now}else if(nanoseconds==1073741822){atime=-1}else{atime=seconds*1e3+nanoseconds/(1e3*1e3)}times+=16;seconds=readI53FromI64(times);nanoseconds=HEAP32[times+8>>2];if(nanoseconds==1073741823){mtime=now}else if(nanoseconds==1073741822){mtime=-1}else{mtime=seconds*1e3+nanoseconds/(1e3*1e3)}}if(mtime!=-1||atime!=-1){FS.utime(path,atime,mtime)}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>{abort("")};var __emscripten_runtime_keepalive_clear=()=>{noExitRuntime=false;runtimeKeepaliveCounter=0};var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};function __localtime_js(time_low,time_high,tmPtr){var time=convertI32PairToI53Checked(time_low,time_high);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}function __mmap_js(len,prot,flags,fd,offset_low,offset_high,allocated,addr){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);var res=FS.mmap(stream,len,offset,prot,flags);var ptr=res.ptr;HEAP32[allocated>>2]=res.allocated;HEAPU32[addr>>2]=ptr;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function __munmap_js(addr,len,prot,flags,fd,offset_low,offset_high){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset)}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var timers={};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var maybeExit=()=>{if(!keepRuntimeAlive()){try{_exit(EXITSTATUS)}catch(e){handleException(e)}}};var callUserCallback=func=>{if(ABORT){return}try{func();maybeExit()}catch(e){handleException(e)}};var _emscripten_get_now;_emscripten_get_now=()=>performance.now();var __setitimer_js=(which,timeout_ms)=>{if(timers[which]){clearTimeout(timers[which].id);delete timers[which]}if(!timeout_ms)return 0;var id=setTimeout(()=>{delete timers[which];callUserCallback(()=>__emscripten_timeout(which,_emscripten_get_now()))},timeout_ms);timers[which]={id,timeout_ms};return 0};var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetDate.now();var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var rightsBase=0;var rightsInheriting=0;var flags=0;{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4}HEAP8[pbuf]=type;HEAP16[pbuf+2>>1]=flags;tempI64=[rightsBase>>>0,(tempDouble=rightsBase,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[pbuf+8>>2]=tempI64[0],HEAP32[pbuf+12>>2]=tempI64[1];tempI64=[rightsInheriting>>>0,(tempDouble=rightsInheriting,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[pbuf+16>>2]=tempI64[0],HEAP32[pbuf+20>>2]=tempI64[1];return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_sync(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);if(stream.stream_ops?.fsync){return stream.stream_ops.fsync(stream)}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var adapters_support=function(){const handleAsync=typeof Asyncify==="object"?Asyncify.handleAsync.bind(Asyncify):null;Module["handleAsync"]=handleAsync;const targets=new Map;Module["setCallback"]=(key,target)=>targets.set(key,target);Module["getCallback"]=key=>targets.get(key);Module["deleteCallback"]=key=>targets.delete(key);adapters_support=function(isAsync,key,...args){const receiver=targets.get(key);let methodName=null;const f=typeof receiver==="function"?receiver:receiver[methodName=UTF8ToString(args.shift())];if(isAsync){if(handleAsync){return handleAsync(()=>f.apply(receiver,args))}throw new Error("Synchronous WebAssembly cannot call async function")}const result=f.apply(receiver,args);if(typeof result?.then=="function"){console.error("unexpected Promise",f);throw new Error(`${methodName} unexpectedly returned a Promise`)}return result}};function _ipp(...args){return adapters_support(false,...args)}function _ipp_async(...args){return adapters_support(true,...args)}function _ippipppp(...args){return adapters_support(false,...args)}function _ippipppp_async(...args){return adapters_support(true,...args)}function _ippp(...args){return adapters_support(false,...args)}function _ippp_async(...args){return adapters_support(true,...args)}function _ipppi(...args){return adapters_support(false,...args)}function _ipppi_async(...args){return adapters_support(true,...args)}function _ipppiii(...args){return adapters_support(false,...args)}function _ipppiii_async(...args){return adapters_support(true,...args)}function _ipppiiip(...args){return adapters_support(false,...args)}function _ipppiiip_async(...args){return adapters_support(true,...args)}function _ipppip(...args){return adapters_support(false,...args)}function _ipppip_async(...args){return adapters_support(true,...args)}function _ipppj(...args){return adapters_support(false,...args)}function _ipppj_async(...args){return adapters_support(true,...args)}function _ipppp(...args){return adapters_support(false,...args)}function _ipppp_async(...args){return adapters_support(true,...args)}function _ippppi(...args){return adapters_support(false,...args)}function _ippppi_async(...args){return adapters_support(true,...args)}function _ippppij(...args){return adapters_support(false,...args)}function _ippppij_async(...args){return adapters_support(true,...args)}function _ippppip(...args){return adapters_support(false,...args)}function _ippppip_async(...args){return adapters_support(true,...args)}function _ipppppip(...args){return adapters_support(false,...args)}function _ipppppip_async(...args){return adapters_support(true,...args)}function _vppippii(...args){return adapters_support(false,...args)}function _vppippii_async(...args){return adapters_support(true,...args)}function _vppp(...args){return adapters_support(false,...args)}function _vppp_async(...args){return adapters_support(true,...args)}function _vpppip(...args){return adapters_support(false,...args)}function _vpppip_async(...args){return adapters_support(true,...args)}var uleb128Encode=(n,target)=>{if(n<128){target.push(n)}else{target.push(n%128|128,n>>7)}};var sigToWasmTypes=sig=>{var typeNames={i:"i32",j:"i64",f:"f32",d:"f64",e:"externref",p:"i32"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i{var sigRet=sig.slice(0,1);var sigParam=sig.slice(1);var typeCodes={i:127,p:127,j:126,f:125,d:124,e:111};target.push(96);uleb128Encode(sigParam.length,target);for(var i=0;i{if(typeof WebAssembly.Function=="function"){return new WebAssembly.Function(sigToWasmTypes(sig),func)}var typeSectionBody=[1];generateFuncType(sig,typeSectionBody);var bytes=[0,97,115,109,1,0,0,0,1];uleb128Encode(typeSectionBody.length,bytes);bytes.push(...typeSectionBody);bytes.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);var module=new WebAssembly.Module(new Uint8Array(bytes));var instance=new WebAssembly.Instance(module,{e:{f:func}});var wrappedFunc=instance.exports["f"];return wrappedFunc};var wasmTable;var getWasmTableEntry=funcPtr=>wasmTable.get(funcPtr);var updateTableMap=(offset,count)=>{if(functionsInTableMap){for(var i=offset;i{if(!functionsInTableMap){functionsInTableMap=new WeakMap;updateTableMap(0,wasmTable.length)}return functionsInTableMap.get(func)||0};var freeTableIndexes=[];var getEmptyTableSlot=()=>{if(freeTableIndexes.length){return freeTableIndexes.pop()}try{wasmTable.grow(1)}catch(err){if(!(err instanceof RangeError)){throw err}throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."}return wasmTable.length-1};var setWasmTableEntry=(idx,func)=>wasmTable.set(idx,func);var addFunction=(func,sig)=>{var rtn=getFunctionAddress(func);if(rtn){return rtn}var ret=getEmptyTableSlot();try{setWasmTableEntry(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);setWasmTableEntry(ret,wrapped)}functionsInTableMap.set(func,ret);return ret};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var getTempRet0=val=>__emscripten_tempret_get();var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead)=>{var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder)return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr));var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str};var UTF32ToString=(ptr,maxBytesToRead)=>{var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str};function intArrayToString(array){var ret=[];for(var i=0;i255){chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();adapters_support();var wasmImports={a:___assert_fail,aa:___syscall_chmod,da:___syscall_faccessat,ba:___syscall_fchmod,$:___syscall_fchown32,b:___syscall_fcntl64,_:___syscall_fstat64,y:___syscall_ftruncate64,U:___syscall_getcwd,Y:___syscall_lstat64,R:___syscall_mkdirat,W:___syscall_newfstatat,P:___syscall_openat,N:___syscall_readlinkat,M:___syscall_rmdir,Z:___syscall_stat64,K:___syscall_unlinkat,J:___syscall_utimensat,F:__abort_js,E:__emscripten_runtime_keepalive_clear,w:__localtime_js,u:__mmap_js,v:__munmap_js,G:__setitimer_js,Q:__tzset_js,n:_emscripten_date_now,g:_emscripten_get_now,H:_emscripten_resize_heap,S:_environ_get,T:_environ_sizes_get,o:_fd_close,I:_fd_fdstat_get,O:_fd_read,x:_fd_seek,V:_fd_sync,L:_fd_write,na:_ipp,r:_ipp_async,ka:_ippipppp,oa:_ippipppp_async,j:_ippp,k:_ippp_async,c:_ipppi,d:_ipppi_async,ga:_ipppiii,ha:_ipppiii_async,ia:_ipppiiip,ja:_ipppiiip_async,h:_ipppip,i:_ipppip_async,z:_ipppj,A:_ipppj_async,e:_ipppp,f:_ipppp_async,ea:_ippppi,fa:_ippppi_async,B:_ippppij,C:_ippppij_async,p:_ippppip,q:_ippppip_async,la:_ipppppip,ma:_ipppppip_async,D:_proc_exit,s:_vppippii,t:_vppippii_async,l:_vppp,m:_vppp_async,X:_vpppip,ca:_vpppip_async};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["qa"])();var _sqlite3_status64=Module["_sqlite3_status64"]=(a0,a1,a2,a3)=>(_sqlite3_status64=Module["_sqlite3_status64"]=wasmExports["ra"])(a0,a1,a2,a3);var _sqlite3_status=Module["_sqlite3_status"]=(a0,a1,a2,a3)=>(_sqlite3_status=Module["_sqlite3_status"]=wasmExports["sa"])(a0,a1,a2,a3);var _sqlite3_db_status=Module["_sqlite3_db_status"]=(a0,a1,a2,a3,a4)=>(_sqlite3_db_status=Module["_sqlite3_db_status"]=wasmExports["ta"])(a0,a1,a2,a3,a4);var _sqlite3_msize=Module["_sqlite3_msize"]=a0=>(_sqlite3_msize=Module["_sqlite3_msize"]=wasmExports["ua"])(a0);var _sqlite3_vfs_find=Module["_sqlite3_vfs_find"]=a0=>(_sqlite3_vfs_find=Module["_sqlite3_vfs_find"]=wasmExports["va"])(a0);var _sqlite3_vfs_register=Module["_sqlite3_vfs_register"]=(a0,a1)=>(_sqlite3_vfs_register=Module["_sqlite3_vfs_register"]=wasmExports["wa"])(a0,a1);var _sqlite3_vfs_unregister=Module["_sqlite3_vfs_unregister"]=a0=>(_sqlite3_vfs_unregister=Module["_sqlite3_vfs_unregister"]=wasmExports["xa"])(a0);var _sqlite3_release_memory=Module["_sqlite3_release_memory"]=a0=>(_sqlite3_release_memory=Module["_sqlite3_release_memory"]=wasmExports["ya"])(a0);var _sqlite3_soft_heap_limit64=Module["_sqlite3_soft_heap_limit64"]=(a0,a1)=>(_sqlite3_soft_heap_limit64=Module["_sqlite3_soft_heap_limit64"]=wasmExports["za"])(a0,a1);var _sqlite3_memory_used=Module["_sqlite3_memory_used"]=()=>(_sqlite3_memory_used=Module["_sqlite3_memory_used"]=wasmExports["Aa"])();var _sqlite3_hard_heap_limit64=Module["_sqlite3_hard_heap_limit64"]=(a0,a1)=>(_sqlite3_hard_heap_limit64=Module["_sqlite3_hard_heap_limit64"]=wasmExports["Ba"])(a0,a1);var _sqlite3_memory_highwater=Module["_sqlite3_memory_highwater"]=a0=>(_sqlite3_memory_highwater=Module["_sqlite3_memory_highwater"]=wasmExports["Ca"])(a0);var _sqlite3_malloc=Module["_sqlite3_malloc"]=a0=>(_sqlite3_malloc=Module["_sqlite3_malloc"]=wasmExports["Da"])(a0);var _sqlite3_malloc64=Module["_sqlite3_malloc64"]=(a0,a1)=>(_sqlite3_malloc64=Module["_sqlite3_malloc64"]=wasmExports["Ea"])(a0,a1);var _sqlite3_free=Module["_sqlite3_free"]=a0=>(_sqlite3_free=Module["_sqlite3_free"]=wasmExports["Fa"])(a0);var _sqlite3_realloc=Module["_sqlite3_realloc"]=(a0,a1)=>(_sqlite3_realloc=Module["_sqlite3_realloc"]=wasmExports["Ga"])(a0,a1);var _sqlite3_realloc64=Module["_sqlite3_realloc64"]=(a0,a1,a2)=>(_sqlite3_realloc64=Module["_sqlite3_realloc64"]=wasmExports["Ha"])(a0,a1,a2);var _sqlite3_str_vappendf=Module["_sqlite3_str_vappendf"]=(a0,a1,a2)=>(_sqlite3_str_vappendf=Module["_sqlite3_str_vappendf"]=wasmExports["Ia"])(a0,a1,a2);var _sqlite3_str_append=Module["_sqlite3_str_append"]=(a0,a1,a2)=>(_sqlite3_str_append=Module["_sqlite3_str_append"]=wasmExports["Ja"])(a0,a1,a2);var _sqlite3_str_appendchar=Module["_sqlite3_str_appendchar"]=(a0,a1,a2)=>(_sqlite3_str_appendchar=Module["_sqlite3_str_appendchar"]=wasmExports["Ka"])(a0,a1,a2);var _sqlite3_str_appendall=Module["_sqlite3_str_appendall"]=(a0,a1)=>(_sqlite3_str_appendall=Module["_sqlite3_str_appendall"]=wasmExports["La"])(a0,a1);var _sqlite3_str_appendf=Module["_sqlite3_str_appendf"]=(a0,a1,a2)=>(_sqlite3_str_appendf=Module["_sqlite3_str_appendf"]=wasmExports["Ma"])(a0,a1,a2);var _sqlite3_str_finish=Module["_sqlite3_str_finish"]=a0=>(_sqlite3_str_finish=Module["_sqlite3_str_finish"]=wasmExports["Na"])(a0);var _sqlite3_str_errcode=Module["_sqlite3_str_errcode"]=a0=>(_sqlite3_str_errcode=Module["_sqlite3_str_errcode"]=wasmExports["Oa"])(a0);var _sqlite3_str_length=Module["_sqlite3_str_length"]=a0=>(_sqlite3_str_length=Module["_sqlite3_str_length"]=wasmExports["Pa"])(a0);var _sqlite3_str_value=Module["_sqlite3_str_value"]=a0=>(_sqlite3_str_value=Module["_sqlite3_str_value"]=wasmExports["Qa"])(a0);var _sqlite3_str_reset=Module["_sqlite3_str_reset"]=a0=>(_sqlite3_str_reset=Module["_sqlite3_str_reset"]=wasmExports["Ra"])(a0);var _sqlite3_str_new=Module["_sqlite3_str_new"]=a0=>(_sqlite3_str_new=Module["_sqlite3_str_new"]=wasmExports["Sa"])(a0);var _sqlite3_vmprintf=Module["_sqlite3_vmprintf"]=(a0,a1)=>(_sqlite3_vmprintf=Module["_sqlite3_vmprintf"]=wasmExports["Ta"])(a0,a1);var _sqlite3_mprintf=Module["_sqlite3_mprintf"]=(a0,a1)=>(_sqlite3_mprintf=Module["_sqlite3_mprintf"]=wasmExports["Ua"])(a0,a1);var _sqlite3_vsnprintf=Module["_sqlite3_vsnprintf"]=(a0,a1,a2,a3)=>(_sqlite3_vsnprintf=Module["_sqlite3_vsnprintf"]=wasmExports["Va"])(a0,a1,a2,a3);var _sqlite3_snprintf=Module["_sqlite3_snprintf"]=(a0,a1,a2,a3)=>(_sqlite3_snprintf=Module["_sqlite3_snprintf"]=wasmExports["Wa"])(a0,a1,a2,a3);var _sqlite3_log=Module["_sqlite3_log"]=(a0,a1,a2)=>(_sqlite3_log=Module["_sqlite3_log"]=wasmExports["Xa"])(a0,a1,a2);var _sqlite3_randomness=Module["_sqlite3_randomness"]=(a0,a1)=>(_sqlite3_randomness=Module["_sqlite3_randomness"]=wasmExports["Ya"])(a0,a1);var _sqlite3_stricmp=Module["_sqlite3_stricmp"]=(a0,a1)=>(_sqlite3_stricmp=Module["_sqlite3_stricmp"]=wasmExports["Za"])(a0,a1);var _sqlite3_strnicmp=Module["_sqlite3_strnicmp"]=(a0,a1,a2)=>(_sqlite3_strnicmp=Module["_sqlite3_strnicmp"]=wasmExports["_a"])(a0,a1,a2);var _sqlite3_os_init=Module["_sqlite3_os_init"]=()=>(_sqlite3_os_init=Module["_sqlite3_os_init"]=wasmExports["$a"])();var _sqlite3_os_end=Module["_sqlite3_os_end"]=()=>(_sqlite3_os_end=Module["_sqlite3_os_end"]=wasmExports["ab"])();var _sqlite3_serialize=Module["_sqlite3_serialize"]=(a0,a1,a2,a3)=>(_sqlite3_serialize=Module["_sqlite3_serialize"]=wasmExports["bb"])(a0,a1,a2,a3);var _sqlite3_prepare_v2=Module["_sqlite3_prepare_v2"]=(a0,a1,a2,a3,a4)=>(_sqlite3_prepare_v2=Module["_sqlite3_prepare_v2"]=wasmExports["cb"])(a0,a1,a2,a3,a4);var _sqlite3_step=Module["_sqlite3_step"]=a0=>(_sqlite3_step=Module["_sqlite3_step"]=wasmExports["db"])(a0);var _sqlite3_column_int64=Module["_sqlite3_column_int64"]=(a0,a1)=>(_sqlite3_column_int64=Module["_sqlite3_column_int64"]=wasmExports["eb"])(a0,a1);var _sqlite3_reset=Module["_sqlite3_reset"]=a0=>(_sqlite3_reset=Module["_sqlite3_reset"]=wasmExports["fb"])(a0);var _sqlite3_exec=Module["_sqlite3_exec"]=(a0,a1,a2,a3,a4)=>(_sqlite3_exec=Module["_sqlite3_exec"]=wasmExports["gb"])(a0,a1,a2,a3,a4);var _sqlite3_column_int=Module["_sqlite3_column_int"]=(a0,a1)=>(_sqlite3_column_int=Module["_sqlite3_column_int"]=wasmExports["hb"])(a0,a1);var _sqlite3_finalize=Module["_sqlite3_finalize"]=a0=>(_sqlite3_finalize=Module["_sqlite3_finalize"]=wasmExports["ib"])(a0);var _sqlite3_deserialize=Module["_sqlite3_deserialize"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(_sqlite3_deserialize=Module["_sqlite3_deserialize"]=wasmExports["jb"])(a0,a1,a2,a3,a4,a5,a6,a7);var _sqlite3_database_file_object=Module["_sqlite3_database_file_object"]=a0=>(_sqlite3_database_file_object=Module["_sqlite3_database_file_object"]=wasmExports["kb"])(a0);var _sqlite3_backup_init=Module["_sqlite3_backup_init"]=(a0,a1,a2,a3)=>(_sqlite3_backup_init=Module["_sqlite3_backup_init"]=wasmExports["lb"])(a0,a1,a2,a3);var _sqlite3_backup_step=Module["_sqlite3_backup_step"]=(a0,a1)=>(_sqlite3_backup_step=Module["_sqlite3_backup_step"]=wasmExports["mb"])(a0,a1);var _sqlite3_backup_finish=Module["_sqlite3_backup_finish"]=a0=>(_sqlite3_backup_finish=Module["_sqlite3_backup_finish"]=wasmExports["nb"])(a0);var _sqlite3_backup_remaining=Module["_sqlite3_backup_remaining"]=a0=>(_sqlite3_backup_remaining=Module["_sqlite3_backup_remaining"]=wasmExports["ob"])(a0);var _sqlite3_backup_pagecount=Module["_sqlite3_backup_pagecount"]=a0=>(_sqlite3_backup_pagecount=Module["_sqlite3_backup_pagecount"]=wasmExports["pb"])(a0);var _sqlite3_clear_bindings=Module["_sqlite3_clear_bindings"]=a0=>(_sqlite3_clear_bindings=Module["_sqlite3_clear_bindings"]=wasmExports["qb"])(a0);var _sqlite3_value_blob=Module["_sqlite3_value_blob"]=a0=>(_sqlite3_value_blob=Module["_sqlite3_value_blob"]=wasmExports["rb"])(a0);var _sqlite3_value_text=Module["_sqlite3_value_text"]=a0=>(_sqlite3_value_text=Module["_sqlite3_value_text"]=wasmExports["sb"])(a0);var _sqlite3_value_bytes=Module["_sqlite3_value_bytes"]=a0=>(_sqlite3_value_bytes=Module["_sqlite3_value_bytes"]=wasmExports["tb"])(a0);var _sqlite3_value_bytes16=Module["_sqlite3_value_bytes16"]=a0=>(_sqlite3_value_bytes16=Module["_sqlite3_value_bytes16"]=wasmExports["ub"])(a0);var _sqlite3_value_double=Module["_sqlite3_value_double"]=a0=>(_sqlite3_value_double=Module["_sqlite3_value_double"]=wasmExports["vb"])(a0);var _sqlite3_value_int=Module["_sqlite3_value_int"]=a0=>(_sqlite3_value_int=Module["_sqlite3_value_int"]=wasmExports["wb"])(a0);var _sqlite3_value_int64=Module["_sqlite3_value_int64"]=a0=>(_sqlite3_value_int64=Module["_sqlite3_value_int64"]=wasmExports["xb"])(a0);var _sqlite3_value_subtype=Module["_sqlite3_value_subtype"]=a0=>(_sqlite3_value_subtype=Module["_sqlite3_value_subtype"]=wasmExports["yb"])(a0);var _sqlite3_value_pointer=Module["_sqlite3_value_pointer"]=(a0,a1)=>(_sqlite3_value_pointer=Module["_sqlite3_value_pointer"]=wasmExports["zb"])(a0,a1);var _sqlite3_value_text16=Module["_sqlite3_value_text16"]=a0=>(_sqlite3_value_text16=Module["_sqlite3_value_text16"]=wasmExports["Ab"])(a0);var _sqlite3_value_text16be=Module["_sqlite3_value_text16be"]=a0=>(_sqlite3_value_text16be=Module["_sqlite3_value_text16be"]=wasmExports["Bb"])(a0);var _sqlite3_value_text16le=Module["_sqlite3_value_text16le"]=a0=>(_sqlite3_value_text16le=Module["_sqlite3_value_text16le"]=wasmExports["Cb"])(a0);var _sqlite3_value_type=Module["_sqlite3_value_type"]=a0=>(_sqlite3_value_type=Module["_sqlite3_value_type"]=wasmExports["Db"])(a0);var _sqlite3_value_encoding=Module["_sqlite3_value_encoding"]=a0=>(_sqlite3_value_encoding=Module["_sqlite3_value_encoding"]=wasmExports["Eb"])(a0);var _sqlite3_value_nochange=Module["_sqlite3_value_nochange"]=a0=>(_sqlite3_value_nochange=Module["_sqlite3_value_nochange"]=wasmExports["Fb"])(a0);var _sqlite3_value_frombind=Module["_sqlite3_value_frombind"]=a0=>(_sqlite3_value_frombind=Module["_sqlite3_value_frombind"]=wasmExports["Gb"])(a0);var _sqlite3_value_dup=Module["_sqlite3_value_dup"]=a0=>(_sqlite3_value_dup=Module["_sqlite3_value_dup"]=wasmExports["Hb"])(a0);var _sqlite3_value_free=Module["_sqlite3_value_free"]=a0=>(_sqlite3_value_free=Module["_sqlite3_value_free"]=wasmExports["Ib"])(a0);var _sqlite3_result_blob=Module["_sqlite3_result_blob"]=(a0,a1,a2,a3)=>(_sqlite3_result_blob=Module["_sqlite3_result_blob"]=wasmExports["Jb"])(a0,a1,a2,a3);var _sqlite3_result_blob64=Module["_sqlite3_result_blob64"]=(a0,a1,a2,a3,a4)=>(_sqlite3_result_blob64=Module["_sqlite3_result_blob64"]=wasmExports["Kb"])(a0,a1,a2,a3,a4);var _sqlite3_result_double=Module["_sqlite3_result_double"]=(a0,a1)=>(_sqlite3_result_double=Module["_sqlite3_result_double"]=wasmExports["Lb"])(a0,a1);var _sqlite3_result_error=Module["_sqlite3_result_error"]=(a0,a1,a2)=>(_sqlite3_result_error=Module["_sqlite3_result_error"]=wasmExports["Mb"])(a0,a1,a2);var _sqlite3_result_error16=Module["_sqlite3_result_error16"]=(a0,a1,a2)=>(_sqlite3_result_error16=Module["_sqlite3_result_error16"]=wasmExports["Nb"])(a0,a1,a2);var _sqlite3_result_int=Module["_sqlite3_result_int"]=(a0,a1)=>(_sqlite3_result_int=Module["_sqlite3_result_int"]=wasmExports["Ob"])(a0,a1);var _sqlite3_result_int64=Module["_sqlite3_result_int64"]=(a0,a1,a2)=>(_sqlite3_result_int64=Module["_sqlite3_result_int64"]=wasmExports["Pb"])(a0,a1,a2);var _sqlite3_result_null=Module["_sqlite3_result_null"]=a0=>(_sqlite3_result_null=Module["_sqlite3_result_null"]=wasmExports["Qb"])(a0);var _sqlite3_result_pointer=Module["_sqlite3_result_pointer"]=(a0,a1,a2,a3)=>(_sqlite3_result_pointer=Module["_sqlite3_result_pointer"]=wasmExports["Rb"])(a0,a1,a2,a3);var _sqlite3_result_subtype=Module["_sqlite3_result_subtype"]=(a0,a1)=>(_sqlite3_result_subtype=Module["_sqlite3_result_subtype"]=wasmExports["Sb"])(a0,a1);var _sqlite3_result_text=Module["_sqlite3_result_text"]=(a0,a1,a2,a3)=>(_sqlite3_result_text=Module["_sqlite3_result_text"]=wasmExports["Tb"])(a0,a1,a2,a3);var _sqlite3_result_text64=Module["_sqlite3_result_text64"]=(a0,a1,a2,a3,a4,a5)=>(_sqlite3_result_text64=Module["_sqlite3_result_text64"]=wasmExports["Ub"])(a0,a1,a2,a3,a4,a5);var _sqlite3_result_text16=Module["_sqlite3_result_text16"]=(a0,a1,a2,a3)=>(_sqlite3_result_text16=Module["_sqlite3_result_text16"]=wasmExports["Vb"])(a0,a1,a2,a3);var _sqlite3_result_text16be=Module["_sqlite3_result_text16be"]=(a0,a1,a2,a3)=>(_sqlite3_result_text16be=Module["_sqlite3_result_text16be"]=wasmExports["Wb"])(a0,a1,a2,a3);var _sqlite3_result_text16le=Module["_sqlite3_result_text16le"]=(a0,a1,a2,a3)=>(_sqlite3_result_text16le=Module["_sqlite3_result_text16le"]=wasmExports["Xb"])(a0,a1,a2,a3);var _sqlite3_result_value=Module["_sqlite3_result_value"]=(a0,a1)=>(_sqlite3_result_value=Module["_sqlite3_result_value"]=wasmExports["Yb"])(a0,a1);var _sqlite3_result_error_toobig=Module["_sqlite3_result_error_toobig"]=a0=>(_sqlite3_result_error_toobig=Module["_sqlite3_result_error_toobig"]=wasmExports["Zb"])(a0);var _sqlite3_result_zeroblob=Module["_sqlite3_result_zeroblob"]=(a0,a1)=>(_sqlite3_result_zeroblob=Module["_sqlite3_result_zeroblob"]=wasmExports["_b"])(a0,a1);var _sqlite3_result_zeroblob64=Module["_sqlite3_result_zeroblob64"]=(a0,a1,a2)=>(_sqlite3_result_zeroblob64=Module["_sqlite3_result_zeroblob64"]=wasmExports["$b"])(a0,a1,a2);var _sqlite3_result_error_code=Module["_sqlite3_result_error_code"]=(a0,a1)=>(_sqlite3_result_error_code=Module["_sqlite3_result_error_code"]=wasmExports["ac"])(a0,a1);var _sqlite3_result_error_nomem=Module["_sqlite3_result_error_nomem"]=a0=>(_sqlite3_result_error_nomem=Module["_sqlite3_result_error_nomem"]=wasmExports["bc"])(a0);var _sqlite3_user_data=Module["_sqlite3_user_data"]=a0=>(_sqlite3_user_data=Module["_sqlite3_user_data"]=wasmExports["cc"])(a0);var _sqlite3_context_db_handle=Module["_sqlite3_context_db_handle"]=a0=>(_sqlite3_context_db_handle=Module["_sqlite3_context_db_handle"]=wasmExports["dc"])(a0);var _sqlite3_vtab_nochange=Module["_sqlite3_vtab_nochange"]=a0=>(_sqlite3_vtab_nochange=Module["_sqlite3_vtab_nochange"]=wasmExports["ec"])(a0);var _sqlite3_vtab_in_first=Module["_sqlite3_vtab_in_first"]=(a0,a1)=>(_sqlite3_vtab_in_first=Module["_sqlite3_vtab_in_first"]=wasmExports["fc"])(a0,a1);var _sqlite3_vtab_in_next=Module["_sqlite3_vtab_in_next"]=(a0,a1)=>(_sqlite3_vtab_in_next=Module["_sqlite3_vtab_in_next"]=wasmExports["gc"])(a0,a1);var _sqlite3_aggregate_context=Module["_sqlite3_aggregate_context"]=(a0,a1)=>(_sqlite3_aggregate_context=Module["_sqlite3_aggregate_context"]=wasmExports["hc"])(a0,a1);var _sqlite3_get_auxdata=Module["_sqlite3_get_auxdata"]=(a0,a1)=>(_sqlite3_get_auxdata=Module["_sqlite3_get_auxdata"]=wasmExports["ic"])(a0,a1);var _sqlite3_set_auxdata=Module["_sqlite3_set_auxdata"]=(a0,a1,a2,a3)=>(_sqlite3_set_auxdata=Module["_sqlite3_set_auxdata"]=wasmExports["jc"])(a0,a1,a2,a3);var _sqlite3_column_count=Module["_sqlite3_column_count"]=a0=>(_sqlite3_column_count=Module["_sqlite3_column_count"]=wasmExports["kc"])(a0);var _sqlite3_data_count=Module["_sqlite3_data_count"]=a0=>(_sqlite3_data_count=Module["_sqlite3_data_count"]=wasmExports["lc"])(a0);var _sqlite3_column_blob=Module["_sqlite3_column_blob"]=(a0,a1)=>(_sqlite3_column_blob=Module["_sqlite3_column_blob"]=wasmExports["mc"])(a0,a1);var _sqlite3_column_bytes=Module["_sqlite3_column_bytes"]=(a0,a1)=>(_sqlite3_column_bytes=Module["_sqlite3_column_bytes"]=wasmExports["nc"])(a0,a1);var _sqlite3_column_bytes16=Module["_sqlite3_column_bytes16"]=(a0,a1)=>(_sqlite3_column_bytes16=Module["_sqlite3_column_bytes16"]=wasmExports["oc"])(a0,a1);var _sqlite3_column_double=Module["_sqlite3_column_double"]=(a0,a1)=>(_sqlite3_column_double=Module["_sqlite3_column_double"]=wasmExports["pc"])(a0,a1);var _sqlite3_column_text=Module["_sqlite3_column_text"]=(a0,a1)=>(_sqlite3_column_text=Module["_sqlite3_column_text"]=wasmExports["qc"])(a0,a1);var _sqlite3_column_value=Module["_sqlite3_column_value"]=(a0,a1)=>(_sqlite3_column_value=Module["_sqlite3_column_value"]=wasmExports["rc"])(a0,a1);var _sqlite3_column_text16=Module["_sqlite3_column_text16"]=(a0,a1)=>(_sqlite3_column_text16=Module["_sqlite3_column_text16"]=wasmExports["sc"])(a0,a1);var _sqlite3_column_type=Module["_sqlite3_column_type"]=(a0,a1)=>(_sqlite3_column_type=Module["_sqlite3_column_type"]=wasmExports["tc"])(a0,a1);var _sqlite3_column_name=Module["_sqlite3_column_name"]=(a0,a1)=>(_sqlite3_column_name=Module["_sqlite3_column_name"]=wasmExports["uc"])(a0,a1);var _sqlite3_column_name16=Module["_sqlite3_column_name16"]=(a0,a1)=>(_sqlite3_column_name16=Module["_sqlite3_column_name16"]=wasmExports["vc"])(a0,a1);var _sqlite3_bind_blob=Module["_sqlite3_bind_blob"]=(a0,a1,a2,a3,a4)=>(_sqlite3_bind_blob=Module["_sqlite3_bind_blob"]=wasmExports["wc"])(a0,a1,a2,a3,a4);var _sqlite3_bind_blob64=Module["_sqlite3_bind_blob64"]=(a0,a1,a2,a3,a4,a5)=>(_sqlite3_bind_blob64=Module["_sqlite3_bind_blob64"]=wasmExports["xc"])(a0,a1,a2,a3,a4,a5);var _sqlite3_bind_double=Module["_sqlite3_bind_double"]=(a0,a1,a2)=>(_sqlite3_bind_double=Module["_sqlite3_bind_double"]=wasmExports["yc"])(a0,a1,a2);var _sqlite3_bind_int=Module["_sqlite3_bind_int"]=(a0,a1,a2)=>(_sqlite3_bind_int=Module["_sqlite3_bind_int"]=wasmExports["zc"])(a0,a1,a2);var _sqlite3_bind_int64=Module["_sqlite3_bind_int64"]=(a0,a1,a2,a3)=>(_sqlite3_bind_int64=Module["_sqlite3_bind_int64"]=wasmExports["Ac"])(a0,a1,a2,a3);var _sqlite3_bind_null=Module["_sqlite3_bind_null"]=(a0,a1)=>(_sqlite3_bind_null=Module["_sqlite3_bind_null"]=wasmExports["Bc"])(a0,a1);var _sqlite3_bind_pointer=Module["_sqlite3_bind_pointer"]=(a0,a1,a2,a3,a4)=>(_sqlite3_bind_pointer=Module["_sqlite3_bind_pointer"]=wasmExports["Cc"])(a0,a1,a2,a3,a4);var _sqlite3_bind_text=Module["_sqlite3_bind_text"]=(a0,a1,a2,a3,a4)=>(_sqlite3_bind_text=Module["_sqlite3_bind_text"]=wasmExports["Dc"])(a0,a1,a2,a3,a4);var _sqlite3_bind_text64=Module["_sqlite3_bind_text64"]=(a0,a1,a2,a3,a4,a5,a6)=>(_sqlite3_bind_text64=Module["_sqlite3_bind_text64"]=wasmExports["Ec"])(a0,a1,a2,a3,a4,a5,a6);var _sqlite3_bind_text16=Module["_sqlite3_bind_text16"]=(a0,a1,a2,a3,a4)=>(_sqlite3_bind_text16=Module["_sqlite3_bind_text16"]=wasmExports["Fc"])(a0,a1,a2,a3,a4);var _sqlite3_bind_value=Module["_sqlite3_bind_value"]=(a0,a1,a2)=>(_sqlite3_bind_value=Module["_sqlite3_bind_value"]=wasmExports["Gc"])(a0,a1,a2);var _sqlite3_bind_zeroblob=Module["_sqlite3_bind_zeroblob"]=(a0,a1,a2)=>(_sqlite3_bind_zeroblob=Module["_sqlite3_bind_zeroblob"]=wasmExports["Hc"])(a0,a1,a2);var _sqlite3_bind_zeroblob64=Module["_sqlite3_bind_zeroblob64"]=(a0,a1,a2,a3)=>(_sqlite3_bind_zeroblob64=Module["_sqlite3_bind_zeroblob64"]=wasmExports["Ic"])(a0,a1,a2,a3);var _sqlite3_bind_parameter_count=Module["_sqlite3_bind_parameter_count"]=a0=>(_sqlite3_bind_parameter_count=Module["_sqlite3_bind_parameter_count"]=wasmExports["Jc"])(a0);var _sqlite3_bind_parameter_name=Module["_sqlite3_bind_parameter_name"]=(a0,a1)=>(_sqlite3_bind_parameter_name=Module["_sqlite3_bind_parameter_name"]=wasmExports["Kc"])(a0,a1);var _sqlite3_bind_parameter_index=Module["_sqlite3_bind_parameter_index"]=(a0,a1)=>(_sqlite3_bind_parameter_index=Module["_sqlite3_bind_parameter_index"]=wasmExports["Lc"])(a0,a1);var _sqlite3_db_handle=Module["_sqlite3_db_handle"]=a0=>(_sqlite3_db_handle=Module["_sqlite3_db_handle"]=wasmExports["Mc"])(a0);var _sqlite3_stmt_readonly=Module["_sqlite3_stmt_readonly"]=a0=>(_sqlite3_stmt_readonly=Module["_sqlite3_stmt_readonly"]=wasmExports["Nc"])(a0);var _sqlite3_stmt_isexplain=Module["_sqlite3_stmt_isexplain"]=a0=>(_sqlite3_stmt_isexplain=Module["_sqlite3_stmt_isexplain"]=wasmExports["Oc"])(a0);var _sqlite3_stmt_explain=Module["_sqlite3_stmt_explain"]=(a0,a1)=>(_sqlite3_stmt_explain=Module["_sqlite3_stmt_explain"]=wasmExports["Pc"])(a0,a1);var _sqlite3_stmt_busy=Module["_sqlite3_stmt_busy"]=a0=>(_sqlite3_stmt_busy=Module["_sqlite3_stmt_busy"]=wasmExports["Qc"])(a0);var _sqlite3_next_stmt=Module["_sqlite3_next_stmt"]=(a0,a1)=>(_sqlite3_next_stmt=Module["_sqlite3_next_stmt"]=wasmExports["Rc"])(a0,a1);var _sqlite3_stmt_status=Module["_sqlite3_stmt_status"]=(a0,a1,a2)=>(_sqlite3_stmt_status=Module["_sqlite3_stmt_status"]=wasmExports["Sc"])(a0,a1,a2);var _sqlite3_sql=Module["_sqlite3_sql"]=a0=>(_sqlite3_sql=Module["_sqlite3_sql"]=wasmExports["Tc"])(a0);var _sqlite3_expanded_sql=Module["_sqlite3_expanded_sql"]=a0=>(_sqlite3_expanded_sql=Module["_sqlite3_expanded_sql"]=wasmExports["Uc"])(a0);var _sqlite3_value_numeric_type=Module["_sqlite3_value_numeric_type"]=a0=>(_sqlite3_value_numeric_type=Module["_sqlite3_value_numeric_type"]=wasmExports["Vc"])(a0);var _sqlite3_blob_open=Module["_sqlite3_blob_open"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(_sqlite3_blob_open=Module["_sqlite3_blob_open"]=wasmExports["Wc"])(a0,a1,a2,a3,a4,a5,a6,a7);var _sqlite3_blob_close=Module["_sqlite3_blob_close"]=a0=>(_sqlite3_blob_close=Module["_sqlite3_blob_close"]=wasmExports["Xc"])(a0);var _sqlite3_blob_read=Module["_sqlite3_blob_read"]=(a0,a1,a2,a3)=>(_sqlite3_blob_read=Module["_sqlite3_blob_read"]=wasmExports["Yc"])(a0,a1,a2,a3);var _sqlite3_blob_write=Module["_sqlite3_blob_write"]=(a0,a1,a2,a3)=>(_sqlite3_blob_write=Module["_sqlite3_blob_write"]=wasmExports["Zc"])(a0,a1,a2,a3);var _sqlite3_blob_bytes=Module["_sqlite3_blob_bytes"]=a0=>(_sqlite3_blob_bytes=Module["_sqlite3_blob_bytes"]=wasmExports["_c"])(a0);var _sqlite3_blob_reopen=Module["_sqlite3_blob_reopen"]=(a0,a1,a2)=>(_sqlite3_blob_reopen=Module["_sqlite3_blob_reopen"]=wasmExports["$c"])(a0,a1,a2);var _sqlite3_set_authorizer=Module["_sqlite3_set_authorizer"]=(a0,a1,a2)=>(_sqlite3_set_authorizer=Module["_sqlite3_set_authorizer"]=wasmExports["ad"])(a0,a1,a2);var _sqlite3_strglob=Module["_sqlite3_strglob"]=(a0,a1)=>(_sqlite3_strglob=Module["_sqlite3_strglob"]=wasmExports["bd"])(a0,a1);var _sqlite3_strlike=Module["_sqlite3_strlike"]=(a0,a1,a2)=>(_sqlite3_strlike=Module["_sqlite3_strlike"]=wasmExports["cd"])(a0,a1,a2);var _sqlite3_errmsg=Module["_sqlite3_errmsg"]=a0=>(_sqlite3_errmsg=Module["_sqlite3_errmsg"]=wasmExports["dd"])(a0);var _sqlite3_auto_extension=Module["_sqlite3_auto_extension"]=a0=>(_sqlite3_auto_extension=Module["_sqlite3_auto_extension"]=wasmExports["ed"])(a0);var _sqlite3_cancel_auto_extension=Module["_sqlite3_cancel_auto_extension"]=a0=>(_sqlite3_cancel_auto_extension=Module["_sqlite3_cancel_auto_extension"]=wasmExports["fd"])(a0);var _sqlite3_reset_auto_extension=Module["_sqlite3_reset_auto_extension"]=()=>(_sqlite3_reset_auto_extension=Module["_sqlite3_reset_auto_extension"]=wasmExports["gd"])();var _sqlite3_prepare=Module["_sqlite3_prepare"]=(a0,a1,a2,a3,a4)=>(_sqlite3_prepare=Module["_sqlite3_prepare"]=wasmExports["hd"])(a0,a1,a2,a3,a4);var _sqlite3_prepare_v3=Module["_sqlite3_prepare_v3"]=(a0,a1,a2,a3,a4,a5)=>(_sqlite3_prepare_v3=Module["_sqlite3_prepare_v3"]=wasmExports["id"])(a0,a1,a2,a3,a4,a5);var _sqlite3_prepare16=Module["_sqlite3_prepare16"]=(a0,a1,a2,a3,a4)=>(_sqlite3_prepare16=Module["_sqlite3_prepare16"]=wasmExports["jd"])(a0,a1,a2,a3,a4);var _sqlite3_prepare16_v2=Module["_sqlite3_prepare16_v2"]=(a0,a1,a2,a3,a4)=>(_sqlite3_prepare16_v2=Module["_sqlite3_prepare16_v2"]=wasmExports["kd"])(a0,a1,a2,a3,a4);var _sqlite3_prepare16_v3=Module["_sqlite3_prepare16_v3"]=(a0,a1,a2,a3,a4,a5)=>(_sqlite3_prepare16_v3=Module["_sqlite3_prepare16_v3"]=wasmExports["ld"])(a0,a1,a2,a3,a4,a5);var _sqlite3_get_table=Module["_sqlite3_get_table"]=(a0,a1,a2,a3,a4,a5)=>(_sqlite3_get_table=Module["_sqlite3_get_table"]=wasmExports["md"])(a0,a1,a2,a3,a4,a5);var _sqlite3_free_table=Module["_sqlite3_free_table"]=a0=>(_sqlite3_free_table=Module["_sqlite3_free_table"]=wasmExports["nd"])(a0);var _sqlite3_create_module=Module["_sqlite3_create_module"]=(a0,a1,a2,a3)=>(_sqlite3_create_module=Module["_sqlite3_create_module"]=wasmExports["od"])(a0,a1,a2,a3);var _sqlite3_create_module_v2=Module["_sqlite3_create_module_v2"]=(a0,a1,a2,a3,a4)=>(_sqlite3_create_module_v2=Module["_sqlite3_create_module_v2"]=wasmExports["pd"])(a0,a1,a2,a3,a4);var _sqlite3_drop_modules=Module["_sqlite3_drop_modules"]=(a0,a1)=>(_sqlite3_drop_modules=Module["_sqlite3_drop_modules"]=wasmExports["qd"])(a0,a1);var _sqlite3_declare_vtab=Module["_sqlite3_declare_vtab"]=(a0,a1)=>(_sqlite3_declare_vtab=Module["_sqlite3_declare_vtab"]=wasmExports["rd"])(a0,a1);var _sqlite3_vtab_on_conflict=Module["_sqlite3_vtab_on_conflict"]=a0=>(_sqlite3_vtab_on_conflict=Module["_sqlite3_vtab_on_conflict"]=wasmExports["sd"])(a0);var _sqlite3_vtab_config=Module["_sqlite3_vtab_config"]=(a0,a1,a2)=>(_sqlite3_vtab_config=Module["_sqlite3_vtab_config"]=wasmExports["td"])(a0,a1,a2);var _sqlite3_vtab_collation=Module["_sqlite3_vtab_collation"]=(a0,a1)=>(_sqlite3_vtab_collation=Module["_sqlite3_vtab_collation"]=wasmExports["ud"])(a0,a1);var _sqlite3_vtab_in=Module["_sqlite3_vtab_in"]=(a0,a1,a2)=>(_sqlite3_vtab_in=Module["_sqlite3_vtab_in"]=wasmExports["vd"])(a0,a1,a2);var _sqlite3_vtab_rhs_value=Module["_sqlite3_vtab_rhs_value"]=(a0,a1,a2)=>(_sqlite3_vtab_rhs_value=Module["_sqlite3_vtab_rhs_value"]=wasmExports["wd"])(a0,a1,a2);var _sqlite3_vtab_distinct=Module["_sqlite3_vtab_distinct"]=a0=>(_sqlite3_vtab_distinct=Module["_sqlite3_vtab_distinct"]=wasmExports["xd"])(a0);var _sqlite3_keyword_name=Module["_sqlite3_keyword_name"]=(a0,a1,a2)=>(_sqlite3_keyword_name=Module["_sqlite3_keyword_name"]=wasmExports["yd"])(a0,a1,a2);var _sqlite3_keyword_count=Module["_sqlite3_keyword_count"]=()=>(_sqlite3_keyword_count=Module["_sqlite3_keyword_count"]=wasmExports["zd"])();var _sqlite3_keyword_check=Module["_sqlite3_keyword_check"]=(a0,a1)=>(_sqlite3_keyword_check=Module["_sqlite3_keyword_check"]=wasmExports["Ad"])(a0,a1);var _sqlite3_complete=Module["_sqlite3_complete"]=a0=>(_sqlite3_complete=Module["_sqlite3_complete"]=wasmExports["Bd"])(a0);var _sqlite3_complete16=Module["_sqlite3_complete16"]=a0=>(_sqlite3_complete16=Module["_sqlite3_complete16"]=wasmExports["Cd"])(a0);var _sqlite3_libversion=Module["_sqlite3_libversion"]=()=>(_sqlite3_libversion=Module["_sqlite3_libversion"]=wasmExports["Dd"])();var _sqlite3_libversion_number=Module["_sqlite3_libversion_number"]=()=>(_sqlite3_libversion_number=Module["_sqlite3_libversion_number"]=wasmExports["Ed"])();var _sqlite3_threadsafe=Module["_sqlite3_threadsafe"]=()=>(_sqlite3_threadsafe=Module["_sqlite3_threadsafe"]=wasmExports["Fd"])();var _sqlite3_initialize=Module["_sqlite3_initialize"]=()=>(_sqlite3_initialize=Module["_sqlite3_initialize"]=wasmExports["Gd"])();var _sqlite3_shutdown=Module["_sqlite3_shutdown"]=()=>(_sqlite3_shutdown=Module["_sqlite3_shutdown"]=wasmExports["Hd"])();var _sqlite3_config=Module["_sqlite3_config"]=(a0,a1)=>(_sqlite3_config=Module["_sqlite3_config"]=wasmExports["Id"])(a0,a1);var _sqlite3_db_mutex=Module["_sqlite3_db_mutex"]=a0=>(_sqlite3_db_mutex=Module["_sqlite3_db_mutex"]=wasmExports["Jd"])(a0);var _sqlite3_db_release_memory=Module["_sqlite3_db_release_memory"]=a0=>(_sqlite3_db_release_memory=Module["_sqlite3_db_release_memory"]=wasmExports["Kd"])(a0);var _sqlite3_db_cacheflush=Module["_sqlite3_db_cacheflush"]=a0=>(_sqlite3_db_cacheflush=Module["_sqlite3_db_cacheflush"]=wasmExports["Ld"])(a0);var _sqlite3_db_config=Module["_sqlite3_db_config"]=(a0,a1,a2)=>(_sqlite3_db_config=Module["_sqlite3_db_config"]=wasmExports["Md"])(a0,a1,a2);var _sqlite3_last_insert_rowid=Module["_sqlite3_last_insert_rowid"]=a0=>(_sqlite3_last_insert_rowid=Module["_sqlite3_last_insert_rowid"]=wasmExports["Nd"])(a0);var _sqlite3_set_last_insert_rowid=Module["_sqlite3_set_last_insert_rowid"]=(a0,a1,a2)=>(_sqlite3_set_last_insert_rowid=Module["_sqlite3_set_last_insert_rowid"]=wasmExports["Od"])(a0,a1,a2);var _sqlite3_changes64=Module["_sqlite3_changes64"]=a0=>(_sqlite3_changes64=Module["_sqlite3_changes64"]=wasmExports["Pd"])(a0);var _sqlite3_changes=Module["_sqlite3_changes"]=a0=>(_sqlite3_changes=Module["_sqlite3_changes"]=wasmExports["Qd"])(a0);var _sqlite3_total_changes64=Module["_sqlite3_total_changes64"]=a0=>(_sqlite3_total_changes64=Module["_sqlite3_total_changes64"]=wasmExports["Rd"])(a0);var _sqlite3_total_changes=Module["_sqlite3_total_changes"]=a0=>(_sqlite3_total_changes=Module["_sqlite3_total_changes"]=wasmExports["Sd"])(a0);var _sqlite3_txn_state=Module["_sqlite3_txn_state"]=(a0,a1)=>(_sqlite3_txn_state=Module["_sqlite3_txn_state"]=wasmExports["Td"])(a0,a1);var _sqlite3_close=Module["_sqlite3_close"]=a0=>(_sqlite3_close=Module["_sqlite3_close"]=wasmExports["Ud"])(a0);var _sqlite3_close_v2=Module["_sqlite3_close_v2"]=a0=>(_sqlite3_close_v2=Module["_sqlite3_close_v2"]=wasmExports["Vd"])(a0);var _sqlite3_busy_handler=Module["_sqlite3_busy_handler"]=(a0,a1,a2)=>(_sqlite3_busy_handler=Module["_sqlite3_busy_handler"]=wasmExports["Wd"])(a0,a1,a2);var _sqlite3_progress_handler=Module["_sqlite3_progress_handler"]=(a0,a1,a2,a3)=>(_sqlite3_progress_handler=Module["_sqlite3_progress_handler"]=wasmExports["Xd"])(a0,a1,a2,a3);var _sqlite3_busy_timeout=Module["_sqlite3_busy_timeout"]=(a0,a1)=>(_sqlite3_busy_timeout=Module["_sqlite3_busy_timeout"]=wasmExports["Yd"])(a0,a1);var _sqlite3_interrupt=Module["_sqlite3_interrupt"]=a0=>(_sqlite3_interrupt=Module["_sqlite3_interrupt"]=wasmExports["Zd"])(a0);var _sqlite3_is_interrupted=Module["_sqlite3_is_interrupted"]=a0=>(_sqlite3_is_interrupted=Module["_sqlite3_is_interrupted"]=wasmExports["_d"])(a0);var _sqlite3_create_function=Module["_sqlite3_create_function"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(_sqlite3_create_function=Module["_sqlite3_create_function"]=wasmExports["$d"])(a0,a1,a2,a3,a4,a5,a6,a7);var _sqlite3_create_function_v2=Module["_sqlite3_create_function_v2"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8)=>(_sqlite3_create_function_v2=Module["_sqlite3_create_function_v2"]=wasmExports["ae"])(a0,a1,a2,a3,a4,a5,a6,a7,a8);var _sqlite3_create_window_function=Module["_sqlite3_create_window_function"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)=>(_sqlite3_create_window_function=Module["_sqlite3_create_window_function"]=wasmExports["be"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9);var _sqlite3_create_function16=Module["_sqlite3_create_function16"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(_sqlite3_create_function16=Module["_sqlite3_create_function16"]=wasmExports["ce"])(a0,a1,a2,a3,a4,a5,a6,a7);var _sqlite3_overload_function=Module["_sqlite3_overload_function"]=(a0,a1,a2)=>(_sqlite3_overload_function=Module["_sqlite3_overload_function"]=wasmExports["de"])(a0,a1,a2);var _sqlite3_trace_v2=Module["_sqlite3_trace_v2"]=(a0,a1,a2,a3)=>(_sqlite3_trace_v2=Module["_sqlite3_trace_v2"]=wasmExports["ee"])(a0,a1,a2,a3);var _sqlite3_commit_hook=Module["_sqlite3_commit_hook"]=(a0,a1,a2)=>(_sqlite3_commit_hook=Module["_sqlite3_commit_hook"]=wasmExports["fe"])(a0,a1,a2);var _sqlite3_update_hook=Module["_sqlite3_update_hook"]=(a0,a1,a2)=>(_sqlite3_update_hook=Module["_sqlite3_update_hook"]=wasmExports["ge"])(a0,a1,a2);var _sqlite3_rollback_hook=Module["_sqlite3_rollback_hook"]=(a0,a1,a2)=>(_sqlite3_rollback_hook=Module["_sqlite3_rollback_hook"]=wasmExports["he"])(a0,a1,a2);var _sqlite3_autovacuum_pages=Module["_sqlite3_autovacuum_pages"]=(a0,a1,a2,a3)=>(_sqlite3_autovacuum_pages=Module["_sqlite3_autovacuum_pages"]=wasmExports["ie"])(a0,a1,a2,a3);var _sqlite3_wal_autocheckpoint=Module["_sqlite3_wal_autocheckpoint"]=(a0,a1)=>(_sqlite3_wal_autocheckpoint=Module["_sqlite3_wal_autocheckpoint"]=wasmExports["je"])(a0,a1);var _sqlite3_wal_hook=Module["_sqlite3_wal_hook"]=(a0,a1,a2)=>(_sqlite3_wal_hook=Module["_sqlite3_wal_hook"]=wasmExports["ke"])(a0,a1,a2);var _sqlite3_wal_checkpoint_v2=Module["_sqlite3_wal_checkpoint_v2"]=(a0,a1,a2,a3,a4)=>(_sqlite3_wal_checkpoint_v2=Module["_sqlite3_wal_checkpoint_v2"]=wasmExports["le"])(a0,a1,a2,a3,a4);var _sqlite3_wal_checkpoint=Module["_sqlite3_wal_checkpoint"]=(a0,a1)=>(_sqlite3_wal_checkpoint=Module["_sqlite3_wal_checkpoint"]=wasmExports["me"])(a0,a1);var _sqlite3_error_offset=Module["_sqlite3_error_offset"]=a0=>(_sqlite3_error_offset=Module["_sqlite3_error_offset"]=wasmExports["ne"])(a0);var _sqlite3_errmsg16=Module["_sqlite3_errmsg16"]=a0=>(_sqlite3_errmsg16=Module["_sqlite3_errmsg16"]=wasmExports["oe"])(a0);var _sqlite3_errcode=Module["_sqlite3_errcode"]=a0=>(_sqlite3_errcode=Module["_sqlite3_errcode"]=wasmExports["pe"])(a0);var _sqlite3_extended_errcode=Module["_sqlite3_extended_errcode"]=a0=>(_sqlite3_extended_errcode=Module["_sqlite3_extended_errcode"]=wasmExports["qe"])(a0);var _sqlite3_system_errno=Module["_sqlite3_system_errno"]=a0=>(_sqlite3_system_errno=Module["_sqlite3_system_errno"]=wasmExports["re"])(a0);var _sqlite3_errstr=Module["_sqlite3_errstr"]=a0=>(_sqlite3_errstr=Module["_sqlite3_errstr"]=wasmExports["se"])(a0);var _sqlite3_limit=Module["_sqlite3_limit"]=(a0,a1,a2)=>(_sqlite3_limit=Module["_sqlite3_limit"]=wasmExports["te"])(a0,a1,a2);var _sqlite3_open=Module["_sqlite3_open"]=(a0,a1)=>(_sqlite3_open=Module["_sqlite3_open"]=wasmExports["ue"])(a0,a1);var _sqlite3_open_v2=Module["_sqlite3_open_v2"]=(a0,a1,a2,a3)=>(_sqlite3_open_v2=Module["_sqlite3_open_v2"]=wasmExports["ve"])(a0,a1,a2,a3);var _sqlite3_open16=Module["_sqlite3_open16"]=(a0,a1)=>(_sqlite3_open16=Module["_sqlite3_open16"]=wasmExports["we"])(a0,a1);var _sqlite3_create_collation=Module["_sqlite3_create_collation"]=(a0,a1,a2,a3,a4)=>(_sqlite3_create_collation=Module["_sqlite3_create_collation"]=wasmExports["xe"])(a0,a1,a2,a3,a4);var _sqlite3_create_collation_v2=Module["_sqlite3_create_collation_v2"]=(a0,a1,a2,a3,a4,a5)=>(_sqlite3_create_collation_v2=Module["_sqlite3_create_collation_v2"]=wasmExports["ye"])(a0,a1,a2,a3,a4,a5);var _sqlite3_create_collation16=Module["_sqlite3_create_collation16"]=(a0,a1,a2,a3,a4)=>(_sqlite3_create_collation16=Module["_sqlite3_create_collation16"]=wasmExports["ze"])(a0,a1,a2,a3,a4);var _sqlite3_collation_needed=Module["_sqlite3_collation_needed"]=(a0,a1,a2)=>(_sqlite3_collation_needed=Module["_sqlite3_collation_needed"]=wasmExports["Ae"])(a0,a1,a2);var _sqlite3_collation_needed16=Module["_sqlite3_collation_needed16"]=(a0,a1,a2)=>(_sqlite3_collation_needed16=Module["_sqlite3_collation_needed16"]=wasmExports["Be"])(a0,a1,a2);var _sqlite3_get_clientdata=Module["_sqlite3_get_clientdata"]=(a0,a1)=>(_sqlite3_get_clientdata=Module["_sqlite3_get_clientdata"]=wasmExports["Ce"])(a0,a1);var _sqlite3_set_clientdata=Module["_sqlite3_set_clientdata"]=(a0,a1,a2,a3)=>(_sqlite3_set_clientdata=Module["_sqlite3_set_clientdata"]=wasmExports["De"])(a0,a1,a2,a3);var _sqlite3_get_autocommit=Module["_sqlite3_get_autocommit"]=a0=>(_sqlite3_get_autocommit=Module["_sqlite3_get_autocommit"]=wasmExports["Ee"])(a0);var _sqlite3_table_column_metadata=Module["_sqlite3_table_column_metadata"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8)=>(_sqlite3_table_column_metadata=Module["_sqlite3_table_column_metadata"]=wasmExports["Fe"])(a0,a1,a2,a3,a4,a5,a6,a7,a8);var _sqlite3_sleep=Module["_sqlite3_sleep"]=a0=>(_sqlite3_sleep=Module["_sqlite3_sleep"]=wasmExports["Ge"])(a0);var _sqlite3_extended_result_codes=Module["_sqlite3_extended_result_codes"]=(a0,a1)=>(_sqlite3_extended_result_codes=Module["_sqlite3_extended_result_codes"]=wasmExports["He"])(a0,a1);var _sqlite3_file_control=Module["_sqlite3_file_control"]=(a0,a1,a2,a3)=>(_sqlite3_file_control=Module["_sqlite3_file_control"]=wasmExports["Ie"])(a0,a1,a2,a3);var _sqlite3_test_control=Module["_sqlite3_test_control"]=(a0,a1)=>(_sqlite3_test_control=Module["_sqlite3_test_control"]=wasmExports["Je"])(a0,a1);var _sqlite3_create_filename=Module["_sqlite3_create_filename"]=(a0,a1,a2,a3,a4)=>(_sqlite3_create_filename=Module["_sqlite3_create_filename"]=wasmExports["Ke"])(a0,a1,a2,a3,a4);var _sqlite3_free_filename=Module["_sqlite3_free_filename"]=a0=>(_sqlite3_free_filename=Module["_sqlite3_free_filename"]=wasmExports["Le"])(a0);var _sqlite3_uri_parameter=Module["_sqlite3_uri_parameter"]=(a0,a1)=>(_sqlite3_uri_parameter=Module["_sqlite3_uri_parameter"]=wasmExports["Me"])(a0,a1);var _sqlite3_uri_key=Module["_sqlite3_uri_key"]=(a0,a1)=>(_sqlite3_uri_key=Module["_sqlite3_uri_key"]=wasmExports["Ne"])(a0,a1);var _sqlite3_uri_boolean=Module["_sqlite3_uri_boolean"]=(a0,a1,a2)=>(_sqlite3_uri_boolean=Module["_sqlite3_uri_boolean"]=wasmExports["Oe"])(a0,a1,a2);var _sqlite3_uri_int64=Module["_sqlite3_uri_int64"]=(a0,a1,a2,a3)=>(_sqlite3_uri_int64=Module["_sqlite3_uri_int64"]=wasmExports["Pe"])(a0,a1,a2,a3);var _sqlite3_filename_database=Module["_sqlite3_filename_database"]=a0=>(_sqlite3_filename_database=Module["_sqlite3_filename_database"]=wasmExports["Qe"])(a0);var _sqlite3_filename_journal=Module["_sqlite3_filename_journal"]=a0=>(_sqlite3_filename_journal=Module["_sqlite3_filename_journal"]=wasmExports["Re"])(a0);var _sqlite3_filename_wal=Module["_sqlite3_filename_wal"]=a0=>(_sqlite3_filename_wal=Module["_sqlite3_filename_wal"]=wasmExports["Se"])(a0);var _sqlite3_db_name=Module["_sqlite3_db_name"]=(a0,a1)=>(_sqlite3_db_name=Module["_sqlite3_db_name"]=wasmExports["Te"])(a0,a1);var _sqlite3_db_filename=Module["_sqlite3_db_filename"]=(a0,a1)=>(_sqlite3_db_filename=Module["_sqlite3_db_filename"]=wasmExports["Ue"])(a0,a1);var _sqlite3_db_readonly=Module["_sqlite3_db_readonly"]=(a0,a1)=>(_sqlite3_db_readonly=Module["_sqlite3_db_readonly"]=wasmExports["Ve"])(a0,a1);var _sqlite3_compileoption_used=Module["_sqlite3_compileoption_used"]=a0=>(_sqlite3_compileoption_used=Module["_sqlite3_compileoption_used"]=wasmExports["We"])(a0);var _sqlite3_compileoption_get=Module["_sqlite3_compileoption_get"]=a0=>(_sqlite3_compileoption_get=Module["_sqlite3_compileoption_get"]=wasmExports["Xe"])(a0);var _sqlite3_sourceid=Module["_sqlite3_sourceid"]=()=>(_sqlite3_sourceid=Module["_sqlite3_sourceid"]=wasmExports["Ye"])();var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["Ze"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["_e"])(a0);var _RegisterExtensionFunctions=Module["_RegisterExtensionFunctions"]=a0=>(_RegisterExtensionFunctions=Module["_RegisterExtensionFunctions"]=wasmExports["$e"])(a0);var _getSqliteFree=Module["_getSqliteFree"]=()=>(_getSqliteFree=Module["_getSqliteFree"]=wasmExports["af"])();var _main=Module["_main"]=(a0,a1)=>(_main=Module["_main"]=wasmExports["bf"])(a0,a1);var _libauthorizer_set_authorizer=Module["_libauthorizer_set_authorizer"]=(a0,a1,a2)=>(_libauthorizer_set_authorizer=Module["_libauthorizer_set_authorizer"]=wasmExports["cf"])(a0,a1,a2);var _libfunction_create_function=Module["_libfunction_create_function"]=(a0,a1,a2,a3,a4,a5,a6,a7)=>(_libfunction_create_function=Module["_libfunction_create_function"]=wasmExports["df"])(a0,a1,a2,a3,a4,a5,a6,a7);var _libhook_update_hook=Module["_libhook_update_hook"]=(a0,a1,a2)=>(_libhook_update_hook=Module["_libhook_update_hook"]=wasmExports["ef"])(a0,a1,a2);var _libprogress_progress_handler=Module["_libprogress_progress_handler"]=(a0,a1,a2,a3)=>(_libprogress_progress_handler=Module["_libprogress_progress_handler"]=wasmExports["ff"])(a0,a1,a2,a3);var _libvfs_vfs_register=Module["_libvfs_vfs_register"]=(a0,a1,a2,a3,a4,a5)=>(_libvfs_vfs_register=Module["_libvfs_vfs_register"]=wasmExports["gf"])(a0,a1,a2,a3,a4,a5);var _emscripten_builtin_memalign=(a0,a1)=>(_emscripten_builtin_memalign=wasmExports["jf"])(a0,a1);var __emscripten_timeout=(a0,a1)=>(__emscripten_timeout=wasmExports["kf"])(a0,a1);var __emscripten_tempret_get=()=>(__emscripten_tempret_get=wasmExports["lf"])();var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["mf"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["nf"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["of"])();var _sqlite3_version=Module["_sqlite3_version"]=5472;Module["getTempRet0"]=getTempRet0;Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;Module["intArrayFromString"]=intArrayFromString;Module["intArrayToString"]=intArrayToString;Module["AsciiToString"]=AsciiToString;Module["UTF16ToString"]=UTF16ToString;Module["stringToUTF16"]=stringToUTF16;Module["UTF32ToString"]=UTF32ToString;Module["stringToUTF32"]=stringToUTF32;Module["writeArrayToMemory"]=writeArrayToMemory;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(){var entryFunction=_main;var argc=0;var argv=0;try{var ret=entryFunction(argc,argv);exitJS(ret,true);return ret}catch(e){return handleException(e)}}function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();if(shouldRunNow)callMain();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"])shouldRunNow=false;run();(function(){const AsyncFunction=Object.getPrototypeOf(async function(){}).constructor;let pAsyncFlags=0;Module["set_authorizer"]=function(db,xAuthorizer,pApp){if(pAsyncFlags){Module["deleteCallback"](pAsyncFlags);Module["_sqlite3_free"](pAsyncFlags);pAsyncFlags=0}pAsyncFlags=Module["_sqlite3_malloc"](4);setValue(pAsyncFlags,xAuthorizer instanceof AsyncFunction?1:0,"i32");const result=ccall("libauthorizer_set_authorizer","number",["number","number","number"],[db,xAuthorizer?1:0,pAsyncFlags]);if(!result&&xAuthorizer){Module["setCallback"](pAsyncFlags,(_,iAction,p3,p4,p5,p6)=>xAuthorizer(pApp,iAction,p3,p4,p5,p6))}return result}})();(function(){const AsyncFunction=Object.getPrototypeOf(async function(){}).constructor;const FUNC_METHODS=["xFunc","xStep","xFinal"];const mapFunctionNameToKey=new Map;Module["create_function"]=function(db,zFunctionName,nArg,eTextRep,pApp,xFunc,xStep,xFinal){const pAsyncFlags=Module["_sqlite3_malloc"](4);const target={xFunc,xStep,xFinal};setValue(pAsyncFlags,FUNC_METHODS.reduce((mask,method,i)=>{if(target[method]instanceof AsyncFunction){return mask|1<xUpdateHook(iUpdateType,dbName,tblName,lo32,hi32))}}})();(function(){const AsyncFunction=Object.getPrototypeOf(async function(){}).constructor;let pAsyncFlags=0;Module["progress_handler"]=function(db,nOps,xProgress,pApp){if(pAsyncFlags){Module["deleteCallback"](pAsyncFlags);Module["_sqlite3_free"](pAsyncFlags);pAsyncFlags=0}pAsyncFlags=Module["_sqlite3_malloc"](4);setValue(pAsyncFlags,xProgress instanceof AsyncFunction?1:0,"i32");ccall("libprogress_progress_handler","number",["number","number","number","number"],[db,nOps,xProgress?1:0,pAsyncFlags]);if(xProgress){Module["setCallback"](pAsyncFlags,_=>xProgress(pApp))}}})();(function(){const VFS_METHODS=["xOpen","xDelete","xAccess","xFullPathname","xRandomness","xSleep","xCurrentTime","xGetLastError","xCurrentTimeInt64","xClose","xRead","xWrite","xTruncate","xSync","xFileSize","xLock","xUnlock","xCheckReservedLock","xFileControl","xSectorSize","xDeviceCharacteristics","xShmMap","xShmLock","xShmBarrier","xShmUnmap"];const mapVFSNameToKey=new Map;Module["vfs_register"]=function(vfs,makeDefault){let methodMask=0;let asyncMask=0;VFS_METHODS.forEach((method,i)=>{if(vfs[method]){methodMask|=1< { - const response = await Sentry.startSpan({ name: "GET_ISSUES" }, async () => { + const response = await startSpan({ name: "GET_ISSUES" }, async () => { const res = await persistence.getIssues(workspaceSlug, projectId, queries, config); return res; }); @@ -100,7 +100,7 @@ export class IssueService extends APIService { }) .then((response) => { if (response.data) { - addIssue(response?.data); + updateIssue({ ...response.data, is_local_update: 1 }); } return response?.data; }) diff --git a/web/core/store/issue/archived/filter.store.ts b/web/core/store/issue/archived/filter.store.ts index 31f5f4496af..ad673c8cf41 100644 --- a/web/core/store/issue/archived/filter.store.ts +++ b/web/core/store/issue/archived/filter.store.ts @@ -33,6 +33,7 @@ export interface IArchivedIssuesFilter extends IBaseIssueFilterStore { groupId: string | undefined, subGroupId: string | undefined ) => Partial>; + getIssueFilters(projectId: string): IIssueFilters | undefined; // action fetchFilters: (workspaceSlug: string, projectId: string) => Promise; updateFilters: ( diff --git a/web/core/store/issue/cycle/filter.store.ts b/web/core/store/issue/cycle/filter.store.ts index 3565a05c062..5e62061f055 100644 --- a/web/core/store/issue/cycle/filter.store.ts +++ b/web/core/store/issue/cycle/filter.store.ts @@ -31,6 +31,7 @@ export interface ICycleIssuesFilter extends IBaseIssueFilterStore { groupId: string | undefined, subGroupId: string | undefined ) => Partial>; + getIssueFilters(cycleId: string): IIssueFilters | undefined; // action fetchFilters: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; updateFilters: ( diff --git a/web/core/store/issue/draft/filter.store.ts b/web/core/store/issue/draft/filter.store.ts index 0c657543838..bbada1199bf 100644 --- a/web/core/store/issue/draft/filter.store.ts +++ b/web/core/store/issue/draft/filter.store.ts @@ -33,6 +33,7 @@ export interface IDraftIssuesFilter extends IBaseIssueFilterStore { groupId: string | undefined, subGroupId: string | undefined ) => Partial>; + getIssueFilters(projectId: string): IIssueFilters | undefined; // action fetchFilters: (workspaceSlug: string, projectId: string) => Promise; updateFilters: ( diff --git a/web/core/store/issue/helpers/issue-filter-helper.store.ts b/web/core/store/issue/helpers/issue-filter-helper.store.ts index 5d4b638a539..338b6a4ac85 100644 --- a/web/core/store/issue/helpers/issue-filter-helper.store.ts +++ b/web/core/store/issue/helpers/issue-filter-helper.store.ts @@ -13,7 +13,7 @@ import { TStaticViewTypes, } from "@plane/types"; // constants -import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue"; +import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue"; // helpers import { getComputedDisplayFilters, getComputedDisplayProperties } from "@/helpers/issue.helper"; // lib @@ -114,6 +114,8 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore { : nonEmptyArrayValue; }); + if (displayFilters?.layout) issueFiltersParams.layout = displayFilters?.layout; + return issueFiltersParams; }; diff --git a/web/core/store/issue/module/filter.store.ts b/web/core/store/issue/module/filter.store.ts index 1830619cd9f..9c22963b91e 100644 --- a/web/core/store/issue/module/filter.store.ts +++ b/web/core/store/issue/module/filter.store.ts @@ -31,6 +31,7 @@ export interface IModuleIssuesFilter extends IBaseIssueFilterStore { groupId: string | undefined, subGroupId: string | undefined ) => Partial>; + getIssueFilters(moduleId: string): IIssueFilters | undefined; // action fetchFilters: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; updateFilters: ( diff --git a/web/core/store/issue/project-views/filter.store.ts b/web/core/store/issue/project-views/filter.store.ts index 4261b1bece2..b49633c367c 100644 --- a/web/core/store/issue/project-views/filter.store.ts +++ b/web/core/store/issue/project-views/filter.store.ts @@ -31,6 +31,7 @@ export interface IProjectViewIssuesFilter extends IBaseIssueFilterStore { groupId: string | undefined, subGroupId: string | undefined ) => Partial>; + getIssueFilters(viewId: string): IIssueFilters | undefined; // action fetchFilters: (workspaceSlug: string, projectId: string, viewId: string) => Promise; updateFilters: ( @@ -264,9 +265,16 @@ export class ProjectViewIssuesFilter extends IssueFilterHelperStore implements I const currentUserId = this.rootIssueStore.currentUserId; if (currentUserId) - this.handleIssuesLocalFilters.set(EIssuesStoreType.PROJECT_VIEW, type, workspaceSlug, viewId, currentUserId, { - kanban_filters: _filters.kanbanFilters, - }); + this.handleIssuesLocalFilters.set( + EIssuesStoreType.PROJECT_VIEW, + type, + workspaceSlug, + viewId, + currentUserId, + { + kanban_filters: _filters.kanbanFilters, + } + ); runInAction(() => { Object.keys(updatedKanbanFilters).forEach((_key) => { diff --git a/web/core/store/issue/project/filter.store.ts b/web/core/store/issue/project/filter.store.ts index 54b109d12af..069f2ed8932 100644 --- a/web/core/store/issue/project/filter.store.ts +++ b/web/core/store/issue/project/filter.store.ts @@ -31,6 +31,7 @@ export interface IProjectIssuesFilter extends IBaseIssueFilterStore { groupId: string | undefined, subGroupId: string | undefined ) => Partial>; + getIssueFilters(projectId: string): IIssueFilters | undefined; // action fetchFilters: (workspaceSlug: string, projectId: string) => Promise; updateFilters: ( diff --git a/web/package.json b/web/package.json index 03a8167f482..54b70a2d79a 100644 --- a/web/package.json +++ b/web/package.json @@ -10,7 +10,9 @@ "lint": "eslint . --ext .ts,.tsx", "lint:errors": "eslint . --ext .ts,.tsx --quiet", "export": "next export", - "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist" + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", + "dev:trace": "NEXT_TURBOPACK_TRACING=1 NEXT_CPU_PROF=1 next dev", + "view-trace": "next internal turbo-trace-server ./.next/trace" }, "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.1.3", @@ -34,10 +36,10 @@ "@popperjs/core": "^2.11.8", "@react-pdf/renderer": "^3.4.5", "@sentry/nextjs": "^8.32.0", - "@sqlite.org/sqlite-wasm": "^3.46.0-build2", "axios": "^1.7.4", "clsx": "^2.0.0", "cmdk": "^1.0.0", + "comlink": "^4.4.1", "date-fns": "^2.30.0", "dotenv": "^16.0.3", "isomorphic-dompurify": "^2.12.0", @@ -59,6 +61,7 @@ "react-markdown": "^8.0.7", "react-pdf-html": "^2.1.2", "react-popper": "^2.3.0", + "recharts": "^2.12.7", "sharp": "^0.32.1", "smooth-scroll-into-view-if-needed": "^2.0.2", "swr": "^2.1.3", diff --git a/web/sentry.client.config.ts b/web/sentry.client.config.ts index c8103062290..efe3e3cd5ad 100644 --- a/web/sentry.client.config.ts +++ b/web/sentry.client.config.ts @@ -2,9 +2,9 @@ // The config you add here will be used whenever a users loads a page in their browser. // https://docs.sentry.io/platforms/javascript/guides/nextjs/ -import * as Sentry from "@sentry/nextjs"; +import { replayIntegration, init } from "@sentry/nextjs"; -Sentry.init({ +init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, environment: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT || "development", @@ -22,7 +22,7 @@ Sentry.init({ // You can remove this option if you're not planning to use the Sentry Session Replay feature: integrations: [ - Sentry.replayIntegration({ + replayIntegration({ // Additional Replay configuration goes in here, for example: maskAllText: true, blockAllMedia: true, diff --git a/web/sentry.edge.config.ts b/web/sentry.edge.config.ts index 2dbc6e93afb..93d1c5e4776 100644 --- a/web/sentry.edge.config.ts +++ b/web/sentry.edge.config.ts @@ -3,9 +3,9 @@ // Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally. // https://docs.sentry.io/platforms/javascript/guides/nextjs/ -import * as Sentry from "@sentry/nextjs"; +import { init } from "@sentry/nextjs"; -Sentry.init({ +init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, environment: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT || "development", diff --git a/yarn.lock b/yarn.lock index 38e9459844b..09ff72c42ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2935,15 +2935,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== -"@sqlite.org/sqlite-wasm@^3.46.0-build2": - version "3.46.0-build2" - resolved "https://registry.yarnpkg.com/@sqlite.org/sqlite-wasm/-/sqlite-wasm-3.46.0-build2.tgz#f84c3014f3fed6db08fc585d67e386d39e3956bf" - integrity sha512-10s/u/Main1RGO+jjzK+mgC/zh1ls1CEnq3Dujr03TwvzLg+j4FAohOmlYkQj8KQOj1vGR9cuB9F8tVBTwVGVA== - -"@storybook/addon-actions@8.3.6": - version "8.3.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.3.6.tgz#80c5dbfc2278d72dc461a954bb729165ee1dfecb" - integrity sha512-nOqgl0WoZK2KwjaABaXMoIgrIHOQl9inOzJvqQau0HOtsvnXGXYfJXYnpjZenoZDoZXKbUDl0U2haDFx2a2fJw== +"@storybook/addon-actions@8.3.5": + version "8.3.5" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.3.5.tgz#03fdb891114439ed47cb7df6ef21826530449db7" + integrity sha512-t8D5oo+4XfD+F8091wLa2y/CDd/W2lExCeol5Vm1tp5saO+u6f2/d7iykLhTowWV84Uohi3D073uFeyTAlGebg== dependencies: "@storybook/global" "^5.0.0" "@types/uuid" "^9.0.1" @@ -5416,6 +5411,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +comlink@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.1.tgz#e568b8e86410b809e8600eb2cf40c189371ef981" + integrity sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q== + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee"